highstock.src.js 2.5 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984569855698656987569885698956990569915699256993569945699556996569975699856999570005700157002570035700457005570065700757008570095701057011570125701357014570155701657017570185701957020570215702257023570245702557026570275702857029570305703157032570335703457035570365703757038570395704057041570425704357044570455704657047570485704957050570515705257053570545705557056570575705857059570605706157062570635706457065570665706757068570695707057071570725707357074570755707657077570785707957080570815708257083570845708557086570875708857089570905709157092570935709457095570965709757098570995710057101571025710357104571055710657107571085710957110571115711257113571145711557116571175711857119571205712157122571235712457125571265712757128571295713057131571325713357134571355713657137571385713957140571415714257143571445714557146571475714857149571505715157152571535715457155571565715757158571595716057161571625716357164571655716657167571685716957170571715717257173571745717557176571775717857179571805718157182571835718457185571865718757188571895719057191571925719357194571955719657197571985719957200572015720257203572045720557206572075720857209572105721157212572135721457215572165721757218572195722057221572225722357224572255722657227572285722957230572315723257233572345723557236572375723857239572405724157242572435724457245572465724757248572495725057251572525725357254572555725657257572585725957260572615726257263572645726557266572675726857269572705727157272572735727457275572765727757278572795728057281572825728357284572855728657287572885728957290572915729257293572945729557296572975729857299573005730157302573035730457305573065730757308573095731057311573125731357314573155731657317573185731957320573215732257323573245732557326573275732857329573305733157332573335733457335573365733757338573395734057341573425734357344573455734657347573485734957350573515735257353573545735557356573575735857359573605736157362573635736457365573665736757368573695737057371573725737357374573755737657377573785737957380573815738257383573845738557386573875738857389573905739157392573935739457395573965739757398573995740057401574025740357404574055740657407574085740957410574115741257413574145741557416574175741857419574205742157422574235742457425574265742757428574295743057431574325743357434574355743657437574385743957440574415744257443574445744557446574475744857449574505745157452574535745457455574565745757458574595746057461574625746357464574655746657467574685746957470574715747257473574745747557476574775747857479574805748157482574835748457485574865748757488574895749057491574925749357494574955749657497574985749957500575015750257503575045750557506575075750857509575105751157512575135751457515575165751757518575195752057521575225752357524575255752657527575285752957530575315753257533575345753557536575375753857539575405754157542575435754457545575465754757548575495755057551575525755357554575555755657557575585755957560575615756257563575645756557566575675756857569575705757157572575735757457575575765757757578575795758057581575825758357584575855758657587575885758957590575915759257593575945759557596575975759857599576005760157602576035760457605576065760757608576095761057611576125761357614576155761657617576185761957620576215762257623576245762557626576275762857629576305763157632576335763457635576365763757638576395764057641576425764357644576455764657647576485764957650576515765257653576545765557656576575765857659576605766157662576635766457665576665766757668576695767057671576725767357674576755767657677576785767957680576815768257683576845768557686576875768857689576905769157692576935769457695576965769757698576995770057701577025770357704577055770657707577085770957710577115771257713577145771557716577175771857719577205772157722577235772457725577265772757728577295773057731577325773357734577355773657737577385773957740577415774257743577445774557746577475774857749577505775157752577535775457755577565775757758577595776057761577625776357764577655776657767577685776957770577715777257773577745777557776577775777857779577805778157782577835778457785577865778757788577895779057791577925779357794577955779657797577985779957800578015780257803578045780557806578075780857809578105781157812578135781457815578165781757818578195782057821578225782357824578255782657827578285782957830578315783257833578345783557836578375783857839578405784157842578435784457845578465784757848578495785057851578525785357854578555785657857578585785957860578615786257863578645786557866578675786857869578705787157872578735787457875578765787757878578795788057881578825788357884578855788657887578885788957890578915789257893578945789557896578975789857899579005790157902579035790457905579065790757908579095791057911579125791357914579155791657917579185791957920579215792257923579245792557926579275792857929579305793157932579335793457935579365793757938579395794057941579425794357944579455794657947579485794957950579515795257953579545795557956579575795857959579605796157962579635796457965579665796757968579695797057971579725797357974579755797657977579785797957980579815798257983579845798557986579875798857989579905799157992579935799457995579965799757998579995800058001580025800358004580055800658007580085800958010580115801258013580145801558016580175801858019580205802158022580235802458025580265802758028580295803058031580325803358034580355803658037580385803958040580415804258043580445804558046580475804858049580505805158052580535805458055580565805758058580595806058061580625806358064580655806658067580685806958070580715807258073580745807558076580775807858079580805808158082580835808458085580865808758088580895809058091580925809358094580955809658097580985809958100581015810258103
  1. /**
  2. * @license Highstock JS v11.1.0 (2023-06-05)
  3. *
  4. * (c) 2009-2021 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. (function (root, factory) {
  9. if (typeof module === 'object' && module.exports) {
  10. factory['default'] = factory;
  11. module.exports = root.document ?
  12. factory(root) :
  13. factory;
  14. } else if (typeof define === 'function' && define.amd) {
  15. define('highcharts/highstock', function () {
  16. return factory(root);
  17. });
  18. } else {
  19. if (root.Highcharts) {
  20. root.Highcharts.error(16, true);
  21. }
  22. root.Highcharts = factory(root);
  23. }
  24. }(typeof window !== 'undefined' ? window : this, function (window) {
  25. 'use strict';
  26. var _modules = {};
  27. function _registerModule(obj, path, args, fn) {
  28. if (!obj.hasOwnProperty(path)) {
  29. obj[path] = fn.apply(null, args);
  30. if (typeof CustomEvent === 'function') {
  31. window.dispatchEvent(
  32. new CustomEvent(
  33. 'HighchartsModuleLoaded',
  34. { detail: { path: path, module: obj[path] }
  35. })
  36. );
  37. }
  38. }
  39. }
  40. _registerModule(_modules, 'Core/Globals.js', [], function () {
  41. /* *
  42. *
  43. * (c) 2010-2021 Torstein Honsi
  44. *
  45. * License: www.highcharts.com/license
  46. *
  47. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48. *
  49. * */
  50. /* *
  51. *
  52. * Namespace
  53. *
  54. * */
  55. /**
  56. * Shared Highcharts properties.
  57. * @private
  58. */
  59. var Globals;
  60. (function (Globals) {
  61. /* *
  62. *
  63. * Constants
  64. *
  65. * */
  66. Globals.SVG_NS = 'http://www.w3.org/2000/svg', Globals.product = 'Highcharts', Globals.version = '11.1.0', Globals.win = (typeof window !== 'undefined' ?
  67. window :
  68. {}), // eslint-disable-line node/no-unsupported-features/es-builtins
  69. Globals.doc = Globals.win.document, Globals.svg = (Globals.doc &&
  70. Globals.doc.createElementNS &&
  71. !!Globals.doc.createElementNS(Globals.SVG_NS, 'svg').createSVGRect), Globals.userAgent = (Globals.win.navigator && Globals.win.navigator.userAgent) || '', Globals.isChrome = Globals.userAgent.indexOf('Chrome') !== -1, Globals.isFirefox = Globals.userAgent.indexOf('Firefox') !== -1, Globals.isMS = /(edge|msie|trident)/i.test(Globals.userAgent) && !Globals.win.opera, Globals.isSafari = !Globals.isChrome && Globals.userAgent.indexOf('Safari') !== -1, Globals.isTouchDevice = /(Mobile|Android|Windows Phone)/.test(Globals.userAgent), Globals.isWebKit = Globals.userAgent.indexOf('AppleWebKit') !== -1, Globals.deg2rad = Math.PI * 2 / 360, Globals.hasBidiBug = (Globals.isFirefox &&
  72. parseInt(Globals.userAgent.split('Firefox/')[1], 10) < 4 // issue #38
  73. ), Globals.hasTouch = !!Globals.win.TouchEvent, Globals.marginNames = [
  74. 'plotTop',
  75. 'marginRight',
  76. 'marginBottom',
  77. 'plotLeft'
  78. ], Globals.noop = function () { }, Globals.supportsPassiveEvents = (function () {
  79. // Checks whether the browser supports passive events, (#11353).
  80. let supportsPassive = false;
  81. // Object.defineProperty doesn't work on IE as well as passive
  82. // events - instead of using polyfill, we can exclude IE totally.
  83. if (!Globals.isMS) {
  84. const opts = Object.defineProperty({}, 'passive', {
  85. get: function () {
  86. supportsPassive = true;
  87. }
  88. });
  89. if (Globals.win.addEventListener && Globals.win.removeEventListener) {
  90. Globals.win.addEventListener('testPassive', Globals.noop, opts);
  91. Globals.win.removeEventListener('testPassive', Globals.noop, opts);
  92. }
  93. }
  94. return supportsPassive;
  95. }());
  96. /**
  97. * An array containing the current chart objects in the page. A chart's
  98. * position in the array is preserved throughout the page's lifetime. When
  99. * a chart is destroyed, the array item becomes `undefined`.
  100. *
  101. * @name Highcharts.charts
  102. * @type {Array<Highcharts.Chart|undefined>}
  103. */
  104. Globals.charts = [];
  105. /**
  106. * A hook for defining additional date format specifiers. New
  107. * specifiers are defined as key-value pairs by using the
  108. * specifier as key, and a function which takes the timestamp as
  109. * value. This function returns the formatted portion of the
  110. * date.
  111. *
  112. * @sample highcharts/global/dateformats/
  113. * Adding support for week number
  114. *
  115. * @name Highcharts.dateFormats
  116. * @type {Record<string, Highcharts.TimeFormatCallbackFunction>}
  117. */
  118. Globals.dateFormats = {};
  119. /**
  120. * @private
  121. * @deprecated
  122. * @todo Use only `Core/Series/SeriesRegistry.seriesTypes`
  123. */
  124. Globals.seriesTypes = {};
  125. /**
  126. * @private
  127. */
  128. Globals.symbolSizes = {};
  129. /* *
  130. *
  131. * Properties
  132. *
  133. * */
  134. // eslint-disable-next-line prefer-const
  135. Globals.chartCount = 0;
  136. })(Globals || (Globals = {}));
  137. /* *
  138. *
  139. * Default Export
  140. *
  141. * */
  142. /* *
  143. *
  144. * API Declarations
  145. *
  146. * */
  147. /**
  148. * Theme options that should get applied to the chart. In module mode it
  149. * might not be possible to change this property because of read-only
  150. * restrictions, instead use {@link Highcharts.setOptions}.
  151. *
  152. * @deprecated
  153. * @name Highcharts.theme
  154. * @type {Highcharts.Options}
  155. */
  156. (''); // keeps doclets above in JS file
  157. return Globals;
  158. });
  159. _registerModule(_modules, 'Core/Utilities.js', [_modules['Core/Globals.js']], function (H) {
  160. /* *
  161. *
  162. * (c) 2010-2021 Torstein Honsi
  163. *
  164. * License: www.highcharts.com/license
  165. *
  166. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  167. *
  168. * */
  169. const { charts, doc, win } = H;
  170. /* *
  171. *
  172. * Functions
  173. *
  174. * */
  175. /**
  176. * Provide error messages for debugging, with links to online explanation. This
  177. * function can be overridden to provide custom error handling.
  178. *
  179. * @sample highcharts/chart/highcharts-error/
  180. * Custom error handler
  181. *
  182. * @function Highcharts.error
  183. *
  184. * @param {number|string} code
  185. * The error code. See
  186. * [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
  187. * for available codes. If it is a string, the error message is printed
  188. * directly in the console.
  189. *
  190. * @param {boolean} [stop=false]
  191. * Whether to throw an error or just log a warning in the console.
  192. *
  193. * @param {Highcharts.Chart} [chart]
  194. * Reference to the chart that causes the error. Used in 'debugger'
  195. * module to display errors directly on the chart.
  196. * Important note: This argument is undefined for errors that lack
  197. * access to the Chart instance. In such case, the error will be
  198. * displayed on the last created chart.
  199. *
  200. * @param {Highcharts.Dictionary<string>} [params]
  201. * Additional parameters for the generated message.
  202. *
  203. * @return {void}
  204. */
  205. function error(code, stop, chart, params) {
  206. const severity = stop ? 'Highcharts error' : 'Highcharts warning';
  207. if (code === 32) {
  208. code = `${severity}: Deprecated member`;
  209. }
  210. const isCode = isNumber(code);
  211. let message = isCode ?
  212. `${severity} #${code}: www.highcharts.com/errors/${code}/` :
  213. code.toString();
  214. const defaultHandler = function () {
  215. if (stop) {
  216. throw new Error(message);
  217. }
  218. // else ...
  219. if (win.console &&
  220. error.messages.indexOf(message) === -1 // prevent console flooting
  221. ) {
  222. console.warn(message); // eslint-disable-line no-console
  223. }
  224. };
  225. if (typeof params !== 'undefined') {
  226. let additionalMessages = '';
  227. if (isCode) {
  228. message += '?';
  229. }
  230. objectEach(params, function (value, key) {
  231. additionalMessages += `\n - ${key}: ${value}`;
  232. if (isCode) {
  233. message += encodeURI(key) + '=' + encodeURI(value);
  234. }
  235. });
  236. message += additionalMessages;
  237. }
  238. fireEvent(H, 'displayError', { chart, code, message, params }, defaultHandler);
  239. error.messages.push(message);
  240. }
  241. (function (error) {
  242. error.messages = [];
  243. })(error || (error = {}));
  244. /* eslint-disable valid-jsdoc */
  245. /**
  246. * Utility function to deep merge two or more objects and return a third object.
  247. * If the first argument is true, the contents of the second object is copied
  248. * into the first object. The merge function can also be used with a single
  249. * object argument to create a deep copy of an object.
  250. *
  251. * @function Highcharts.merge<T>
  252. *
  253. * @param {boolean} extend
  254. * Whether to extend the left-side object (a) or return a whole new
  255. * object.
  256. *
  257. * @param {T|undefined} a
  258. * The first object to extend. When only this is given, the function
  259. * returns a deep copy.
  260. *
  261. * @param {...Array<object|undefined>} [n]
  262. * An object to merge into the previous one.
  263. *
  264. * @return {T}
  265. * The merged object. If the first argument is true, the return is the
  266. * same as the second argument.
  267. */ /**
  268. * Utility function to deep merge two or more objects and return a third object.
  269. * The merge function can also be used with a single object argument to create a
  270. * deep copy of an object.
  271. *
  272. * @function Highcharts.merge<T>
  273. *
  274. * @param {T|undefined} a
  275. * The first object to extend. When only this is given, the function
  276. * returns a deep copy.
  277. *
  278. * @param {...Array<object|undefined>} [n]
  279. * An object to merge into the previous one.
  280. *
  281. * @return {T}
  282. * The merged object. If the first argument is true, the return is the
  283. * same as the second argument.
  284. */
  285. function merge() {
  286. /* eslint-enable valid-jsdoc */
  287. let i, args = arguments, ret = {};
  288. const doCopy = function (copy, original) {
  289. // An object is replacing a primitive
  290. if (typeof copy !== 'object') {
  291. copy = {};
  292. }
  293. objectEach(original, function (value, key) {
  294. // Prototype pollution (#14883)
  295. if (key === '__proto__' || key === 'constructor') {
  296. return;
  297. }
  298. // Copy the contents of objects, but not arrays or DOM nodes
  299. if (isObject(value, true) &&
  300. !isClass(value) &&
  301. !isDOMElement(value)) {
  302. copy[key] = doCopy(copy[key] || {}, value);
  303. // Primitives and arrays are copied over directly
  304. }
  305. else {
  306. copy[key] = original[key];
  307. }
  308. });
  309. return copy;
  310. };
  311. // If first argument is true, copy into the existing object. Used in
  312. // setOptions.
  313. if (args[0] === true) {
  314. ret = args[1];
  315. args = Array.prototype.slice.call(args, 2);
  316. }
  317. // For each argument, extend the return
  318. const len = args.length;
  319. for (i = 0; i < len; i++) {
  320. ret = doCopy(ret, args[i]);
  321. }
  322. return ret;
  323. }
  324. /**
  325. * Constrain a value to within a lower and upper threshold.
  326. *
  327. * @private
  328. * @param {number} value The initial value
  329. * @param {number} min The lower threshold
  330. * @param {number} max The upper threshold
  331. * @return {number} Returns a number value within min and max.
  332. */
  333. function clamp(value, min, max) {
  334. return value > min ? value < max ? value : max : min;
  335. }
  336. // eslint-disable-next-line valid-jsdoc
  337. /**
  338. * Return the deep difference between two objects. It can either return the new
  339. * properties, or optionally return the old values of new properties.
  340. * @private
  341. */
  342. function diffObjects(newer, older, keepOlder, collectionsWithUpdate) {
  343. const ret = {};
  344. /**
  345. * Recurse over a set of options and its current values, and store the
  346. * current values in the ret object.
  347. */
  348. function diff(newer, older, ret, depth) {
  349. const keeper = keepOlder ? older : newer;
  350. objectEach(newer, function (newerVal, key) {
  351. if (!depth &&
  352. collectionsWithUpdate &&
  353. collectionsWithUpdate.indexOf(key) > -1 &&
  354. older[key]) {
  355. newerVal = splat(newerVal);
  356. ret[key] = [];
  357. // Iterate over collections like series, xAxis or yAxis and map
  358. // the items by index.
  359. for (let i = 0; i < Math.max(newerVal.length, older[key].length); i++) {
  360. // Item exists in current data (#6347)
  361. if (older[key][i]) {
  362. // If the item is missing from the new data, we need to
  363. // save the whole config structure. Like when
  364. // responsively updating from a dual axis layout to a
  365. // single axis and back (#13544).
  366. if (newerVal[i] === void 0) {
  367. ret[key][i] = older[key][i];
  368. // Otherwise, proceed
  369. }
  370. else {
  371. ret[key][i] = {};
  372. diff(newerVal[i], older[key][i], ret[key][i], depth + 1);
  373. }
  374. }
  375. }
  376. }
  377. else if (isObject(newerVal, true) &&
  378. !newerVal.nodeType // #10044
  379. ) {
  380. ret[key] = isArray(newerVal) ? [] : {};
  381. diff(newerVal, older[key] || {}, ret[key], depth + 1);
  382. // Delete empty nested objects
  383. if (Object.keys(ret[key]).length === 0 &&
  384. // Except colorAxis which is a special case where the empty
  385. // object means it is enabled. Which is unfortunate and we
  386. // should try to find a better way.
  387. !(key === 'colorAxis' && depth === 0)) {
  388. delete ret[key];
  389. }
  390. }
  391. else if (newer[key] !== older[key] ||
  392. // If the newer key is explicitly undefined, keep it (#10525)
  393. (key in newer && !(key in older))) {
  394. ret[key] = keeper[key];
  395. }
  396. });
  397. }
  398. diff(newer, older, ret, 0);
  399. return ret;
  400. }
  401. /**
  402. * Shortcut for parseInt
  403. *
  404. * @private
  405. * @function Highcharts.pInt
  406. *
  407. * @param {*} s
  408. * any
  409. *
  410. * @param {number} [mag]
  411. * Magnitude
  412. *
  413. * @return {number}
  414. * number
  415. */
  416. function pInt(s, mag) {
  417. return parseInt(s, mag || 10);
  418. }
  419. /**
  420. * Utility function to check for string type.
  421. *
  422. * @function Highcharts.isString
  423. *
  424. * @param {*} s
  425. * The item to check.
  426. *
  427. * @return {boolean}
  428. * True if the argument is a string.
  429. */
  430. function isString(s) {
  431. return typeof s === 'string';
  432. }
  433. /**
  434. * Utility function to check if an item is an array.
  435. *
  436. * @function Highcharts.isArray
  437. *
  438. * @param {*} obj
  439. * The item to check.
  440. *
  441. * @return {boolean}
  442. * True if the argument is an array.
  443. */
  444. function isArray(obj) {
  445. const str = Object.prototype.toString.call(obj);
  446. return str === '[object Array]' || str === '[object Array Iterator]';
  447. }
  448. /**
  449. * Utility function to check if an item is of type object.
  450. *
  451. * @function Highcharts.isObject
  452. *
  453. * @param {*} obj
  454. * The item to check.
  455. *
  456. * @param {boolean} [strict=false]
  457. * Also checks that the object is not an array.
  458. *
  459. * @return {boolean}
  460. * True if the argument is an object.
  461. */
  462. function isObject(obj, strict) {
  463. return (!!obj &&
  464. typeof obj === 'object' &&
  465. (!strict || !isArray(obj))); // eslint-disable-line @typescript-eslint/no-explicit-any
  466. }
  467. /**
  468. * Utility function to check if an Object is a HTML Element.
  469. *
  470. * @function Highcharts.isDOMElement
  471. *
  472. * @param {*} obj
  473. * The item to check.
  474. *
  475. * @return {boolean}
  476. * True if the argument is a HTML Element.
  477. */
  478. function isDOMElement(obj) {
  479. return isObject(obj) && typeof obj.nodeType === 'number';
  480. }
  481. /**
  482. * Utility function to check if an Object is a class.
  483. *
  484. * @function Highcharts.isClass
  485. *
  486. * @param {object|undefined} obj
  487. * The item to check.
  488. *
  489. * @return {boolean}
  490. * True if the argument is a class.
  491. */
  492. function isClass(obj) {
  493. const c = obj && obj.constructor;
  494. return !!(isObject(obj, true) &&
  495. !isDOMElement(obj) &&
  496. (c && c.name && c.name !== 'Object'));
  497. }
  498. /**
  499. * Utility function to check if an item is a number and it is finite (not NaN,
  500. * Infinity or -Infinity).
  501. *
  502. * @function Highcharts.isNumber
  503. *
  504. * @param {*} n
  505. * The item to check.
  506. *
  507. * @return {boolean}
  508. * True if the item is a finite number
  509. */
  510. function isNumber(n) {
  511. return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
  512. }
  513. /**
  514. * Remove the last occurence of an item from an array.
  515. *
  516. * @function Highcharts.erase
  517. *
  518. * @param {Array<*>} arr
  519. * The array.
  520. *
  521. * @param {*} item
  522. * The item to remove.
  523. *
  524. * @return {void}
  525. */
  526. function erase(arr, item) {
  527. let i = arr.length;
  528. while (i--) {
  529. if (arr[i] === item) {
  530. arr.splice(i, 1);
  531. break;
  532. }
  533. }
  534. }
  535. /**
  536. * Insert a series or an axis in a collection with other items, either the
  537. * chart series or yAxis series or axis collections, in the correct order
  538. * according to the index option and whether it is internal. Used internally
  539. * when adding series and axes.
  540. *
  541. * @private
  542. * @function Highcharts.Chart#insertItem
  543. * @param {Highcharts.Series|Highcharts.Axis} item
  544. * The item to insert
  545. * @param {Array<Highcharts.Series>|Array<Highcharts.Axis>} collection
  546. * A collection of items, like `chart.series` or `xAxis.series`.
  547. * @return {number} The index of the series in the collection.
  548. */
  549. function insertItem(item, collection) {
  550. const indexOption = item.options.index, length = collection.length;
  551. let i;
  552. for (
  553. // Internal item (navigator) should always be pushed to the end
  554. i = item.options.isInternal ? length : 0; i < length + 1; i++) {
  555. if (
  556. // No index option, reached the end of the collection,
  557. // equivalent to pushing
  558. !collection[i] ||
  559. // Handle index option, the element to insert has lower index
  560. (isNumber(indexOption) &&
  561. indexOption < pick(collection[i].options.index, collection[i]._i)) ||
  562. // Insert the new item before other internal items
  563. // (navigator)
  564. collection[i].options.isInternal) {
  565. collection.splice(i, 0, item);
  566. break;
  567. }
  568. }
  569. return i;
  570. }
  571. /**
  572. * Adds an item to an array, if it is not present in the array.
  573. *
  574. * @function Highcharts.pushUnique
  575. *
  576. * @param {Array<unknown>} array
  577. * The array to add the item to.
  578. *
  579. * @param {unknown} item
  580. * The item to add.
  581. *
  582. * @return {boolean}
  583. * Returns true, if the item was not present and has been added.
  584. */
  585. function pushUnique(array, item) {
  586. return array.indexOf(item) < 0 && !!array.push(item);
  587. }
  588. /**
  589. * Check if an object is null or undefined.
  590. *
  591. * @function Highcharts.defined
  592. *
  593. * @param {*} obj
  594. * The object to check.
  595. *
  596. * @return {boolean}
  597. * False if the object is null or undefined, otherwise true.
  598. */
  599. function defined(obj) {
  600. return typeof obj !== 'undefined' && obj !== null;
  601. }
  602. /**
  603. * Set or get an attribute or an object of attributes.
  604. *
  605. * To use as a setter, pass a key and a value, or let the second argument be a
  606. * collection of keys and values. When using a collection, passing a value of
  607. * `null` or `undefined` will remove the attribute.
  608. *
  609. * To use as a getter, pass only a string as the second argument.
  610. *
  611. * @function Highcharts.attr
  612. *
  613. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
  614. * The DOM element to receive the attribute(s).
  615. *
  616. * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [keyOrAttribs]
  617. * The property or an object of key-value pairs.
  618. *
  619. * @param {number|string} [value]
  620. * The value if a single property is set.
  621. *
  622. * @return {string|null|undefined}
  623. * When used as a getter, return the value.
  624. */
  625. function attr(elem, keyOrAttribs, value) {
  626. const isGetter = isString(keyOrAttribs) && !defined(value);
  627. let ret;
  628. const attrSingle = (value, key) => {
  629. // Set the value
  630. if (defined(value)) {
  631. elem.setAttribute(key, value);
  632. // Get the value
  633. }
  634. else if (isGetter) {
  635. ret = elem.getAttribute(key);
  636. // IE7 and below cannot get class through getAttribute (#7850)
  637. if (!ret && key === 'class') {
  638. ret = elem.getAttribute(key + 'Name');
  639. }
  640. // Remove the value
  641. }
  642. else {
  643. elem.removeAttribute(key);
  644. }
  645. };
  646. // If keyOrAttribs is a string
  647. if (isString(keyOrAttribs)) {
  648. attrSingle(value, keyOrAttribs);
  649. // Else if keyOrAttribs is defined, it is a hash of key/value pairs
  650. }
  651. else {
  652. objectEach(keyOrAttribs, attrSingle);
  653. }
  654. return ret;
  655. }
  656. /**
  657. * Check if an element is an array, and if not, make it into an array.
  658. *
  659. * @function Highcharts.splat
  660. *
  661. * @param {*} obj
  662. * The object to splat.
  663. *
  664. * @return {Array}
  665. * The produced or original array.
  666. */
  667. function splat(obj) {
  668. return isArray(obj) ? obj : [obj];
  669. }
  670. /**
  671. * Set a timeout if the delay is given, otherwise perform the function
  672. * synchronously.
  673. *
  674. * @function Highcharts.syncTimeout
  675. *
  676. * @param {Function} fn
  677. * The function callback.
  678. *
  679. * @param {number} delay
  680. * Delay in milliseconds.
  681. *
  682. * @param {*} [context]
  683. * An optional context to send to the function callback.
  684. *
  685. * @return {number}
  686. * An identifier for the timeout that can later be cleared with
  687. * Highcharts.clearTimeout. Returns -1 if there is no timeout.
  688. */
  689. function syncTimeout(fn, delay, context) {
  690. if (delay > 0) {
  691. return setTimeout(fn, delay, context);
  692. }
  693. fn.call(0, context);
  694. return -1;
  695. }
  696. /**
  697. * Internal clear timeout. The function checks that the `id` was not removed
  698. * (e.g. by `chart.destroy()`). For the details see
  699. * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
  700. *
  701. * @function Highcharts.clearTimeout
  702. *
  703. * @param {number|undefined} id
  704. * Id of a timeout.
  705. */
  706. function internalClearTimeout(id) {
  707. if (defined(id)) {
  708. clearTimeout(id);
  709. }
  710. }
  711. /* eslint-disable valid-jsdoc */
  712. /**
  713. * Utility function to extend an object with the members of another.
  714. *
  715. * @function Highcharts.extend<T>
  716. *
  717. * @param {T|undefined} a
  718. * The object to be extended.
  719. *
  720. * @param {Partial<T>} b
  721. * The object to add to the first one.
  722. *
  723. * @return {T}
  724. * Object a, the original object.
  725. */
  726. function extend(a, b) {
  727. /* eslint-enable valid-jsdoc */
  728. let n;
  729. if (!a) {
  730. a = {};
  731. }
  732. for (n in b) { // eslint-disable-line guard-for-in
  733. a[n] = b[n];
  734. }
  735. return a;
  736. }
  737. /* eslint-disable valid-jsdoc */
  738. /**
  739. * Return the first value that is not null or undefined.
  740. *
  741. * @function Highcharts.pick<T>
  742. *
  743. * @param {...Array<T|null|undefined>} items
  744. * Variable number of arguments to inspect.
  745. *
  746. * @return {T}
  747. * The value of the first argument that is not null or undefined.
  748. */
  749. function pick() {
  750. const args = arguments;
  751. const length = args.length;
  752. for (let i = 0; i < length; i++) {
  753. const arg = args[i];
  754. if (typeof arg !== 'undefined' && arg !== null) {
  755. return arg;
  756. }
  757. }
  758. }
  759. /**
  760. * Set CSS on a given element.
  761. *
  762. * @function Highcharts.css
  763. *
  764. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} el
  765. * An HTML DOM element.
  766. *
  767. * @param {Highcharts.CSSObject} styles
  768. * Style object with camel case property names.
  769. *
  770. * @return {void}
  771. */
  772. function css(el, styles) {
  773. if (H.isMS && !H.svg) { // #2686
  774. if (styles && defined(styles.opacity)) {
  775. styles.filter = `alpha(opacity=${styles.opacity * 100})`;
  776. }
  777. }
  778. extend(el.style, styles);
  779. }
  780. /**
  781. * Utility function to create an HTML element with attributes and styles.
  782. *
  783. * @function Highcharts.createElement
  784. *
  785. * @param {string} tag
  786. * The HTML tag.
  787. *
  788. * @param {Highcharts.HTMLAttributes} [attribs]
  789. * Attributes as an object of key-value pairs.
  790. *
  791. * @param {Highcharts.CSSObject} [styles]
  792. * Styles as an object of key-value pairs.
  793. *
  794. * @param {Highcharts.HTMLDOMElement} [parent]
  795. * The parent HTML object.
  796. *
  797. * @param {boolean} [nopad=false]
  798. * If true, remove all padding, border and margin.
  799. *
  800. * @return {Highcharts.HTMLDOMElement}
  801. * The created DOM element.
  802. */
  803. function createElement(tag, attribs, styles, parent, nopad) {
  804. const el = doc.createElement(tag);
  805. if (attribs) {
  806. extend(el, attribs);
  807. }
  808. if (nopad) {
  809. css(el, { padding: '0', border: 'none', margin: '0' });
  810. }
  811. if (styles) {
  812. css(el, styles);
  813. }
  814. if (parent) {
  815. parent.appendChild(el);
  816. }
  817. return el;
  818. }
  819. // eslint-disable-next-line valid-jsdoc
  820. /**
  821. * Extend a prototyped class by new members.
  822. *
  823. * @deprecated
  824. * @function Highcharts.extendClass<T>
  825. *
  826. * @param {Highcharts.Class<T>} parent
  827. * The parent prototype to inherit.
  828. *
  829. * @param {Highcharts.Dictionary<*>} members
  830. * A collection of prototype members to add or override compared to the
  831. * parent prototype.
  832. *
  833. * @return {Highcharts.Class<T>}
  834. * A new prototype.
  835. */
  836. function extendClass(parent, members) {
  837. const obj = (function () { });
  838. obj.prototype = new parent(); // eslint-disable-line new-cap
  839. extend(obj.prototype, members);
  840. return obj;
  841. }
  842. /**
  843. * Left-pad a string to a given length by adding a character repetitively.
  844. *
  845. * @function Highcharts.pad
  846. *
  847. * @param {number} number
  848. * The input string or number.
  849. *
  850. * @param {number} [length]
  851. * The desired string length.
  852. *
  853. * @param {string} [padder=0]
  854. * The character to pad with.
  855. *
  856. * @return {string}
  857. * The padded string.
  858. */
  859. function pad(number, length, padder) {
  860. return new Array((length || 2) +
  861. 1 -
  862. String(number)
  863. .replace('-', '')
  864. .length).join(padder || '0') + number;
  865. }
  866. /**
  867. * Return a length based on either the integer value, or a percentage of a base.
  868. *
  869. * @function Highcharts.relativeLength
  870. *
  871. * @param {Highcharts.RelativeSize} value
  872. * A percentage string or a number.
  873. *
  874. * @param {number} base
  875. * The full length that represents 100%.
  876. *
  877. * @param {number} [offset=0]
  878. * A pixel offset to apply for percentage values. Used internally in
  879. * axis positioning.
  880. *
  881. * @return {number}
  882. * The computed length.
  883. */
  884. function relativeLength(value, base, offset) {
  885. return (/%$/).test(value) ?
  886. (base * parseFloat(value) / 100) + (offset || 0) :
  887. parseFloat(value);
  888. }
  889. /**
  890. * Wrap a method with extended functionality, preserving the original function.
  891. *
  892. * @function Highcharts.wrap
  893. *
  894. * @param {*} obj
  895. * The context object that the method belongs to. In real cases, this is
  896. * often a prototype.
  897. *
  898. * @param {string} method
  899. * The name of the method to extend.
  900. *
  901. * @param {Highcharts.WrapProceedFunction} func
  902. * A wrapper function callback. This function is called with the same
  903. * arguments as the original function, except that the original function
  904. * is unshifted and passed as the first argument.
  905. */
  906. function wrap(obj, method, func) {
  907. const proceed = obj[method];
  908. obj[method] = function () {
  909. const outerArgs = arguments, scope = this;
  910. return func.apply(this, [
  911. function () {
  912. return proceed.apply(scope, arguments.length ? arguments : outerArgs);
  913. }
  914. ].concat([].slice.call(arguments)));
  915. };
  916. }
  917. /**
  918. * Get the magnitude of a number.
  919. *
  920. * @function Highcharts.getMagnitude
  921. *
  922. * @param {number} num
  923. * The number.
  924. *
  925. * @return {number}
  926. * The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
  927. */
  928. function getMagnitude(num) {
  929. return Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
  930. }
  931. /**
  932. * Take an interval and normalize it to multiples of round numbers.
  933. *
  934. * @deprecated
  935. * @function Highcharts.normalizeTickInterval
  936. *
  937. * @param {number} interval
  938. * The raw, un-rounded interval.
  939. *
  940. * @param {Array<*>} [multiples]
  941. * Allowed multiples.
  942. *
  943. * @param {number} [magnitude]
  944. * The magnitude of the number.
  945. *
  946. * @param {boolean} [allowDecimals]
  947. * Whether to allow decimals.
  948. *
  949. * @param {boolean} [hasTickAmount]
  950. * If it has tickAmount, avoid landing on tick intervals lower than
  951. * original.
  952. *
  953. * @return {number}
  954. * The normalized interval.
  955. *
  956. * @todo
  957. * Move this function to the Axis prototype. It is here only for historical
  958. * reasons.
  959. */
  960. function normalizeTickInterval(interval, multiples, magnitude, allowDecimals, hasTickAmount) {
  961. let i, retInterval = interval;
  962. // round to a tenfold of 1, 2, 2.5 or 5
  963. magnitude = pick(magnitude, getMagnitude(interval));
  964. const normalized = interval / magnitude;
  965. // multiples for a linear scale
  966. if (!multiples) {
  967. multiples = hasTickAmount ?
  968. // Finer grained ticks when the tick amount is hard set, including
  969. // when alignTicks is true on multiple axes (#4580).
  970. [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
  971. // Else, let ticks fall on rounder numbers
  972. [1, 2, 2.5, 5, 10];
  973. // the allowDecimals option
  974. if (allowDecimals === false) {
  975. if (magnitude === 1) {
  976. multiples = multiples.filter(function (num) {
  977. return num % 1 === 0;
  978. });
  979. }
  980. else if (magnitude <= 0.1) {
  981. multiples = [1 / magnitude];
  982. }
  983. }
  984. }
  985. // normalize the interval to the nearest multiple
  986. for (i = 0; i < multiples.length; i++) {
  987. retInterval = multiples[i];
  988. // only allow tick amounts smaller than natural
  989. if ((hasTickAmount &&
  990. retInterval * magnitude >= interval) ||
  991. (!hasTickAmount &&
  992. (normalized <=
  993. (multiples[i] +
  994. (multiples[i + 1] || multiples[i])) / 2))) {
  995. break;
  996. }
  997. }
  998. // Multiply back to the correct magnitude. Correct floats to appropriate
  999. // precision (#6085).
  1000. retInterval = correctFloat(retInterval * magnitude, -Math.round(Math.log(0.001) / Math.LN10));
  1001. return retInterval;
  1002. }
  1003. /**
  1004. * Sort an object array and keep the order of equal items. The ECMAScript
  1005. * standard does not specify the behaviour when items are equal.
  1006. *
  1007. * @function Highcharts.stableSort
  1008. *
  1009. * @param {Array<*>} arr
  1010. * The array to sort.
  1011. *
  1012. * @param {Function} sortFunction
  1013. * The function to sort it with, like with regular Array.prototype.sort.
  1014. */
  1015. function stableSort(arr, sortFunction) {
  1016. // @todo It seems like Chrome since v70 sorts in a stable way internally,
  1017. // plus all other browsers do it, so over time we may be able to remove this
  1018. // function
  1019. const length = arr.length;
  1020. let sortValue, i;
  1021. // Add index to each item
  1022. for (i = 0; i < length; i++) {
  1023. arr[i].safeI = i; // stable sort index
  1024. }
  1025. arr.sort(function (a, b) {
  1026. sortValue = sortFunction(a, b);
  1027. return sortValue === 0 ? a.safeI - b.safeI : sortValue;
  1028. });
  1029. // Remove index from items
  1030. for (i = 0; i < length; i++) {
  1031. delete arr[i].safeI; // stable sort index
  1032. }
  1033. }
  1034. /**
  1035. * Non-recursive method to find the lowest member of an array. `Math.min` raises
  1036. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1037. * than 150.000 points. This method is slightly slower, but safe.
  1038. *
  1039. * @function Highcharts.arrayMin
  1040. *
  1041. * @param {Array<*>} data
  1042. * An array of numbers.
  1043. *
  1044. * @return {number}
  1045. * The lowest number.
  1046. */
  1047. function arrayMin(data) {
  1048. let i = data.length, min = data[0];
  1049. while (i--) {
  1050. if (data[i] < min) {
  1051. min = data[i];
  1052. }
  1053. }
  1054. return min;
  1055. }
  1056. /**
  1057. * Non-recursive method to find the lowest member of an array. `Math.max` raises
  1058. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1059. * than 150.000 points. This method is slightly slower, but safe.
  1060. *
  1061. * @function Highcharts.arrayMax
  1062. *
  1063. * @param {Array<*>} data
  1064. * An array of numbers.
  1065. *
  1066. * @return {number}
  1067. * The highest number.
  1068. */
  1069. function arrayMax(data) {
  1070. let i = data.length, max = data[0];
  1071. while (i--) {
  1072. if (data[i] > max) {
  1073. max = data[i];
  1074. }
  1075. }
  1076. return max;
  1077. }
  1078. /**
  1079. * Utility method that destroys any SVGElement instances that are properties on
  1080. * the given object. It loops all properties and invokes destroy if there is a
  1081. * destroy method. The property is then delete.
  1082. *
  1083. * @function Highcharts.destroyObjectProperties
  1084. *
  1085. * @param {*} obj
  1086. * The object to destroy properties on.
  1087. *
  1088. * @param {*} [except]
  1089. * Exception, do not destroy this property, only delete it.
  1090. */
  1091. function destroyObjectProperties(obj, except) {
  1092. objectEach(obj, function (val, n) {
  1093. // If the object is non-null and destroy is defined
  1094. if (val && val !== except && val.destroy) {
  1095. // Invoke the destroy
  1096. val.destroy();
  1097. }
  1098. // Delete the property from the object.
  1099. delete obj[n];
  1100. });
  1101. }
  1102. /**
  1103. * Discard a HTML element
  1104. *
  1105. * @function Highcharts.discardElement
  1106. *
  1107. * @param {Highcharts.HTMLDOMElement} element
  1108. * The HTML node to discard.
  1109. */
  1110. function discardElement(element) {
  1111. if (element && element.parentElement) {
  1112. element.parentElement.removeChild(element);
  1113. }
  1114. }
  1115. /**
  1116. * Fix JS round off float errors.
  1117. *
  1118. * @function Highcharts.correctFloat
  1119. *
  1120. * @param {number} num
  1121. * A float number to fix.
  1122. *
  1123. * @param {number} [prec=14]
  1124. * The precision.
  1125. *
  1126. * @return {number}
  1127. * The corrected float number.
  1128. */
  1129. function correctFloat(num, prec) {
  1130. // When the number is higher than 1e14 use the number (#16275)
  1131. return num > 1e14 ? num : parseFloat(num.toPrecision(prec || 14));
  1132. }
  1133. /**
  1134. * The time unit lookup
  1135. *
  1136. * @ignore
  1137. */
  1138. const timeUnits = {
  1139. millisecond: 1,
  1140. second: 1000,
  1141. minute: 60000,
  1142. hour: 3600000,
  1143. day: 24 * 3600000,
  1144. week: 7 * 24 * 3600000,
  1145. month: 28 * 24 * 3600000,
  1146. year: 364 * 24 * 3600000
  1147. };
  1148. /**
  1149. * Easing definition
  1150. *
  1151. * @private
  1152. * @function Math.easeInOutSine
  1153. *
  1154. * @param {number} pos
  1155. * Current position, ranging from 0 to 1.
  1156. *
  1157. * @return {number}
  1158. * Ease result
  1159. */
  1160. Math.easeInOutSine = function (pos) {
  1161. return -0.5 * (Math.cos(Math.PI * pos) - 1);
  1162. };
  1163. /**
  1164. * Find the closest distance between two values of a two-dimensional array
  1165. * @private
  1166. * @function Highcharts.getClosestDistance
  1167. *
  1168. * @param {Array<Array<number>>} arrays
  1169. * An array of arrays of numbers
  1170. *
  1171. * @return {number | undefined}
  1172. * The closest distance between values
  1173. */
  1174. function getClosestDistance(arrays, onError) {
  1175. const allowNegative = !onError;
  1176. let closest, loopLength, distance, i;
  1177. arrays.forEach((xData) => {
  1178. if (xData.length > 1) {
  1179. loopLength = xData.length - 1;
  1180. for (i = loopLength; i > 0; i--) {
  1181. distance = xData[i] - xData[i - 1];
  1182. if (distance < 0 && !allowNegative) {
  1183. onError === null || onError === void 0 ? void 0 : onError();
  1184. // Only one call
  1185. onError = void 0;
  1186. }
  1187. else if (distance && (typeof closest === 'undefined' || distance < closest)) {
  1188. closest = distance;
  1189. }
  1190. }
  1191. }
  1192. });
  1193. return closest;
  1194. }
  1195. /**
  1196. * Returns the value of a property path on a given object.
  1197. *
  1198. * @private
  1199. * @function getNestedProperty
  1200. *
  1201. * @param {string} path
  1202. * Path to the property, for example `custom.myValue`.
  1203. *
  1204. * @param {unknown} obj
  1205. * Instance containing the property on the specific path.
  1206. *
  1207. * @return {unknown}
  1208. * The unknown property value.
  1209. */
  1210. function getNestedProperty(path, parent) {
  1211. const pathElements = path.split('.');
  1212. while (pathElements.length && defined(parent)) {
  1213. const pathElement = pathElements.shift();
  1214. // Filter on the key
  1215. if (typeof pathElement === 'undefined' ||
  1216. pathElement === '__proto__') {
  1217. return; // undefined
  1218. }
  1219. if (pathElement === 'this') {
  1220. let thisProp;
  1221. if (isObject(parent)) {
  1222. thisProp = parent['@this'];
  1223. }
  1224. return thisProp !== null && thisProp !== void 0 ? thisProp : parent;
  1225. }
  1226. const child = parent[pathElement];
  1227. // Filter on the child
  1228. if (!defined(child) ||
  1229. typeof child === 'function' ||
  1230. typeof child.nodeType === 'number' ||
  1231. child === win) {
  1232. return; // undefined
  1233. }
  1234. // Else, proceed
  1235. parent = child;
  1236. }
  1237. return parent;
  1238. }
  1239. /**
  1240. * Get the computed CSS value for given element and property, only for numerical
  1241. * properties. For width and height, the dimension of the inner box (excluding
  1242. * padding) is returned. Used for fitting the chart within the container.
  1243. *
  1244. * @function Highcharts.getStyle
  1245. *
  1246. * @param {Highcharts.HTMLDOMElement} el
  1247. * An HTML element.
  1248. *
  1249. * @param {string} prop
  1250. * The property name.
  1251. *
  1252. * @param {boolean} [toInt=true]
  1253. * Parse to integer.
  1254. *
  1255. * @return {number|string|undefined}
  1256. * The style value.
  1257. */
  1258. function getStyle(el, prop, toInt) {
  1259. let style;
  1260. // For width and height, return the actual inner pixel size (#4913)
  1261. if (prop === 'width') {
  1262. let offsetWidth = Math.min(el.offsetWidth, el.scrollWidth);
  1263. // In flex boxes, we need to use getBoundingClientRect and floor it,
  1264. // because scrollWidth doesn't support subpixel precision (#6427) ...
  1265. const boundingClientRectWidth = el.getBoundingClientRect &&
  1266. el.getBoundingClientRect().width;
  1267. // ...unless if the containing div or its parents are transform-scaled
  1268. // down, in which case the boundingClientRect can't be used as it is
  1269. // also scaled down (#9871, #10498).
  1270. if (boundingClientRectWidth < offsetWidth &&
  1271. boundingClientRectWidth >= offsetWidth - 1) {
  1272. offsetWidth = Math.floor(boundingClientRectWidth);
  1273. }
  1274. return Math.max(0, // #8377
  1275. (offsetWidth -
  1276. (getStyle(el, 'padding-left', true) || 0) -
  1277. (getStyle(el, 'padding-right', true) || 0)));
  1278. }
  1279. if (prop === 'height') {
  1280. return Math.max(0, // #8377
  1281. (Math.min(el.offsetHeight, el.scrollHeight) -
  1282. (getStyle(el, 'padding-top', true) || 0) -
  1283. (getStyle(el, 'padding-bottom', true) || 0)));
  1284. }
  1285. // Otherwise, get the computed style
  1286. const css = win.getComputedStyle(el, void 0); // eslint-disable-line no-undefined
  1287. if (css) {
  1288. style = css.getPropertyValue(prop);
  1289. if (pick(toInt, prop !== 'opacity')) {
  1290. style = pInt(style);
  1291. }
  1292. }
  1293. return style;
  1294. }
  1295. /**
  1296. * Search for an item in an array.
  1297. *
  1298. * @function Highcharts.inArray
  1299. *
  1300. * @deprecated
  1301. *
  1302. * @param {*} item
  1303. * The item to search for.
  1304. *
  1305. * @param {Array<*>} arr
  1306. * The array or node collection to search in.
  1307. *
  1308. * @param {number} [fromIndex=0]
  1309. * The index to start searching from.
  1310. *
  1311. * @return {number}
  1312. * The index within the array, or -1 if not found.
  1313. */
  1314. function inArray(item, arr, fromIndex) {
  1315. error(32, false, void 0, { 'Highcharts.inArray': 'use Array.indexOf' });
  1316. return arr.indexOf(item, fromIndex);
  1317. }
  1318. /**
  1319. * Return the value of the first element in the array that satisfies the
  1320. * provided testing function.
  1321. *
  1322. * @function Highcharts.find<T>
  1323. *
  1324. * @param {Array<T>} arr
  1325. * The array to test.
  1326. *
  1327. * @param {Function} callback
  1328. * The callback function. The function receives the item as the first
  1329. * argument. Return `true` if this item satisfies the condition.
  1330. *
  1331. * @return {T|undefined}
  1332. * The value of the element.
  1333. */
  1334. const find = Array.prototype.find ?
  1335. function (arr, callback) {
  1336. return arr.find(callback);
  1337. } :
  1338. // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
  1339. function (arr, callback) {
  1340. let i;
  1341. const length = arr.length;
  1342. for (i = 0; i < length; i++) {
  1343. if (callback(arr[i], i)) { // eslint-disable-line node/callback-return
  1344. return arr[i];
  1345. }
  1346. }
  1347. };
  1348. /**
  1349. * Returns an array of a given object's own properties.
  1350. *
  1351. * @function Highcharts.keys
  1352. * @deprecated
  1353. *
  1354. * @param {*} obj
  1355. * The object of which the properties are to be returned.
  1356. *
  1357. * @return {Array<string>}
  1358. * An array of strings that represents all the properties.
  1359. */
  1360. function keys(obj) {
  1361. error(32, false, void 0, { 'Highcharts.keys': 'use Object.keys' });
  1362. return Object.keys(obj);
  1363. }
  1364. /**
  1365. * Get the element's offset position, corrected for `overflow: auto`.
  1366. *
  1367. * @function Highcharts.offset
  1368. *
  1369. * @param {global.Element} el
  1370. * The DOM element.
  1371. *
  1372. * @return {Highcharts.OffsetObject}
  1373. * An object containing `left` and `top` properties for the position in
  1374. * the page.
  1375. */
  1376. function offset(el) {
  1377. const docElem = doc.documentElement, box = (el.parentElement || el.parentNode) ?
  1378. el.getBoundingClientRect() :
  1379. { top: 0, left: 0, width: 0, height: 0 };
  1380. return {
  1381. top: box.top + (win.pageYOffset || docElem.scrollTop) -
  1382. (docElem.clientTop || 0),
  1383. left: box.left + (win.pageXOffset || docElem.scrollLeft) -
  1384. (docElem.clientLeft || 0),
  1385. width: box.width,
  1386. height: box.height
  1387. };
  1388. }
  1389. /* eslint-disable valid-jsdoc */
  1390. /**
  1391. * Iterate over object key pairs in an object.
  1392. *
  1393. * @function Highcharts.objectEach<T>
  1394. *
  1395. * @param {*} obj
  1396. * The object to iterate over.
  1397. *
  1398. * @param {Highcharts.ObjectEachCallbackFunction<T>} fn
  1399. * The iterator callback. It passes three arguments:
  1400. * * value - The property value.
  1401. * * key - The property key.
  1402. * * obj - The object that objectEach is being applied to.
  1403. *
  1404. * @param {T} [ctx]
  1405. * The context.
  1406. */
  1407. function objectEach(obj, fn, ctx) {
  1408. /* eslint-enable valid-jsdoc */
  1409. for (const key in obj) {
  1410. if (Object.hasOwnProperty.call(obj, key)) {
  1411. fn.call(ctx || obj[key], obj[key], key, obj);
  1412. }
  1413. }
  1414. }
  1415. /**
  1416. * Iterate over an array.
  1417. *
  1418. * @deprecated
  1419. * @function Highcharts.each
  1420. *
  1421. * @param {Array<*>} arr
  1422. * The array to iterate over.
  1423. *
  1424. * @param {Function} fn
  1425. * The iterator callback. It passes three arguments:
  1426. * - `item`: The array item.
  1427. * - `index`: The item's index in the array.
  1428. * - `arr`: The array that each is being applied to.
  1429. *
  1430. * @param {*} [ctx]
  1431. * The context.
  1432. *
  1433. * @return {void}
  1434. */
  1435. /**
  1436. * Filter an array by a callback.
  1437. *
  1438. * @deprecated
  1439. * @function Highcharts.grep
  1440. *
  1441. * @param {Array<*>} arr
  1442. * The array to filter.
  1443. *
  1444. * @param {Function} callback
  1445. * The callback function. The function receives the item as the first
  1446. * argument. Return `true` if the item is to be preserved.
  1447. *
  1448. * @return {Array<*>}
  1449. * A new, filtered array.
  1450. */
  1451. /**
  1452. * Map an array by a callback.
  1453. *
  1454. * @deprecated
  1455. * @function Highcharts.map
  1456. *
  1457. * @param {Array<*>} arr
  1458. * The array to map.
  1459. *
  1460. * @param {Function} fn
  1461. * The callback function. Return the new value for the new array.
  1462. *
  1463. * @return {Array<*>}
  1464. * A new array item with modified items.
  1465. */
  1466. /**
  1467. * Reduce an array to a single value.
  1468. *
  1469. * @deprecated
  1470. * @function Highcharts.reduce
  1471. *
  1472. * @param {Array<*>} arr
  1473. * The array to reduce.
  1474. *
  1475. * @param {Function} fn
  1476. * The callback function. Return the reduced value. Receives 4
  1477. * arguments: Accumulated/reduced value, current value, current array
  1478. * index, and the array.
  1479. *
  1480. * @param {*} initialValue
  1481. * The initial value of the accumulator.
  1482. *
  1483. * @return {*}
  1484. * The reduced value.
  1485. */
  1486. /**
  1487. * Test whether at least one element in the array passes the test implemented by
  1488. * the provided function.
  1489. *
  1490. * @deprecated
  1491. * @function Highcharts.some
  1492. *
  1493. * @param {Array<*>} arr
  1494. * The array to test
  1495. *
  1496. * @param {Function} fn
  1497. * The function to run on each item. Return truty to pass the test.
  1498. * Receives arguments `currentValue`, `index` and `array`.
  1499. *
  1500. * @param {*} ctx
  1501. * The context.
  1502. *
  1503. * @return {boolean}
  1504. */
  1505. objectEach({
  1506. map: 'map',
  1507. each: 'forEach',
  1508. grep: 'filter',
  1509. reduce: 'reduce',
  1510. some: 'some'
  1511. }, function (val, key) {
  1512. H[key] = function (arr) {
  1513. error(32, false, void 0, { [`Highcharts.${key}`]: `use Array.${val}` });
  1514. return Array.prototype[val].apply(arr, [].slice.call(arguments, 1));
  1515. };
  1516. });
  1517. /* eslint-disable valid-jsdoc */
  1518. /**
  1519. * Add an event listener.
  1520. *
  1521. * @function Highcharts.addEvent<T>
  1522. *
  1523. * @param {Highcharts.Class<T>|T} el
  1524. * The element or object to add a listener to. It can be a
  1525. * {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
  1526. *
  1527. * @param {string} type
  1528. * The event type.
  1529. *
  1530. * @param {Highcharts.EventCallbackFunction<T>|Function} fn
  1531. * The function callback to execute when the event is fired.
  1532. *
  1533. * @param {Highcharts.EventOptionsObject} [options]
  1534. * Options for adding the event.
  1535. *
  1536. * @return {Function}
  1537. * A callback function to remove the added event.
  1538. */
  1539. function addEvent(el, type, fn, options = {}) {
  1540. /* eslint-enable valid-jsdoc */
  1541. // Add hcEvents to either the prototype (in case we're running addEvent on a
  1542. // class) or the instance. If hasOwnProperty('hcEvents') is false, it is
  1543. // inherited down the prototype chain, in which case we need to set the
  1544. // property on this instance (which may itself be a prototype).
  1545. const owner = typeof el === 'function' && el.prototype || el;
  1546. if (!Object.hasOwnProperty.call(owner, 'hcEvents')) {
  1547. owner.hcEvents = {};
  1548. }
  1549. const events = owner.hcEvents;
  1550. // Allow click events added to points, otherwise they will be prevented by
  1551. // the TouchPointer.pinch function after a pinch zoom operation (#7091).
  1552. if (H.Point && // without H a dependency loop occurs
  1553. el instanceof H.Point &&
  1554. el.series &&
  1555. el.series.chart) {
  1556. el.series.chart.runTrackerClick = true;
  1557. }
  1558. // Handle DOM events
  1559. // If the browser supports passive events, add it to improve performance
  1560. // on touch events (#11353).
  1561. const addEventListener = el.addEventListener;
  1562. if (addEventListener) {
  1563. addEventListener.call(el, type, fn, H.supportsPassiveEvents ? {
  1564. passive: options.passive === void 0 ?
  1565. type.indexOf('touch') !== -1 : options.passive,
  1566. capture: false
  1567. } : false);
  1568. }
  1569. if (!events[type]) {
  1570. events[type] = [];
  1571. }
  1572. const eventObject = {
  1573. fn,
  1574. order: typeof options.order === 'number' ? options.order : Infinity
  1575. };
  1576. events[type].push(eventObject);
  1577. // Order the calls
  1578. events[type].sort((a, b) => a.order - b.order);
  1579. // Return a function that can be called to remove this event.
  1580. return function () {
  1581. removeEvent(el, type, fn);
  1582. };
  1583. }
  1584. /* eslint-disable valid-jsdoc */
  1585. /**
  1586. * Remove an event that was added with {@link Highcharts#addEvent}.
  1587. *
  1588. * @function Highcharts.removeEvent<T>
  1589. *
  1590. * @param {Highcharts.Class<T>|T} el
  1591. * The element to remove events on.
  1592. *
  1593. * @param {string} [type]
  1594. * The type of events to remove. If undefined, all events are removed
  1595. * from the element.
  1596. *
  1597. * @param {Highcharts.EventCallbackFunction<T>} [fn]
  1598. * The specific callback to remove. If undefined, all events that match
  1599. * the element and optionally the type are removed.
  1600. *
  1601. * @return {void}
  1602. */
  1603. function removeEvent(el, type, fn) {
  1604. /* eslint-enable valid-jsdoc */
  1605. /**
  1606. * @private
  1607. */
  1608. function removeOneEvent(type, fn) {
  1609. const removeEventListener = el.removeEventListener;
  1610. if (removeEventListener) {
  1611. removeEventListener.call(el, type, fn, false);
  1612. }
  1613. }
  1614. /**
  1615. * @private
  1616. */
  1617. function removeAllEvents(eventCollection) {
  1618. let types, len;
  1619. if (!el.nodeName) {
  1620. return; // break on non-DOM events
  1621. }
  1622. if (type) {
  1623. types = {};
  1624. types[type] = true;
  1625. }
  1626. else {
  1627. types = eventCollection;
  1628. }
  1629. objectEach(types, function (_val, n) {
  1630. if (eventCollection[n]) {
  1631. len = eventCollection[n].length;
  1632. while (len--) {
  1633. removeOneEvent(n, eventCollection[n][len].fn);
  1634. }
  1635. }
  1636. });
  1637. }
  1638. const owner = typeof el === 'function' && el.prototype || el;
  1639. if (Object.hasOwnProperty.call(owner, 'hcEvents')) {
  1640. const events = owner.hcEvents;
  1641. if (type) {
  1642. const typeEvents = (events[type] || []);
  1643. if (fn) {
  1644. events[type] = typeEvents.filter(function (obj) {
  1645. return fn !== obj.fn;
  1646. });
  1647. removeOneEvent(type, fn);
  1648. }
  1649. else {
  1650. removeAllEvents(events);
  1651. events[type] = [];
  1652. }
  1653. }
  1654. else {
  1655. removeAllEvents(events);
  1656. delete owner.hcEvents;
  1657. }
  1658. }
  1659. }
  1660. /* eslint-disable valid-jsdoc */
  1661. /**
  1662. * Fire an event that was registered with {@link Highcharts#addEvent}.
  1663. *
  1664. * @function Highcharts.fireEvent<T>
  1665. *
  1666. * @param {T} el
  1667. * The object to fire the event on. It can be a {@link HTMLDOMElement},
  1668. * an {@link SVGElement} or any other object.
  1669. *
  1670. * @param {string} type
  1671. * The type of event.
  1672. *
  1673. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  1674. * Custom event arguments that are passed on as an argument to the event
  1675. * handler.
  1676. *
  1677. * @param {Highcharts.EventCallbackFunction<T>|Function} [defaultFunction]
  1678. * The default function to execute if the other listeners haven't
  1679. * returned false.
  1680. *
  1681. * @return {void}
  1682. */
  1683. function fireEvent(el, type, eventArguments, defaultFunction) {
  1684. /* eslint-enable valid-jsdoc */
  1685. let e, i;
  1686. eventArguments = eventArguments || {};
  1687. if (doc.createEvent &&
  1688. (el.dispatchEvent ||
  1689. (el.fireEvent &&
  1690. // Enable firing events on Highcharts instance.
  1691. el !== H))) {
  1692. e = doc.createEvent('Events');
  1693. e.initEvent(type, true, true);
  1694. eventArguments = extend(e, eventArguments);
  1695. if (el.dispatchEvent) {
  1696. el.dispatchEvent(eventArguments);
  1697. }
  1698. else {
  1699. el.fireEvent(type, eventArguments);
  1700. }
  1701. }
  1702. else if (el.hcEvents) {
  1703. if (!eventArguments.target) {
  1704. // We're running a custom event
  1705. extend(eventArguments, {
  1706. // Attach a simple preventDefault function to skip
  1707. // default handler if called. The built-in
  1708. // defaultPrevented property is not overwritable (#5112)
  1709. preventDefault: function () {
  1710. eventArguments.defaultPrevented = true;
  1711. },
  1712. // Setting target to native events fails with clicking
  1713. // the zoom-out button in Chrome.
  1714. target: el,
  1715. // If the type is not set, we're running a custom event
  1716. // (#2297). If it is set, we're running a browser event.
  1717. type: type
  1718. });
  1719. }
  1720. const events = [];
  1721. let object = el;
  1722. let multilevel = false;
  1723. // Recurse up the inheritance chain and collect hcEvents set as own
  1724. // objects on the prototypes.
  1725. while (object.hcEvents) {
  1726. if (Object.hasOwnProperty.call(object, 'hcEvents') &&
  1727. object.hcEvents[type]) {
  1728. if (events.length) {
  1729. multilevel = true;
  1730. }
  1731. events.unshift.apply(events, object.hcEvents[type]);
  1732. }
  1733. object = Object.getPrototypeOf(object);
  1734. }
  1735. // For performance reasons, only sort the event handlers in case we are
  1736. // dealing with multiple levels in the prototype chain. Otherwise, the
  1737. // events are already sorted in the addEvent function.
  1738. if (multilevel) {
  1739. // Order the calls
  1740. events.sort((a, b) => a.order - b.order);
  1741. }
  1742. // Call the collected event handlers
  1743. events.forEach((obj) => {
  1744. // If the event handler returns false, prevent the default handler
  1745. // from executing
  1746. if (obj.fn.call(el, eventArguments) === false) {
  1747. eventArguments.preventDefault();
  1748. }
  1749. });
  1750. }
  1751. // Run the default if not prevented
  1752. if (defaultFunction && !eventArguments.defaultPrevented) {
  1753. defaultFunction.call(el, eventArguments);
  1754. }
  1755. }
  1756. let serialMode;
  1757. /**
  1758. * Get a unique key for using in internal element id's and pointers. The key is
  1759. * composed of a random hash specific to this Highcharts instance, and a
  1760. * counter.
  1761. *
  1762. * @example
  1763. * let id = uniqueKey(); // => 'highcharts-x45f6hp-0'
  1764. *
  1765. * @function Highcharts.uniqueKey
  1766. *
  1767. * @return {string}
  1768. * A unique key.
  1769. */
  1770. const uniqueKey = (function () {
  1771. const hash = Math.random().toString(36).substring(2, 9) + '-';
  1772. let id = 0;
  1773. return function () {
  1774. return 'highcharts-' + (serialMode ? '' : hash) + id++;
  1775. };
  1776. }());
  1777. /**
  1778. * Activates a serial mode for element IDs provided by
  1779. * {@link Highcharts.uniqueKey}. This mode can be used in automated tests, where
  1780. * a simple comparison of two rendered SVG graphics is needed.
  1781. *
  1782. * **Note:** This is only for testing purposes and will break functionality in
  1783. * webpages with multiple charts.
  1784. *
  1785. * @example
  1786. * if (
  1787. * process &&
  1788. * process.env.NODE_ENV === 'development'
  1789. * ) {
  1790. * Highcharts.useSerialIds(true);
  1791. * }
  1792. *
  1793. * @function Highcharts.useSerialIds
  1794. *
  1795. * @param {boolean} [mode]
  1796. * Changes the state of serial mode.
  1797. *
  1798. * @return {boolean|undefined}
  1799. * State of the serial mode.
  1800. */
  1801. function useSerialIds(mode) {
  1802. return (serialMode = pick(mode, serialMode));
  1803. }
  1804. function isFunction(obj) {
  1805. return typeof obj === 'function';
  1806. }
  1807. // Register Highcharts as a plugin in jQuery
  1808. if (win.jQuery) {
  1809. /**
  1810. * Highcharts-extended JQuery.
  1811. *
  1812. * @external JQuery
  1813. */
  1814. /**
  1815. * Helper function to return the chart of the current JQuery selector
  1816. * element.
  1817. *
  1818. * @function external:JQuery#highcharts
  1819. *
  1820. * @return {Highcharts.Chart}
  1821. * The chart that is linked to the JQuery selector element.
  1822. */ /**
  1823. * Factory function to create a chart in the current JQuery selector
  1824. * element.
  1825. *
  1826. * @function external:JQuery#highcharts
  1827. *
  1828. * @param {'Chart'|'Map'|'StockChart'|string} [className]
  1829. * Name of the factory class in the Highcharts namespace.
  1830. *
  1831. * @param {Highcharts.Options} [options]
  1832. * The chart options structure.
  1833. *
  1834. * @param {Highcharts.ChartCallbackFunction} [callback]
  1835. * Function to run when the chart has loaded and and all external
  1836. * images are loaded. Defining a
  1837. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  1838. * handler is equivalent.
  1839. *
  1840. * @return {JQuery}
  1841. * The current JQuery selector.
  1842. */
  1843. win.jQuery.fn.highcharts = function () {
  1844. const args = [].slice.call(arguments);
  1845. if (this[0]) { // this[0] is the renderTo div
  1846. // Create the chart
  1847. if (args[0]) {
  1848. new H[ // eslint-disable-line computed-property-spacing, no-new
  1849. // Constructor defaults to Chart
  1850. isString(args[0]) ? args.shift() : 'Chart'](this[0], args[0], args[1]);
  1851. return this;
  1852. }
  1853. // When called without parameters or with the return argument,
  1854. // return an existing chart
  1855. return charts[attr(this[0], 'data-highcharts-chart')];
  1856. }
  1857. };
  1858. }
  1859. /* *
  1860. *
  1861. * Default Export
  1862. *
  1863. * */
  1864. // TODO use named exports when supported.
  1865. const Utilities = {
  1866. addEvent,
  1867. arrayMax,
  1868. arrayMin,
  1869. attr,
  1870. clamp,
  1871. clearTimeout: internalClearTimeout,
  1872. correctFloat,
  1873. createElement,
  1874. css,
  1875. defined,
  1876. destroyObjectProperties,
  1877. diffObjects,
  1878. discardElement,
  1879. erase,
  1880. error,
  1881. extend,
  1882. extendClass,
  1883. find,
  1884. fireEvent,
  1885. getClosestDistance,
  1886. getMagnitude,
  1887. getNestedProperty,
  1888. getStyle,
  1889. inArray,
  1890. insertItem,
  1891. isArray,
  1892. isClass,
  1893. isDOMElement,
  1894. isFunction,
  1895. isNumber,
  1896. isObject,
  1897. isString,
  1898. keys,
  1899. merge,
  1900. normalizeTickInterval,
  1901. objectEach,
  1902. offset,
  1903. pad,
  1904. pick,
  1905. pInt,
  1906. pushUnique,
  1907. relativeLength,
  1908. removeEvent,
  1909. splat,
  1910. stableSort,
  1911. syncTimeout,
  1912. timeUnits,
  1913. uniqueKey,
  1914. useSerialIds,
  1915. wrap
  1916. };
  1917. /* *
  1918. *
  1919. * API Declarations
  1920. *
  1921. * */
  1922. /**
  1923. * An animation configuration. Animation configurations can also be defined as
  1924. * booleans, where `false` turns off animation and `true` defaults to a duration
  1925. * of 500ms and defer of 0ms.
  1926. *
  1927. * @interface Highcharts.AnimationOptionsObject
  1928. */ /**
  1929. * A callback function to exectute when the animation finishes.
  1930. * @name Highcharts.AnimationOptionsObject#complete
  1931. * @type {Function|undefined}
  1932. */ /**
  1933. * The animation defer in milliseconds.
  1934. * @name Highcharts.AnimationOptionsObject#defer
  1935. * @type {number|undefined}
  1936. */ /**
  1937. * The animation duration in milliseconds.
  1938. * @name Highcharts.AnimationOptionsObject#duration
  1939. * @type {number|undefined}
  1940. */ /**
  1941. * The name of an easing function as defined on the `Math` object.
  1942. * @name Highcharts.AnimationOptionsObject#easing
  1943. * @type {string|Function|undefined}
  1944. */ /**
  1945. * A callback function to execute on each step of each attribute or CSS property
  1946. * that's being animated. The first argument contains information about the
  1947. * animation and progress.
  1948. * @name Highcharts.AnimationOptionsObject#step
  1949. * @type {Function|undefined}
  1950. */
  1951. /**
  1952. * Creates a frame for the animated SVG element.
  1953. *
  1954. * @callback Highcharts.AnimationStepCallbackFunction
  1955. *
  1956. * @param {Highcharts.SVGElement} this
  1957. * The SVG element to animate.
  1958. *
  1959. * @return {void}
  1960. */
  1961. /**
  1962. * Interface description for a class.
  1963. *
  1964. * @interface Highcharts.Class<T>
  1965. * @extends Function
  1966. */ /**
  1967. * Class costructor.
  1968. * @function Highcharts.Class<T>#new
  1969. * @param {...Array<*>} args
  1970. * Constructor arguments.
  1971. * @return {T}
  1972. * Class instance.
  1973. */
  1974. /**
  1975. * A style object with camel case property names to define visual appearance of
  1976. * a SVG element or HTML element. The properties can be whatever styles are
  1977. * supported on the given SVG or HTML element.
  1978. *
  1979. * @example
  1980. * {
  1981. * fontFamily: 'monospace',
  1982. * fontSize: '1.2em'
  1983. * }
  1984. *
  1985. * @interface Highcharts.CSSObject
  1986. */ /**
  1987. * @name Highcharts.CSSObject#[key:string]
  1988. * @type {boolean|number|string|undefined}
  1989. */ /**
  1990. * Background style for the element.
  1991. * @name Highcharts.CSSObject#background
  1992. * @type {string|undefined}
  1993. */ /**
  1994. * Background color of the element.
  1995. * @name Highcharts.CSSObject#backgroundColor
  1996. * @type {Highcharts.ColorString|undefined}
  1997. */ /**
  1998. * Border style for the element.
  1999. * @name Highcharts.CSSObject#border
  2000. * @type {string|undefined}
  2001. */ /**
  2002. * Radius of the element border.
  2003. * @name Highcharts.CSSObject#borderRadius
  2004. * @type {number|undefined}
  2005. */ /**
  2006. * Color used in the element. The 'contrast' option is a Highcharts custom
  2007. * property that results in black or white, depending on the background of the
  2008. * element.
  2009. * @name Highcharts.CSSObject#color
  2010. * @type {'contrast'|Highcharts.ColorString|undefined}
  2011. */ /**
  2012. * Style of the mouse cursor when resting over the element.
  2013. * @name Highcharts.CSSObject#cursor
  2014. * @type {Highcharts.CursorValue|undefined}
  2015. */ /**
  2016. * Font family of the element text. Multiple values have to be in decreasing
  2017. * preference order and separated by comma.
  2018. * @name Highcharts.CSSObject#fontFamily
  2019. * @type {string|undefined}
  2020. */ /**
  2021. * Font size of the element text.
  2022. * @name Highcharts.CSSObject#fontSize
  2023. * @type {string|undefined}
  2024. */ /**
  2025. * Font weight of the element text.
  2026. * @name Highcharts.CSSObject#fontWeight
  2027. * @type {string|undefined}
  2028. */ /**
  2029. * Height of the element.
  2030. * @name Highcharts.CSSObject#height
  2031. * @type {number|undefined}
  2032. */ /**
  2033. * Width of the element border.
  2034. * @name Highcharts.CSSObject#lineWidth
  2035. * @type {number|undefined}
  2036. */ /**
  2037. * Opacity of the element.
  2038. * @name Highcharts.CSSObject#opacity
  2039. * @type {number|undefined}
  2040. */ /**
  2041. * Space around the element content.
  2042. * @name Highcharts.CSSObject#padding
  2043. * @type {string|undefined}
  2044. */ /**
  2045. * Behaviour of the element when the mouse cursor rests over it.
  2046. * @name Highcharts.CSSObject#pointerEvents
  2047. * @type {string|undefined}
  2048. */ /**
  2049. * Positioning of the element.
  2050. * @name Highcharts.CSSObject#position
  2051. * @type {string|undefined}
  2052. */ /**
  2053. * Alignment of the element text.
  2054. * @name Highcharts.CSSObject#textAlign
  2055. * @type {string|undefined}
  2056. */ /**
  2057. * Additional decoration of the element text.
  2058. * @name Highcharts.CSSObject#textDecoration
  2059. * @type {string|undefined}
  2060. */ /**
  2061. * Outline style of the element text.
  2062. * @name Highcharts.CSSObject#textOutline
  2063. * @type {string|undefined}
  2064. */ /**
  2065. * Line break style of the element text. Highcharts SVG elements support
  2066. * `ellipsis` when a `width` is set.
  2067. * @name Highcharts.CSSObject#textOverflow
  2068. * @type {string|undefined}
  2069. */ /**
  2070. * Top spacing of the element relative to the parent element.
  2071. * @name Highcharts.CSSObject#top
  2072. * @type {string|undefined}
  2073. */ /**
  2074. * Animated transition of selected element properties.
  2075. * @name Highcharts.CSSObject#transition
  2076. * @type {string|undefined}
  2077. */ /**
  2078. * Line break style of the element text.
  2079. * @name Highcharts.CSSObject#whiteSpace
  2080. * @type {string|undefined}
  2081. */ /**
  2082. * Width of the element.
  2083. * @name Highcharts.CSSObject#width
  2084. * @type {number|undefined}
  2085. */
  2086. /**
  2087. * All possible cursor styles.
  2088. *
  2089. * @typedef {'alias'|'all-scroll'|'auto'|'cell'|'col-resize'|'context-menu'|'copy'|'crosshair'|'default'|'e-resize'|'ew-resize'|'grab'|'grabbing'|'help'|'move'|'n-resize'|'ne-resize'|'nesw-resize'|'no-drop'|'none'|'not-allowed'|'ns-resize'|'nw-resize'|'nwse-resize'|'pointer'|'progress'|'row-resize'|'s-resize'|'se-resize'|'sw-resize'|'text'|'vertical-text'|'w-resize'|'wait'|'zoom-in'|'zoom-out'} Highcharts.CursorValue
  2090. */
  2091. /**
  2092. * All possible dash styles.
  2093. *
  2094. * @typedef {'Dash'|'DashDot'|'Dot'|'LongDash'|'LongDashDot'|'LongDashDotDot'|'ShortDash'|'ShortDashDot'|'ShortDashDotDot'|'ShortDot'|'Solid'} Highcharts.DashStyleValue
  2095. */
  2096. /**
  2097. * Generic dictionary in TypeScript notation.
  2098. * Use the native `AnyRecord` instead.
  2099. *
  2100. * @deprecated
  2101. * @interface Highcharts.Dictionary<T>
  2102. */ /**
  2103. * @name Highcharts.Dictionary<T>#[key:string]
  2104. * @type {T}
  2105. */
  2106. /**
  2107. * The function callback to execute when the event is fired. The `this` context
  2108. * contains the instance, that fired the event.
  2109. *
  2110. * @callback Highcharts.EventCallbackFunction<T>
  2111. *
  2112. * @param {T} this
  2113. *
  2114. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  2115. * Event arguments.
  2116. *
  2117. * @return {boolean|void}
  2118. */
  2119. /**
  2120. * The event options for adding function callback.
  2121. *
  2122. * @interface Highcharts.EventOptionsObject
  2123. */ /**
  2124. * The order the event handler should be called. This opens for having one
  2125. * handler be called before another, independent of in which order they were
  2126. * added.
  2127. * @name Highcharts.EventOptionsObject#order
  2128. * @type {number}
  2129. */ /**
  2130. * Whether an event should be passive or not.
  2131. * When set to `true`, the function specified by listener will never call
  2132. * `preventDefault()`.
  2133. * @name Highcharts.EventOptionsObject#passive
  2134. * @type boolean
  2135. */
  2136. /**
  2137. * Formats data as a string. Usually the data is accessible throught the `this`
  2138. * keyword.
  2139. *
  2140. * @callback Highcharts.FormatterCallbackFunction<T>
  2141. *
  2142. * @param {T} this
  2143. * Context to format
  2144. *
  2145. * @return {string}
  2146. * Formatted text
  2147. */
  2148. /**
  2149. * An object of key-value pairs for HTML attributes.
  2150. *
  2151. * @typedef {Highcharts.Dictionary<boolean|number|string|Function>} Highcharts.HTMLAttributes
  2152. */
  2153. /**
  2154. * An HTML DOM element. The type is a reference to the regular HTMLElement in
  2155. * the global scope.
  2156. *
  2157. * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
  2158. *
  2159. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
  2160. */
  2161. /**
  2162. * The iterator callback.
  2163. *
  2164. * @callback Highcharts.ObjectEachCallbackFunction<T>
  2165. *
  2166. * @param {T} this
  2167. * The context.
  2168. *
  2169. * @param {*} value
  2170. * The property value.
  2171. *
  2172. * @param {string} key
  2173. * The property key.
  2174. *
  2175. * @param {*} obj
  2176. * The object that objectEach is being applied to.
  2177. */
  2178. /**
  2179. * An object containing `left` and `top` properties for the position in the
  2180. * page.
  2181. *
  2182. * @interface Highcharts.OffsetObject
  2183. */ /**
  2184. * Left distance to the page border.
  2185. * @name Highcharts.OffsetObject#left
  2186. * @type {number}
  2187. */ /**
  2188. * Top distance to the page border.
  2189. * @name Highcharts.OffsetObject#top
  2190. * @type {number}
  2191. */
  2192. /**
  2193. * Describes a range.
  2194. *
  2195. * @interface Highcharts.RangeObject
  2196. */ /**
  2197. * Maximum number of the range.
  2198. * @name Highcharts.RangeObject#max
  2199. * @type {number}
  2200. */ /**
  2201. * Minimum number of the range.
  2202. * @name Highcharts.RangeObject#min
  2203. * @type {number}
  2204. */
  2205. /**
  2206. * If a number is given, it defines the pixel length. If a percentage string is
  2207. * given, like for example `'50%'`, the setting defines a length relative to a
  2208. * base size, for example the size of a container.
  2209. *
  2210. * @typedef {number|string} Highcharts.RelativeSize
  2211. */
  2212. /**
  2213. * Proceed function to call original (wrapped) function.
  2214. *
  2215. * @callback Highcharts.WrapProceedFunction
  2216. *
  2217. * @param {*} [arg1]
  2218. * Optional argument. Without any arguments defaults to first argument of
  2219. * the wrapping function.
  2220. *
  2221. * @param {*} [arg2]
  2222. * Optional argument. Without any arguments defaults to second argument
  2223. * of the wrapping function.
  2224. *
  2225. * @param {*} [arg3]
  2226. * Optional argument. Without any arguments defaults to third argument of
  2227. * the wrapping function.
  2228. *
  2229. * @return {*}
  2230. * Return value of the original function.
  2231. */
  2232. /**
  2233. * The Highcharts object is the placeholder for all other members, and various
  2234. * utility functions. The most important member of the namespace would be the
  2235. * chart constructor.
  2236. *
  2237. * @example
  2238. * let chart = Highcharts.chart('container', { ... });
  2239. *
  2240. * @namespace Highcharts
  2241. */
  2242. ''; // detach doclets above
  2243. return Utilities;
  2244. });
  2245. _registerModule(_modules, 'Core/Chart/ChartDefaults.js', [], function () {
  2246. /* *
  2247. *
  2248. * (c) 2010-2021 Torstein Honsi
  2249. *
  2250. * License: www.highcharts.com/license
  2251. *
  2252. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2253. *
  2254. * */
  2255. /* *
  2256. *
  2257. * API Options
  2258. *
  2259. * */
  2260. /**
  2261. * General options for the chart.
  2262. *
  2263. * @optionparent chart
  2264. */
  2265. const ChartDefaults = {
  2266. /**
  2267. * Default `mapData` for all series, in terms of a GeoJSON or TopoJSON
  2268. * object. If set to a string, it functions as an index into the
  2269. * `Highcharts.maps` array.
  2270. *
  2271. * For picking out individual shapes and geometries to use for each series
  2272. * of the map, see [series.mapData](#series.map.mapData).
  2273. *
  2274. * @sample maps/demo/geojson
  2275. * Loading GeoJSON data
  2276. * @sample maps/chart/topojson
  2277. * Loading TopoJSON data
  2278. *
  2279. * @type {string|Array<*>|Highcharts.GeoJSON|Highcharts.TopoJSON}
  2280. * @since 5.0.0
  2281. * @product highmaps
  2282. * @apioption chart.map
  2283. */
  2284. /**
  2285. * Set lat/lon transformation definitions for the chart. If not defined,
  2286. * these are extracted from the map data.
  2287. *
  2288. * @type {*}
  2289. * @since 5.0.0
  2290. * @product highmaps
  2291. * @apioption chart.mapTransforms
  2292. */
  2293. /**
  2294. * When using multiple axes, the ticks of two or more opposite axes
  2295. * will automatically be aligned by adding ticks to the axis or axes
  2296. * with the least ticks, as if `tickAmount` were specified.
  2297. *
  2298. * This can be prevented by setting `alignTicks` to false. If the grid
  2299. * lines look messy, it's a good idea to hide them for the secondary
  2300. * axis by setting `gridLineWidth` to 0.
  2301. *
  2302. * If `startOnTick` or `endOnTick` in the axis options are set to false,
  2303. * then the `alignTicks ` will be disabled for the axis.
  2304. *
  2305. * Disabled for logarithmic axes.
  2306. *
  2307. * @sample {highcharts} highcharts/chart/alignticks-true/
  2308. * True by default
  2309. * @sample {highcharts} highcharts/chart/alignticks-false/
  2310. * False
  2311. * @sample {highstock} stock/chart/alignticks-true/
  2312. * True by default
  2313. * @sample {highstock} stock/chart/alignticks-false/
  2314. * False
  2315. *
  2316. * @type {boolean}
  2317. * @default true
  2318. * @product highcharts highstock gantt
  2319. * @apioption chart.alignTicks
  2320. */
  2321. /**
  2322. * When using multiple axes, align the thresholds. When this is true, other
  2323. * ticks will also be aligned.
  2324. *
  2325. * Note that for line series and some other series types, the `threshold`
  2326. * option is set to `null` by default. This will in turn cause their y-axis
  2327. * to not have a threshold. In order to avoid that, set the series
  2328. * `threshold` to 0 or another number.
  2329. *
  2330. * If `startOnTick` or `endOnTick` in the axis options are set to false, or
  2331. * if the axis is logarithmic, the threshold will not be aligned.
  2332. *
  2333. * @sample {highcharts} highcharts/chart/alignthresholds/ Set to true
  2334. *
  2335. * @since 10.0.0
  2336. * @product highcharts highstock gantt
  2337. * @apioption chart.alignThresholds
  2338. */
  2339. alignThresholds: false,
  2340. /**
  2341. * Set the overall animation for all chart updating. Animation can be
  2342. * disabled throughout the chart by setting it to false here. It can
  2343. * be overridden for each individual API method as a function parameter.
  2344. * The only animation not affected by this option is the initial series
  2345. * animation, see [plotOptions.series.animation](
  2346. * #plotOptions.series.animation).
  2347. *
  2348. * The animation can either be set as a boolean or a configuration
  2349. * object. If `true`, it will use the 'swing' jQuery easing and a
  2350. * duration of 500 ms. If used as a configuration object, the following
  2351. * properties are supported:
  2352. *
  2353. * - `defer`: The animation delay time in milliseconds.
  2354. *
  2355. * - `duration`: The duration of the animation in milliseconds.
  2356. *
  2357. * - `easing`: A string reference to an easing function set on the
  2358. * `Math` object. See
  2359. * [the easing demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
  2360. *
  2361. * When zooming on a series with less than 100 points, the chart redraw
  2362. * will be done with animation, but in case of more data points, it is
  2363. * necessary to set this option to ensure animation on zoom.
  2364. *
  2365. * @sample {highcharts} highcharts/chart/animation-none/
  2366. * Updating with no animation
  2367. * @sample {highcharts} highcharts/chart/animation-duration/
  2368. * With a longer duration
  2369. * @sample {highcharts} highcharts/chart/animation-easing/
  2370. * With a jQuery UI easing
  2371. * @sample {highmaps} maps/chart/animation-none/
  2372. * Updating with no animation
  2373. * @sample {highmaps} maps/chart/animation-duration/
  2374. * With a longer duration
  2375. *
  2376. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  2377. * @default true
  2378. * @apioption chart.animation
  2379. */
  2380. /**
  2381. * A CSS class name to apply to the charts container `div`, allowing
  2382. * unique CSS styling for each chart.
  2383. *
  2384. * @type {string}
  2385. * @apioption chart.className
  2386. */
  2387. /**
  2388. * Event listeners for the chart.
  2389. *
  2390. * @apioption chart.events
  2391. */
  2392. /**
  2393. * Fires when a series is added to the chart after load time, using the
  2394. * `addSeries` method. One parameter, `event`, is passed to the
  2395. * function, containing common event information. Through
  2396. * `event.options` you can access the series options that were passed to
  2397. * the `addSeries` method. Returning false prevents the series from
  2398. * being added.
  2399. *
  2400. * @sample {highcharts} highcharts/chart/events-addseries/
  2401. * Alert on add series
  2402. * @sample {highstock} stock/chart/events-addseries/
  2403. * Alert on add series
  2404. *
  2405. * @type {Highcharts.ChartAddSeriesCallbackFunction}
  2406. * @since 1.2.0
  2407. * @context Highcharts.Chart
  2408. * @apioption chart.events.addSeries
  2409. */
  2410. /**
  2411. * Fires when clicking on the plot background. One parameter, `event`,
  2412. * is passed to the function, containing common event information.
  2413. *
  2414. * Information on the clicked spot can be found through `event.xAxis`
  2415. * and `event.yAxis`, which are arrays containing the axes of each
  2416. * dimension and each axis' value at the clicked spot. The primary axes
  2417. * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  2418. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  2419. *
  2420. * ```js
  2421. * click: function(e) {
  2422. * console.log(
  2423. * Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
  2424. * e.yAxis[0].value
  2425. * )
  2426. * }
  2427. * ```
  2428. *
  2429. * @sample {highcharts} highcharts/chart/events-click/
  2430. * Alert coordinates on click
  2431. * @sample {highcharts} highcharts/chart/events-container/
  2432. * Alternatively, attach event to container
  2433. * @sample {highstock} stock/chart/events-click/
  2434. * Alert coordinates on click
  2435. * @sample {highstock} highcharts/chart/events-container/
  2436. * Alternatively, attach event to container
  2437. * @sample {highmaps} maps/chart/events-click/
  2438. * Record coordinates on click
  2439. * @sample {highmaps} highcharts/chart/events-container/
  2440. * Alternatively, attach event to container
  2441. *
  2442. * @type {Highcharts.ChartClickCallbackFunction}
  2443. * @since 1.2.0
  2444. * @context Highcharts.Chart
  2445. * @apioption chart.events.click
  2446. */
  2447. /**
  2448. * Fires when the chart is finished loading. Since v4.2.2, it also waits
  2449. * for images to be loaded, for example from point markers. One
  2450. * parameter, `event`, is passed to the function, containing common
  2451. * event information.
  2452. *
  2453. * There is also a second parameter to the chart constructor where a
  2454. * callback function can be passed to be executed on chart.load.
  2455. *
  2456. * @sample {highcharts} highcharts/chart/events-load/
  2457. * Alert on chart load
  2458. * @sample {highcharts} highcharts/chart/events-render/
  2459. * Load vs Redraw vs Render
  2460. * @sample {highstock} stock/chart/events-load/
  2461. * Alert on chart load
  2462. * @sample {highmaps} maps/chart/events-load/
  2463. * Add series on chart load
  2464. *
  2465. * @type {Highcharts.ChartLoadCallbackFunction}
  2466. * @context Highcharts.Chart
  2467. * @apioption chart.events.load
  2468. */
  2469. /**
  2470. * Fires when the chart is redrawn, either after a call to
  2471. * `chart.redraw()` or after an axis, series or point is modified with
  2472. * the `redraw` option set to `true`. One parameter, `event`, is passed
  2473. * to the function, containing common event information.
  2474. *
  2475. * @sample {highcharts} highcharts/chart/events-redraw/
  2476. * Alert on chart redraw
  2477. * @sample {highcharts} highcharts/chart/events-render/
  2478. * Load vs Redraw vs Render
  2479. * @sample {highstock} stock/chart/events-redraw/
  2480. * Alert on chart redraw when adding a series or moving the
  2481. * zoomed range
  2482. * @sample {highmaps} maps/chart/events-redraw/
  2483. * Set subtitle on chart redraw
  2484. *
  2485. * @type {Highcharts.ChartRedrawCallbackFunction}
  2486. * @since 1.2.0
  2487. * @context Highcharts.Chart
  2488. * @apioption chart.events.redraw
  2489. */
  2490. /**
  2491. * Fires after initial load of the chart (directly after the `load`
  2492. * event), and after each redraw (directly after the `redraw` event).
  2493. *
  2494. * @sample {highcharts} highcharts/chart/events-render/
  2495. * Load vs Redraw vs Render
  2496. *
  2497. * @type {Highcharts.ChartRenderCallbackFunction}
  2498. * @since 5.0.7
  2499. * @context Highcharts.Chart
  2500. * @apioption chart.events.render
  2501. */
  2502. /**
  2503. * Fires when an area of the chart has been selected. Selection is
  2504. * enabled by setting the chart's zoomType. One parameter, `event`, is
  2505. * passed to the function, containing common event information. The
  2506. * default action for the selection event is to zoom the chart to the
  2507. * selected area. It can be prevented by calling
  2508. * `event.preventDefault()` or return false.
  2509. *
  2510. * Information on the selected area can be found through `event.xAxis`
  2511. * and `event.yAxis`, which are arrays containing the axes of each
  2512. * dimension and each axis' min and max values. The primary axes are
  2513. * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  2514. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  2515. *
  2516. * ```js
  2517. * selection: function(event) {
  2518. * // log the min and max of the primary, datetime x-axis
  2519. * console.log(
  2520. * Highcharts.dateFormat(
  2521. * '%Y-%m-%d %H:%M:%S',
  2522. * event.xAxis[0].min
  2523. * ),
  2524. * Highcharts.dateFormat(
  2525. * '%Y-%m-%d %H:%M:%S',
  2526. * event.xAxis[0].max
  2527. * )
  2528. * );
  2529. * // log the min and max of the y axis
  2530. * console.log(event.yAxis[0].min, event.yAxis[0].max);
  2531. * }
  2532. * ```
  2533. *
  2534. * @sample {highcharts} highcharts/chart/events-selection/
  2535. * Report on selection and reset
  2536. * @sample {highcharts} highcharts/chart/events-selection-points/
  2537. * Select a range of points through a drag selection
  2538. * @sample {highstock} stock/chart/events-selection/
  2539. * Report on selection and reset
  2540. * @sample {highstock} highcharts/chart/events-selection-points/
  2541. * Select a range of points through a drag selection
  2542. * (Highcharts)
  2543. *
  2544. * @type {Highcharts.ChartSelectionCallbackFunction}
  2545. * @apioption chart.events.selection
  2546. */
  2547. /**
  2548. * The margin between the outer edge of the chart and the plot area.
  2549. * The numbers in the array designate top, right, bottom and left
  2550. * respectively. Use the options `marginTop`, `marginRight`,
  2551. * `marginBottom` and `marginLeft` for shorthand setting of one option.
  2552. *
  2553. * By default there is no margin. The actual space is dynamically
  2554. * calculated from the offset of axis labels, axis title, title,
  2555. * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
  2556. * `spacingBottom` and `spacingLeft` options.
  2557. *
  2558. * @sample {highcharts} highcharts/chart/margins-zero/
  2559. * Zero margins
  2560. * @sample {highstock} stock/chart/margin-zero/
  2561. * Zero margins
  2562. *
  2563. * @type {number|Array<number>}
  2564. * @apioption chart.margin
  2565. */
  2566. /**
  2567. * The margin between the bottom outer edge of the chart and the plot
  2568. * area. Use this to set a fixed pixel value for the margin as opposed
  2569. * to the default dynamic margin. See also `spacingBottom`.
  2570. *
  2571. * @sample {highcharts} highcharts/chart/marginbottom/
  2572. * 100px bottom margin
  2573. * @sample {highstock} stock/chart/marginbottom/
  2574. * 100px bottom margin
  2575. * @sample {highmaps} maps/chart/margin/
  2576. * 100px margins
  2577. *
  2578. * @type {number}
  2579. * @since 2.0
  2580. * @apioption chart.marginBottom
  2581. */
  2582. /**
  2583. * The margin between the left outer edge of the chart and the plot
  2584. * area. Use this to set a fixed pixel value for the margin as opposed
  2585. * to the default dynamic margin. See also `spacingLeft`.
  2586. *
  2587. * @sample {highcharts} highcharts/chart/marginleft/
  2588. * 150px left margin
  2589. * @sample {highstock} stock/chart/marginleft/
  2590. * 150px left margin
  2591. * @sample {highmaps} maps/chart/margin/
  2592. * 100px margins
  2593. *
  2594. * @type {number}
  2595. * @since 2.0
  2596. * @apioption chart.marginLeft
  2597. */
  2598. /**
  2599. * The margin between the right outer edge of the chart and the plot
  2600. * area. Use this to set a fixed pixel value for the margin as opposed
  2601. * to the default dynamic margin. See also `spacingRight`.
  2602. *
  2603. * @sample {highcharts} highcharts/chart/marginright/
  2604. * 100px right margin
  2605. * @sample {highstock} stock/chart/marginright/
  2606. * 100px right margin
  2607. * @sample {highmaps} maps/chart/margin/
  2608. * 100px margins
  2609. *
  2610. * @type {number}
  2611. * @since 2.0
  2612. * @apioption chart.marginRight
  2613. */
  2614. /**
  2615. * The margin between the top outer edge of the chart and the plot area.
  2616. * Use this to set a fixed pixel value for the margin as opposed to
  2617. * the default dynamic margin. See also `spacingTop`.
  2618. *
  2619. * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
  2620. * @sample {highstock} stock/chart/margintop/
  2621. * 100px top margin
  2622. * @sample {highmaps} maps/chart/margin/
  2623. * 100px margins
  2624. *
  2625. * @type {number}
  2626. * @since 2.0
  2627. * @apioption chart.marginTop
  2628. */
  2629. /**
  2630. * Callback function to override the default function that formats all
  2631. * the numbers in the chart. Returns a string with the formatted number.
  2632. *
  2633. * @sample highcharts/members/highcharts-numberformat
  2634. * Arabic digits in Highcharts
  2635. * @type {Highcharts.NumberFormatterCallbackFunction}
  2636. * @since 8.0.0
  2637. * @apioption chart.numberFormatter
  2638. */
  2639. /**
  2640. * Allows setting a key to switch between zooming and panning. Can be
  2641. * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
  2642. * key on Windows) or `shift`. The keys are mapped directly to the key
  2643. * properties of the click event argument (`event.altKey`,
  2644. * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
  2645. *
  2646. * @type {string}
  2647. * @since 4.0.3
  2648. * @product highcharts gantt
  2649. * @validvalue ["alt", "ctrl", "meta", "shift"]
  2650. * @apioption chart.panKey
  2651. */
  2652. /**
  2653. * Allow panning in a chart. Best used with [panKey](#chart.panKey)
  2654. * to combine zooming and panning.
  2655. *
  2656. * On touch devices, when the [tooltip.followTouchMove](
  2657. * #tooltip.followTouchMove) option is `true` (default), panning
  2658. * requires two fingers. To allow panning with one finger, set
  2659. * `followTouchMove` to `false`.
  2660. *
  2661. * @sample {highcharts} highcharts/chart/pankey/ Zooming and panning
  2662. * @sample {highstock} stock/chart/panning/ Zooming and xy panning
  2663. */
  2664. panning: {
  2665. /**
  2666. * Enable or disable chart panning.
  2667. *
  2668. * @type {boolean}
  2669. * @default {highcharts} false
  2670. * @default {highstock|highmaps} true
  2671. */
  2672. enabled: false,
  2673. /**
  2674. * Decides in what dimensions the user can pan the chart. Can be
  2675. * one of `x`, `y`, or `xy`.
  2676. *
  2677. * When this option is set to `y` or `xy`, [yAxis.startOnTick](#yAxis.startOnTick)
  2678. * and [yAxis.endOnTick](#yAxis.endOnTick) are overwritten to `false`.
  2679. *
  2680. * @sample {highcharts} highcharts/chart/panning-type
  2681. * Zooming and xy panning
  2682. *
  2683. * @declare Highcharts.OptionsChartPanningTypeValue
  2684. * @type {string}
  2685. * @validvalue ["x", "y", "xy"]
  2686. * @default {highcharts|highstock} x
  2687. * @product highcharts highstock gantt
  2688. */
  2689. type: 'x'
  2690. },
  2691. /**
  2692. * Equivalent to [zoomType](#chart.zoomType), but for multitouch
  2693. * gestures only. By default, the `pinchType` is the same as the
  2694. * `zoomType` setting. However, pinching can be enabled separately in
  2695. * some cases, for example in stock charts where a mouse drag pans the
  2696. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  2697. * #tooltip.followTouchMove) is true, pinchType only applies to
  2698. * two-finger touches.
  2699. *
  2700. * @type {string}
  2701. * @default {highcharts} undefined
  2702. * @default {highstock} undefined
  2703. * @since 3.0
  2704. * @product highcharts highstock gantt
  2705. * @deprecated
  2706. * @validvalue ["x", "y", "xy"]
  2707. * @apioption chart.pinchType
  2708. */
  2709. /**
  2710. * Whether to apply styled mode. When in styled mode, no presentational
  2711. * attributes or CSS are applied to the chart SVG. Instead, CSS rules
  2712. * are required to style the chart. The default style sheet is
  2713. * available from `https://code.highcharts.com/css/highcharts.css`.
  2714. *
  2715. * [Read more in the docs](https://www.highcharts.com/docs/chart-design-and-style/style-by-css)
  2716. * on what classes and variables are available.
  2717. *
  2718. * @sample highcharts/css/colors
  2719. * Color theming with CSS
  2720. * @sample highcharts/css/prefers-color-scheme
  2721. * Dynamic theme based on system settings
  2722. * @type {boolean}
  2723. * @default false
  2724. * @since 7.0
  2725. * @apioption chart.styledMode
  2726. */
  2727. styledMode: false,
  2728. /**
  2729. * The corner radius of the outer chart border.
  2730. *
  2731. * @sample {highcharts} highcharts/chart/borderradius/
  2732. * 20px radius
  2733. * @sample {highstock} stock/chart/border/
  2734. * 10px radius
  2735. * @sample {highmaps} maps/chart/border/
  2736. * Border options
  2737. *
  2738. */
  2739. borderRadius: 0,
  2740. /**
  2741. * In styled mode, this sets how many colors the class names
  2742. * should rotate between. With ten colors, series (or points) are
  2743. * given class names like `highcharts-color-0`, `highcharts-color-1`
  2744. * [...] `highcharts-color-9`. The equivalent in non-styled mode
  2745. * is to set colors using the [colors](#colors) setting.
  2746. *
  2747. * @since 5.0.0
  2748. */
  2749. colorCount: 10,
  2750. /**
  2751. * By default, (because of memory and performance reasons) the chart does
  2752. * not copy the data but keeps it as a reference. In some cases, this might
  2753. * result in mutating the original data source. In order to prevent that,
  2754. * set that property to false. Please note that changing that might decrease
  2755. * performance, especially with bigger sets of data.
  2756. *
  2757. * @type {boolean}
  2758. * @since 10.1.0
  2759. */
  2760. allowMutatingData: true,
  2761. /**
  2762. * If true, the axes will scale to the remaining visible series once
  2763. * one series is hidden. If false, hiding and showing a series will
  2764. * not affect the axes or the other series. For stacks, once one series
  2765. * within the stack is hidden, the rest of the stack will close in
  2766. * around it even if the axis is not affected.
  2767. *
  2768. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
  2769. * True by default
  2770. * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
  2771. * False
  2772. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
  2773. * True with stack
  2774. * @sample {highstock} stock/chart/ignorehiddenseries-true/
  2775. * True by default
  2776. * @sample {highstock} stock/chart/ignorehiddenseries-false/
  2777. * False
  2778. *
  2779. * @since 1.2.0
  2780. * @product highcharts highstock gantt
  2781. */
  2782. ignoreHiddenSeries: true,
  2783. /**
  2784. * Whether to invert the axes so that the x axis is vertical and y axis
  2785. * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
  2786. * by default.
  2787. *
  2788. * @productdesc {highcharts}
  2789. * If a bar series is present in the chart, it will be inverted
  2790. * automatically. Inverting the chart doesn't have an effect if there
  2791. * are no cartesian series in the chart, or if the chart is
  2792. * [polar](#chart.polar).
  2793. *
  2794. * @sample {highcharts} highcharts/chart/inverted/
  2795. * Inverted line
  2796. * @sample {highstock} stock/navigator/inverted/
  2797. * Inverted stock chart
  2798. *
  2799. * @type {boolean}
  2800. * @default false
  2801. * @product highcharts highstock gantt
  2802. * @apioption chart.inverted
  2803. */
  2804. /**
  2805. * The distance between the outer edge of the chart and the content,
  2806. * like title or legend, or axis title and labels if present. The
  2807. * numbers in the array designate top, right, bottom and left
  2808. * respectively. Use the options spacingTop, spacingRight, spacingBottom
  2809. * and spacingLeft options for shorthand setting of one option.
  2810. *
  2811. * @type {Array<number>}
  2812. * @see [chart.margin](#chart.margin)
  2813. * @default [10, 10, 15, 10]
  2814. * @since 3.0.6
  2815. */
  2816. spacing: [10, 10, 15, 10],
  2817. /**
  2818. * The button that appears after a selection zoom, allowing the user
  2819. * to reset zoom.
  2820. *
  2821. * @since 2.2
  2822. * @deprecated 10.2.1
  2823. */
  2824. resetZoomButton: {
  2825. /**
  2826. * What frame the button placement should be related to. Can be
  2827. * either `plotBox` or `spacingBox`.
  2828. *
  2829. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  2830. * Relative to the chart
  2831. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  2832. * Relative to the chart
  2833. *
  2834. * @type {Highcharts.ButtonRelativeToValue}
  2835. * @default plot
  2836. * @apioption chart.resetZoomButton.relativeTo
  2837. */
  2838. /**
  2839. * A collection of attributes for the button. The object takes SVG
  2840. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  2841. * border radius. The theme also supports `style`, a collection of
  2842. * CSS properties for the text. Equivalent attributes for the hover
  2843. * state are given in `theme.states.hover`.
  2844. *
  2845. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  2846. * Theming the button
  2847. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  2848. * Theming the button
  2849. *
  2850. * @type {Highcharts.SVGAttributes}
  2851. */
  2852. theme: {
  2853. /**
  2854. * @internal
  2855. */
  2856. zIndex: 6
  2857. },
  2858. /**
  2859. * The position of the button.
  2860. *
  2861. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  2862. * Above the plot area
  2863. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  2864. * Above the plot area
  2865. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  2866. * Above the plot area
  2867. *
  2868. * @type {Highcharts.AlignObject}
  2869. */
  2870. position: {
  2871. /**
  2872. * The horizontal alignment of the button.
  2873. */
  2874. align: 'right',
  2875. /**
  2876. * The horizontal offset of the button.
  2877. */
  2878. x: -10,
  2879. /**
  2880. * The vertical alignment of the button.
  2881. *
  2882. * @type {Highcharts.VerticalAlignValue}
  2883. * @default top
  2884. * @apioption chart.resetZoomButton.position.verticalAlign
  2885. */
  2886. /**
  2887. * The vertical offset of the button.
  2888. */
  2889. y: 10
  2890. }
  2891. },
  2892. /**
  2893. * The pixel width of the plot area border.
  2894. *
  2895. * @sample {highcharts} highcharts/chart/plotborderwidth/
  2896. * 1px border
  2897. * @sample {highstock} stock/chart/plotborder/
  2898. * 2px border
  2899. * @sample {highmaps} maps/chart/plotborder/
  2900. * Plot border options
  2901. *
  2902. * @type {number}
  2903. * @default 0
  2904. * @apioption chart.plotBorderWidth
  2905. */
  2906. /**
  2907. * Whether to apply a drop shadow to the plot area. Requires that
  2908. * plotBackgroundColor be set. The shadow can be an object configuration
  2909. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  2910. *
  2911. * @sample {highcharts} highcharts/chart/plotshadow/
  2912. * Plot shadow
  2913. * @sample {highstock} stock/chart/plotshadow/
  2914. * Plot shadow
  2915. * @sample {highmaps} maps/chart/plotborder/
  2916. * Plot border options
  2917. *
  2918. * @type {boolean|Highcharts.ShadowOptionsObject}
  2919. * @default false
  2920. * @apioption chart.plotShadow
  2921. */
  2922. /**
  2923. * When true, cartesian charts like line, spline, area and column are
  2924. * transformed into the polar coordinate system. This produces _polar
  2925. * charts_, also known as _radar charts_.
  2926. *
  2927. * @sample {highcharts} highcharts/demo/polar/
  2928. * Polar chart
  2929. * @sample {highcharts} highcharts/demo/polar-wind-rose/
  2930. * Wind rose, stacked polar column chart
  2931. * @sample {highcharts} highcharts/demo/polar-spider/
  2932. * Spider web chart
  2933. * @sample {highcharts} highcharts/parallel-coordinates/polar/
  2934. * Star plot, multivariate data in a polar chart
  2935. *
  2936. * @type {boolean}
  2937. * @default false
  2938. * @since 2.3.0
  2939. * @product highcharts
  2940. * @requires highcharts-more
  2941. * @apioption chart.polar
  2942. */
  2943. /**
  2944. * Whether to reflow the chart to fit the width of the container div
  2945. * on resizing the window.
  2946. *
  2947. * @sample {highcharts} highcharts/chart/reflow-true/
  2948. * True by default
  2949. * @sample {highcharts} highcharts/chart/reflow-false/
  2950. * False
  2951. * @sample {highstock} stock/chart/reflow-true/
  2952. * True by default
  2953. * @sample {highstock} stock/chart/reflow-false/
  2954. * False
  2955. * @sample {highmaps} maps/chart/reflow-true/
  2956. * True by default
  2957. * @sample {highmaps} maps/chart/reflow-false/
  2958. * False
  2959. *
  2960. * @since 2.1
  2961. */
  2962. reflow: true,
  2963. /**
  2964. * The HTML element where the chart will be rendered. If it is a string,
  2965. * the element by that id is used. The HTML element can also be passed
  2966. * by direct reference, or as the first argument of the chart
  2967. * constructor, in which case the option is not needed.
  2968. *
  2969. * @sample {highcharts} highcharts/chart/reflow-true/
  2970. * String
  2971. * @sample {highcharts} highcharts/chart/renderto-object/
  2972. * Object reference
  2973. * @sample {highstock} stock/chart/renderto-string/
  2974. * String
  2975. * @sample {highstock} stock/chart/renderto-object/
  2976. * Object reference
  2977. *
  2978. * @type {string|Highcharts.HTMLDOMElement}
  2979. * @apioption chart.renderTo
  2980. */
  2981. /**
  2982. * The background color of the marker square when selecting (zooming
  2983. * in on) an area of the chart.
  2984. *
  2985. * @see In styled mode, the selection marker fill is set with the
  2986. * `.highcharts-selection-marker` class.
  2987. *
  2988. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  2989. * @default rgba(51,92,173,0.25)
  2990. * @since 2.1.7
  2991. * @apioption chart.selectionMarkerFill
  2992. */
  2993. /**
  2994. * Whether to apply a drop shadow to the global series group. This causes
  2995. * all the series to have the same shadow. Contrary to the `series.shadow`
  2996. * option, this prevents items from casting shadows on each other, like for
  2997. * others series in a stack. The shadow can be an object configuration
  2998. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  2999. *
  3000. * @sample highcharts/chart/seriesgroupshadow/ Shadow
  3001. *
  3002. * @type {boolean|Highcharts.ShadowOptionsObject}
  3003. * @default false
  3004. * @apioption chart.shadow
  3005. */
  3006. /**
  3007. * Whether to apply a drop shadow to the outer chart area. Requires
  3008. * that backgroundColor be set. The shadow can be an object
  3009. * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
  3010. * `width`.
  3011. *
  3012. * @sample {highcharts} highcharts/chart/shadow/
  3013. * Shadow
  3014. * @sample {highstock} stock/chart/shadow/
  3015. * Shadow
  3016. * @sample {highmaps} maps/chart/border/
  3017. * Chart border and shadow
  3018. *
  3019. * @type {boolean|Highcharts.ShadowOptionsObject}
  3020. * @default false
  3021. * @apioption chart.shadow
  3022. */
  3023. /**
  3024. * Whether to show the axes initially. This only applies to empty charts
  3025. * where series are added dynamically, as axes are automatically added
  3026. * to cartesian series.
  3027. *
  3028. * @sample {highcharts} highcharts/chart/showaxes-false/
  3029. * False by default
  3030. * @sample {highcharts} highcharts/chart/showaxes-true/
  3031. * True
  3032. *
  3033. * @type {boolean}
  3034. * @since 1.2.5
  3035. * @product highcharts gantt
  3036. * @apioption chart.showAxes
  3037. */
  3038. /**
  3039. * The space between the bottom edge of the chart and the content (plot
  3040. * area, axis title and labels, title, subtitle or legend in top
  3041. * position).
  3042. *
  3043. * @sample {highcharts} highcharts/chart/spacingbottom/
  3044. * Spacing bottom set to 100
  3045. * @sample {highstock} stock/chart/spacingbottom/
  3046. * Spacing bottom set to 100
  3047. * @sample {highmaps} maps/chart/spacing/
  3048. * Spacing 100 all around
  3049. *
  3050. * @type {number}
  3051. * @default 15
  3052. * @since 2.1
  3053. * @apioption chart.spacingBottom
  3054. */
  3055. /**
  3056. * The space between the left edge of the chart and the content (plot
  3057. * area, axis title and labels, title, subtitle or legend in top
  3058. * position).
  3059. *
  3060. * @sample {highcharts} highcharts/chart/spacingleft/
  3061. * Spacing left set to 100
  3062. * @sample {highstock} stock/chart/spacingleft/
  3063. * Spacing left set to 100
  3064. * @sample {highmaps} maps/chart/spacing/
  3065. * Spacing 100 all around
  3066. *
  3067. * @type {number}
  3068. * @default 10
  3069. * @since 2.1
  3070. * @apioption chart.spacingLeft
  3071. */
  3072. /**
  3073. * The space between the right edge of the chart and the content (plot
  3074. * area, axis title and labels, title, subtitle or legend in top
  3075. * position).
  3076. *
  3077. * @sample {highcharts} highcharts/chart/spacingright-100/
  3078. * Spacing set to 100
  3079. * @sample {highcharts} highcharts/chart/spacingright-legend/
  3080. * Legend in right position with default spacing
  3081. * @sample {highstock} stock/chart/spacingright/
  3082. * Spacing set to 100
  3083. * @sample {highmaps} maps/chart/spacing/
  3084. * Spacing 100 all around
  3085. *
  3086. * @type {number}
  3087. * @default 10
  3088. * @since 2.1
  3089. * @apioption chart.spacingRight
  3090. */
  3091. /**
  3092. * The space between the top edge of the chart and the content (plot
  3093. * area, axis title and labels, title, subtitle or legend in top
  3094. * position).
  3095. *
  3096. * @sample {highcharts} highcharts/chart/spacingtop-100/
  3097. * A top spacing of 100
  3098. * @sample {highcharts} highcharts/chart/spacingtop-10/
  3099. * Floating chart title makes the plot area align to the default
  3100. * spacingTop of 10.
  3101. * @sample {highstock} stock/chart/spacingtop/
  3102. * A top spacing of 100
  3103. * @sample {highmaps} maps/chart/spacing/
  3104. * Spacing 100 all around
  3105. *
  3106. * @type {number}
  3107. * @default 10
  3108. * @since 2.1
  3109. * @apioption chart.spacingTop
  3110. */
  3111. /**
  3112. * Additional CSS styles to apply inline to the container `div` and the root
  3113. * SVG.
  3114. *
  3115. * Since v11, the root font size is 1rem by default, and all child element
  3116. * are given a relative `em` font size by default. This allows implementers
  3117. * to control all the chart's font sizes by only setting the root level.
  3118. *
  3119. * @see In styled mode, general chart styles can be set with the
  3120. * `.highcharts-root` class.
  3121. * @sample {highcharts} highcharts/chart/style-serif-font/
  3122. * Using a serif type font
  3123. * @sample {highcharts} highcharts/members/relative-font-size/
  3124. * Relative font sizes
  3125. * @sample {highcharts} highcharts/css/em/
  3126. * Styled mode with relative font sizes
  3127. * @sample {highstock} stock/chart/style/
  3128. * Using a serif type font
  3129. * @sample {highmaps} maps/chart/style-serif-font/
  3130. * Using a serif type font
  3131. *
  3132. * @type {Highcharts.CSSObject}
  3133. * @default {"fontFamily": Helvetica, Arial, sans-serif","fontSize":"1rem"}
  3134. * @apioption chart.style
  3135. */
  3136. /**
  3137. * The default series type for the chart. Can be any of the chart types
  3138. * listed under [plotOptions](#plotOptions) and [series](#series) or can
  3139. * be a series provided by an additional module.
  3140. *
  3141. * In TypeScript this option has no effect in sense of typing and
  3142. * instead the `type` option must always be set in the series.
  3143. *
  3144. * @sample {highcharts} highcharts/chart/type-bar/
  3145. * Bar
  3146. * @sample {highstock} stock/chart/type/
  3147. * Areaspline
  3148. * @sample {highmaps} maps/chart/type-mapline/
  3149. * Mapline
  3150. *
  3151. * @type {string}
  3152. * @default {highcharts} line
  3153. * @default {highstock} line
  3154. * @default {highmaps} map
  3155. * @since 2.1.0
  3156. * @apioption chart.type
  3157. */
  3158. type: 'line',
  3159. /**
  3160. * Decides in what dimensions the user can zoom by dragging the mouse.
  3161. * Can be one of `x`, `y` or `xy`.
  3162. *
  3163. * @see [panKey](#chart.panKey)
  3164. *
  3165. * @sample {highcharts} highcharts/chart/zoomtype-none/
  3166. * None by default
  3167. * @sample {highcharts} highcharts/chart/zoomtype-x/
  3168. * X
  3169. * @sample {highcharts} highcharts/chart/zoomtype-y/
  3170. * Y
  3171. * @sample {highcharts} highcharts/chart/zoomtype-xy/
  3172. * Xy
  3173. * @sample {highcharts} highcharts/chart/zoomtype-polar/
  3174. * Zoom on polar chart
  3175. * @sample {highstock} stock/demo/basic-line/
  3176. * None by default
  3177. * @sample {highstock} stock/chart/zoomtype-x/
  3178. * X
  3179. * @sample {highstock} stock/chart/zoomtype-y/
  3180. * Y
  3181. * @sample {highstock} stock/chart/zoomtype-xy/
  3182. * Xy
  3183. * @sample {highmaps} maps/chart/zoomtype-xy/
  3184. * Map with selection zoom
  3185. *
  3186. * @type {string}
  3187. * @validvalue ["x", "y", "xy"]
  3188. * @deprecated
  3189. * @apioption chart.zoomType
  3190. */
  3191. /**
  3192. * Enables zooming by a single touch, in combination with
  3193. * [chart.zoomType](#chart.zoomType). When enabled, two-finger pinch
  3194. * will still work as set up by [chart.pinchType](#chart.pinchType).
  3195. * However, `zoomBySingleTouch` will interfere with touch-dragging the
  3196. * chart to read the tooltip. And especially when vertical zooming is
  3197. * enabled, it will make it hard to scroll vertically on the page.
  3198. * @since 9.0.0
  3199. * @sample highcharts/chart/zoombysingletouch
  3200. * Zoom by single touch enabled, with buttons to toggle
  3201. * @product highcharts highstock gantt
  3202. * @deprecated
  3203. */
  3204. /**
  3205. * Chart zooming options.
  3206. * @since 10.2.1
  3207. */
  3208. zooming: {
  3209. /**
  3210. * Equivalent to [type](#chart.zooming.type), but for multitouch
  3211. * gestures only. By default, the `pinchType` is the same as the
  3212. * `type` setting. However, pinching can be enabled separately in
  3213. * some cases, for example in stock charts where a mouse drag pans the
  3214. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  3215. * #tooltip.followTouchMove) is true, pinchType only applies to
  3216. * two-finger touches.
  3217. *
  3218. * @type {string}
  3219. * @default {highcharts} undefined
  3220. * @default {highstock} x
  3221. * @product highcharts highstock gantt
  3222. * @validvalue ["x", "y", "xy"]
  3223. * @apioption chart.zooming.pinchType
  3224. */
  3225. /**
  3226. * Decides in what dimensions the user can zoom by dragging the mouse.
  3227. * Can be one of `x`, `y` or `xy`.
  3228. *
  3229. * @declare Highcharts.OptionsChartZoomingTypeValue
  3230. * @type {string}
  3231. * @default {highcharts} undefined
  3232. * @product highcharts highstock gantt
  3233. * @validvalue ["x", "y", "xy"]
  3234. * @apioption chart.zooming.type
  3235. */
  3236. /**
  3237. * Set a key to hold when dragging to zoom the chart. This is useful to
  3238. * avoid zooming while moving points. Should be set different than
  3239. * [chart.panKey](#chart.panKey).
  3240. *
  3241. * @type {string}
  3242. * @default {highcharts} undefined
  3243. * @validvalue ["alt", "ctrl", "meta", "shift"]
  3244. * @requires modules/draggable-points
  3245. * @apioption chart.zooming.key
  3246. */
  3247. /**
  3248. * Enables zooming by a single touch, in combination with
  3249. * [chart.zooming.type](#chart.zooming.type). When enabled, two-finger
  3250. * pinch will still work as set up by [chart.zooming.pinchType]
  3251. * (#chart.zooming.pinchType). However, `singleTouch` will interfere
  3252. * with touch-dragging the chart to read the tooltip. And especially
  3253. * when vertical zooming is enabled, it will make it hard to scroll
  3254. * vertically on the page.
  3255. *
  3256. * @sample highcharts/chart/zoombysingletouch
  3257. * Zoom by single touch enabled, with buttons to toggle
  3258. *
  3259. * @product highcharts highstock gantt
  3260. */
  3261. singleTouch: false,
  3262. /**
  3263. * The button that appears after a selection zoom, allowing the user
  3264. * to reset zoom.
  3265. */
  3266. resetButton: {
  3267. /**
  3268. * What frame the button placement should be related to. Can be
  3269. * either `plotBox` or `spacingBox`.
  3270. *
  3271. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  3272. * Relative to the chart
  3273. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  3274. * Relative to the chart
  3275. *
  3276. * @type {Highcharts.ButtonRelativeToValue}
  3277. * @default plot
  3278. * @apioption chart.zooming.resetButton.relativeTo
  3279. */
  3280. /**
  3281. * A collection of attributes for the button. The object takes SVG
  3282. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  3283. * border radius. The theme also supports `style`, a collection of
  3284. * CSS properties for the text. Equivalent attributes for the hover
  3285. * state are given in `theme.states.hover`.
  3286. *
  3287. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  3288. * Theming the button
  3289. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  3290. * Theming the button
  3291. *
  3292. * @type {Highcharts.SVGAttributes}
  3293. * @since 10.2.1
  3294. */
  3295. theme: {
  3296. /** @internal */
  3297. zIndex: 6
  3298. },
  3299. /**
  3300. * The position of the button.
  3301. *
  3302. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  3303. * Above the plot area
  3304. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  3305. * Above the plot area
  3306. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  3307. * Above the plot area
  3308. *
  3309. * @type {Highcharts.AlignObject}
  3310. * @since 10.2.1
  3311. */
  3312. position: {
  3313. /**
  3314. * The horizontal alignment of the button.
  3315. */
  3316. align: 'right',
  3317. /**
  3318. * The horizontal offset of the button.
  3319. */
  3320. x: -10,
  3321. /**
  3322. * The vertical alignment of the button.
  3323. *
  3324. * @type {Highcharts.VerticalAlignValue}
  3325. * @default top
  3326. * @apioption chart.zooming.resetButton.position.verticalAlign
  3327. */
  3328. /**
  3329. * The vertical offset of the button.
  3330. */
  3331. y: 10
  3332. }
  3333. }
  3334. },
  3335. /**
  3336. * An explicit width for the chart. By default (when `null`) the width
  3337. * is calculated from the offset width of the containing element.
  3338. *
  3339. * @sample {highcharts} highcharts/chart/width/
  3340. * 800px wide
  3341. * @sample {highstock} stock/chart/width/
  3342. * 800px wide
  3343. * @sample {highmaps} maps/chart/size/
  3344. * Chart with explicit size
  3345. *
  3346. * @type {null|number|string}
  3347. */
  3348. width: null,
  3349. /**
  3350. * An explicit height for the chart. If a _number_, the height is
  3351. * given in pixels. If given a _percentage string_ (for example
  3352. * `'56%'`), the height is given as the percentage of the actual chart
  3353. * width. This allows for preserving the aspect ratio across responsive
  3354. * sizes.
  3355. *
  3356. * By default (when `null`) the height is calculated from the offset
  3357. * height of the containing element, or 400 pixels if the containing
  3358. * element's height is 0.
  3359. *
  3360. * @sample {highcharts} highcharts/chart/height/
  3361. * 500px height
  3362. * @sample {highstock} stock/chart/height/
  3363. * 300px height
  3364. * @sample {highmaps} maps/chart/size/
  3365. * Chart with explicit size
  3366. * @sample highcharts/chart/height-percent/
  3367. * Highcharts with percentage height
  3368. *
  3369. * @type {null|number|string}
  3370. */
  3371. height: null,
  3372. /**
  3373. * The color of the outer chart border.
  3374. *
  3375. * @see In styled mode, the stroke is set with the
  3376. * `.highcharts-background` class.
  3377. *
  3378. * @sample {highcharts} highcharts/chart/bordercolor/
  3379. * Brown border
  3380. * @sample {highstock} stock/chart/border/
  3381. * Brown border
  3382. * @sample {highmaps} maps/chart/border/
  3383. * Border options
  3384. *
  3385. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3386. */
  3387. borderColor: "#334eff" /* Palette.highlightColor80 */,
  3388. /**
  3389. * The pixel width of the outer chart border.
  3390. *
  3391. * @see In styled mode, the stroke is set with the
  3392. * `.highcharts-background` class.
  3393. *
  3394. * @sample {highcharts} highcharts/chart/borderwidth/
  3395. * 5px border
  3396. * @sample {highstock} stock/chart/border/
  3397. * 2px border
  3398. * @sample {highmaps} maps/chart/border/
  3399. * Border options
  3400. *
  3401. * @type {number}
  3402. * @default 0
  3403. * @apioption chart.borderWidth
  3404. */
  3405. /**
  3406. * The background color or gradient for the outer chart area.
  3407. *
  3408. * @see In styled mode, the background is set with the
  3409. * `.highcharts-background` class.
  3410. *
  3411. * @sample {highcharts} highcharts/chart/backgroundcolor-color/
  3412. * Color
  3413. * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
  3414. * Gradient
  3415. * @sample {highstock} stock/chart/backgroundcolor-color/
  3416. * Color
  3417. * @sample {highstock} stock/chart/backgroundcolor-gradient/
  3418. * Gradient
  3419. * @sample {highmaps} maps/chart/backgroundcolor-color/
  3420. * Color
  3421. * @sample {highmaps} maps/chart/backgroundcolor-gradient/
  3422. * Gradient
  3423. *
  3424. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3425. */
  3426. backgroundColor: "#ffffff" /* Palette.backgroundColor */,
  3427. /**
  3428. * The background color or gradient for the plot area.
  3429. *
  3430. * @see In styled mode, the plot background is set with the
  3431. * `.highcharts-plot-background` class.
  3432. *
  3433. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
  3434. * Color
  3435. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
  3436. * Gradient
  3437. * @sample {highstock} stock/chart/plotbackgroundcolor-color/
  3438. * Color
  3439. * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
  3440. * Gradient
  3441. * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
  3442. * Color
  3443. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  3444. * Gradient
  3445. *
  3446. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3447. * @apioption chart.plotBackgroundColor
  3448. */
  3449. /**
  3450. * The URL for an image to use as the plot background. To set an image
  3451. * as the background for the entire chart, set a CSS background image
  3452. * to the container element. Note that for the image to be applied to
  3453. * exported charts, its URL needs to be accessible by the export server.
  3454. *
  3455. * @see In styled mode, a plot background image can be set with the
  3456. * `.highcharts-plot-background` class and a [custom pattern](
  3457. * https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  3458. *
  3459. * @sample {highcharts} highcharts/chart/plotbackgroundimage/
  3460. * Skies
  3461. * @sample {highstock} stock/chart/plotbackgroundimage/
  3462. * Skies
  3463. *
  3464. * @type {string}
  3465. * @apioption chart.plotBackgroundImage
  3466. */
  3467. /**
  3468. * The color of the inner chart or plot area border.
  3469. *
  3470. * @see In styled mode, a plot border stroke can be set with the
  3471. * `.highcharts-plot-border` class.
  3472. *
  3473. * @sample {highcharts} highcharts/chart/plotbordercolor/
  3474. * Blue border
  3475. * @sample {highstock} stock/chart/plotborder/
  3476. * Blue border
  3477. * @sample {highmaps} maps/chart/plotborder/
  3478. * Plot border options
  3479. *
  3480. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  3481. */
  3482. plotBorderColor: "#cccccc" /* Palette.neutralColor20 */
  3483. };
  3484. /* *
  3485. *
  3486. * Default Export
  3487. *
  3488. * */
  3489. return ChartDefaults;
  3490. });
  3491. _registerModule(_modules, 'Core/Color/Color.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  3492. /* *
  3493. *
  3494. * (c) 2010-2021 Torstein Honsi
  3495. *
  3496. * License: www.highcharts.com/license
  3497. *
  3498. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3499. *
  3500. * */
  3501. const { isNumber, merge, pInt } = U;
  3502. /* *
  3503. *
  3504. * Class
  3505. *
  3506. * */
  3507. /* eslint-disable valid-jsdoc */
  3508. /**
  3509. * Handle color operations. Some object methods are chainable.
  3510. *
  3511. * @class
  3512. * @name Highcharts.Color
  3513. *
  3514. * @param {Highcharts.ColorType} input
  3515. * The input color in either rbga or hex format
  3516. */
  3517. class Color {
  3518. /* *
  3519. *
  3520. * Static Functions
  3521. *
  3522. * */
  3523. /**
  3524. * Creates a color instance out of a color string or object.
  3525. *
  3526. * @function Highcharts.Color.parse
  3527. *
  3528. * @param {Highcharts.ColorType} [input]
  3529. * The input color in either rbga or hex format.
  3530. *
  3531. * @return {Highcharts.Color}
  3532. * Color instance.
  3533. */
  3534. static parse(input) {
  3535. return input ? new Color(input) : Color.None;
  3536. }
  3537. /* *
  3538. *
  3539. * Constructor
  3540. *
  3541. * */
  3542. constructor(input) {
  3543. this.rgba = [NaN, NaN, NaN, NaN];
  3544. this.input = input;
  3545. const GlobalColor = H.Color;
  3546. // Backwards compatibility, allow class overwrite
  3547. if (GlobalColor && GlobalColor !== Color) {
  3548. return new GlobalColor(input);
  3549. }
  3550. this.init(input);
  3551. }
  3552. /* *
  3553. *
  3554. * Functions
  3555. *
  3556. * */
  3557. /**
  3558. * Parse the input color to rgba array
  3559. *
  3560. * @private
  3561. * @function Highcharts.Color#init
  3562. *
  3563. * @param {Highcharts.ColorType} input
  3564. * The input color in either rbga or hex format
  3565. */
  3566. init(input) {
  3567. let result, rgba, i, parser;
  3568. // Gradients
  3569. if (typeof input === 'object' &&
  3570. typeof input.stops !== 'undefined') {
  3571. this.stops = input.stops.map((stop) => new Color(stop[1]));
  3572. // Solid colors
  3573. }
  3574. else if (typeof input === 'string') {
  3575. this.input = input = (Color.names[input.toLowerCase()] || input);
  3576. // Bitmasking as input[0] is not working for legacy IE.
  3577. if (input.charAt(0) === '#') {
  3578. const len = input.length, col = parseInt(input.substr(1), 16);
  3579. // Handle long-form, e.g. #AABBCC
  3580. if (len === 7) {
  3581. rgba = [
  3582. (col & 0xFF0000) >> 16,
  3583. (col & 0xFF00) >> 8,
  3584. (col & 0xFF),
  3585. 1
  3586. ];
  3587. // Handle short-form, e.g. #ABC
  3588. // In short form, the value is assumed to be the same
  3589. // for both nibbles for each component. e.g. #ABC = #AABBCC
  3590. }
  3591. else if (len === 4) {
  3592. rgba = [
  3593. (((col & 0xF00) >> 4) |
  3594. (col & 0xF00) >> 8),
  3595. (((col & 0xF0) >> 4) |
  3596. (col & 0xF0)),
  3597. ((col & 0xF) << 4) | (col & 0xF),
  3598. 1
  3599. ];
  3600. }
  3601. }
  3602. // Otherwise, check regex parsers
  3603. if (!rgba) {
  3604. i = Color.parsers.length;
  3605. while (i-- && !rgba) {
  3606. parser = Color.parsers[i];
  3607. result = parser.regex.exec(input);
  3608. if (result) {
  3609. rgba = parser.parse(result);
  3610. }
  3611. }
  3612. }
  3613. }
  3614. if (rgba) {
  3615. this.rgba = rgba;
  3616. }
  3617. }
  3618. /**
  3619. * Return the color or gradient stops in the specified format
  3620. *
  3621. * @function Highcharts.Color#get
  3622. *
  3623. * @param {string} [format]
  3624. * Possible values are 'a', 'rgb', 'rgba' (default).
  3625. *
  3626. * @return {Highcharts.ColorType}
  3627. * This color as a string or gradient stops.
  3628. */
  3629. get(format) {
  3630. const input = this.input, rgba = this.rgba;
  3631. if (typeof input === 'object' &&
  3632. typeof this.stops !== 'undefined') {
  3633. const ret = merge(input);
  3634. ret.stops = [].slice.call(ret.stops);
  3635. this.stops.forEach((stop, i) => {
  3636. ret.stops[i] = [
  3637. ret.stops[i][0],
  3638. stop.get(format)
  3639. ];
  3640. });
  3641. return ret;
  3642. }
  3643. // it's NaN if gradient colors on a column chart
  3644. if (rgba && isNumber(rgba[0])) {
  3645. if (format === 'rgb' || (!format && rgba[3] === 1)) {
  3646. return 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
  3647. }
  3648. if (format === 'a') {
  3649. return `${rgba[3]}`;
  3650. }
  3651. return 'rgba(' + rgba.join(',') + ')';
  3652. }
  3653. return input;
  3654. }
  3655. /**
  3656. * Brighten the color instance.
  3657. *
  3658. * @function Highcharts.Color#brighten
  3659. *
  3660. * @param {number} alpha
  3661. * The alpha value.
  3662. *
  3663. * @return {Highcharts.Color}
  3664. * This color with modifications.
  3665. */
  3666. brighten(alpha) {
  3667. const rgba = this.rgba;
  3668. if (this.stops) {
  3669. this.stops.forEach(function (stop) {
  3670. stop.brighten(alpha);
  3671. });
  3672. }
  3673. else if (isNumber(alpha) && alpha !== 0) {
  3674. for (let i = 0; i < 3; i++) {
  3675. rgba[i] += pInt(alpha * 255);
  3676. if (rgba[i] < 0) {
  3677. rgba[i] = 0;
  3678. }
  3679. if (rgba[i] > 255) {
  3680. rgba[i] = 255;
  3681. }
  3682. }
  3683. }
  3684. return this;
  3685. }
  3686. /**
  3687. * Set the color's opacity to a given alpha value.
  3688. *
  3689. * @function Highcharts.Color#setOpacity
  3690. *
  3691. * @param {number} alpha
  3692. * Opacity between 0 and 1.
  3693. *
  3694. * @return {Highcharts.Color}
  3695. * Color with modifications.
  3696. */
  3697. setOpacity(alpha) {
  3698. this.rgba[3] = alpha;
  3699. return this;
  3700. }
  3701. /**
  3702. * Return an intermediate color between two colors.
  3703. *
  3704. * @function Highcharts.Color#tweenTo
  3705. *
  3706. * @param {Highcharts.Color} to
  3707. * The color object to tween to.
  3708. *
  3709. * @param {number} pos
  3710. * The intermediate position, where 0 is the from color (current color
  3711. * item), and 1 is the `to` color.
  3712. *
  3713. * @return {Highcharts.ColorType}
  3714. * The intermediate color in rgba notation, or unsupported type.
  3715. */
  3716. tweenTo(to, pos) {
  3717. const fromRgba = this.rgba, toRgba = to.rgba;
  3718. // Unsupported color, return to-color (#3920, #7034)
  3719. if (!isNumber(fromRgba[0]) || !isNumber(toRgba[0])) {
  3720. return to.input || 'none';
  3721. }
  3722. // Check for has alpha, because rgba colors perform worse due to
  3723. // lack of support in WebKit.
  3724. const hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
  3725. return (hasAlpha ? 'rgba(' : 'rgb(') +
  3726. Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
  3727. ',' +
  3728. Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
  3729. ',' +
  3730. Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
  3731. (hasAlpha ?
  3732. (',' +
  3733. (toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))) :
  3734. '') +
  3735. ')';
  3736. }
  3737. }
  3738. /* *
  3739. *
  3740. * Static Properties
  3741. *
  3742. * */
  3743. /**
  3744. * Collection of named colors. Can be extended from the outside by adding
  3745. * colors to Highcharts.Color.names.
  3746. * @private
  3747. */
  3748. Color.names = {
  3749. white: '#ffffff',
  3750. black: '#000000'
  3751. };
  3752. /**
  3753. * Collection of parsers. This can be extended from the outside by pushing
  3754. * parsers to `Color.parsers`.
  3755. */
  3756. Color.parsers = [{
  3757. // RGBA color
  3758. // eslint-disable-next-line max-len
  3759. regex: /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,
  3760. parse: function (result) {
  3761. return [
  3762. pInt(result[1]),
  3763. pInt(result[2]),
  3764. pInt(result[3]),
  3765. parseFloat(result[4], 10)
  3766. ];
  3767. }
  3768. }, {
  3769. // RGB color
  3770. regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
  3771. parse: function (result) {
  3772. return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
  3773. }
  3774. }];
  3775. // Must be last static member for init cycle
  3776. Color.None = new Color('');
  3777. /* *
  3778. *
  3779. * Default Export
  3780. *
  3781. * */
  3782. /* *
  3783. *
  3784. * API Declarations
  3785. *
  3786. * */
  3787. /**
  3788. * A valid color to be parsed and handled by Highcharts. Highcharts internally
  3789. * supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
  3790. * rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
  3791. * browsers and displayed correctly, but Highcharts is not able to process them
  3792. * and apply concepts like opacity and brightening.
  3793. *
  3794. * @typedef {string} Highcharts.ColorString
  3795. */
  3796. /**
  3797. * A valid color type than can be parsed and handled by Highcharts. It can be a
  3798. * color string, a gradient object, or a pattern object.
  3799. *
  3800. * @typedef {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} Highcharts.ColorType
  3801. */
  3802. /**
  3803. * Gradient options instead of a solid color.
  3804. *
  3805. * @example
  3806. * // Linear gradient used as a color option
  3807. * color: {
  3808. * linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
  3809. * stops: [
  3810. * [0, '#003399'], // start
  3811. * [0.5, '#ffffff'], // middle
  3812. * [1, '#3366AA'] // end
  3813. * ]
  3814. * }
  3815. *
  3816. * @interface Highcharts.GradientColorObject
  3817. */ /**
  3818. * Holds an object that defines the start position and the end position relative
  3819. * to the shape.
  3820. * @name Highcharts.GradientColorObject#linearGradient
  3821. * @type {Highcharts.LinearGradientColorObject|undefined}
  3822. */ /**
  3823. * Holds an object that defines the center position and the radius.
  3824. * @name Highcharts.GradientColorObject#radialGradient
  3825. * @type {Highcharts.RadialGradientColorObject|undefined}
  3826. */ /**
  3827. * The first item in each tuple is the position in the gradient, where 0 is the
  3828. * start of the gradient and 1 is the end of the gradient. Multiple stops can be
  3829. * applied. The second item is the color for each stop. This color can also be
  3830. * given in the rgba format.
  3831. * @name Highcharts.GradientColorObject#stops
  3832. * @type {Array<Highcharts.GradientColorStopObject>}
  3833. */
  3834. /**
  3835. * Color stop tuple.
  3836. *
  3837. * @see Highcharts.GradientColorObject
  3838. *
  3839. * @interface Highcharts.GradientColorStopObject
  3840. */ /**
  3841. * @name Highcharts.GradientColorStopObject#0
  3842. * @type {number}
  3843. */ /**
  3844. * @name Highcharts.GradientColorStopObject#1
  3845. * @type {Highcharts.ColorString}
  3846. */ /**
  3847. * @name Highcharts.GradientColorStopObject#color
  3848. * @type {Highcharts.Color|undefined}
  3849. */
  3850. /**
  3851. * Defines the start position and the end position for a gradient relative
  3852. * to the shape. Start position (x1, y1) and end position (x2, y2) are relative
  3853. * to the shape, where 0 means top/left and 1 is bottom/right.
  3854. *
  3855. * @interface Highcharts.LinearGradientColorObject
  3856. */ /**
  3857. * Start horizontal position of the gradient. Float ranges 0-1.
  3858. * @name Highcharts.LinearGradientColorObject#x1
  3859. * @type {number}
  3860. */ /**
  3861. * End horizontal position of the gradient. Float ranges 0-1.
  3862. * @name Highcharts.LinearGradientColorObject#x2
  3863. * @type {number}
  3864. */ /**
  3865. * Start vertical position of the gradient. Float ranges 0-1.
  3866. * @name Highcharts.LinearGradientColorObject#y1
  3867. * @type {number}
  3868. */ /**
  3869. * End vertical position of the gradient. Float ranges 0-1.
  3870. * @name Highcharts.LinearGradientColorObject#y2
  3871. * @type {number}
  3872. */
  3873. /**
  3874. * Defines the center position and the radius for a gradient.
  3875. *
  3876. * @interface Highcharts.RadialGradientColorObject
  3877. */ /**
  3878. * Center horizontal position relative to the shape. Float ranges 0-1.
  3879. * @name Highcharts.RadialGradientColorObject#cx
  3880. * @type {number}
  3881. */ /**
  3882. * Center vertical position relative to the shape. Float ranges 0-1.
  3883. * @name Highcharts.RadialGradientColorObject#cy
  3884. * @type {number}
  3885. */ /**
  3886. * Radius relative to the shape. Float ranges 0-1.
  3887. * @name Highcharts.RadialGradientColorObject#r
  3888. * @type {number}
  3889. */
  3890. /**
  3891. * Creates a color instance out of a color string.
  3892. *
  3893. * @function Highcharts.color
  3894. *
  3895. * @param {Highcharts.ColorType} input
  3896. * The input color in either rbga or hex format
  3897. *
  3898. * @return {Highcharts.Color}
  3899. * Color instance
  3900. */
  3901. (''); // detach doclets above
  3902. return Color;
  3903. });
  3904. _registerModule(_modules, 'Core/Color/Palettes.js', [], function () {
  3905. /**
  3906. * Series palettes for Highcharts. Series colors are defined in highcharts.css.
  3907. * **Do not edit this file!** This file is generated using the 'gulp palette' task.
  3908. */
  3909. const SeriesPalettes = {
  3910. /**
  3911. * Colors for data series and points
  3912. */
  3913. colors: [
  3914. '#2caffe',
  3915. '#544fc5',
  3916. '#00e272',
  3917. '#fe6a35',
  3918. '#6b8abc',
  3919. '#d568fb',
  3920. '#2ee0ca',
  3921. '#fa4b42',
  3922. '#feb56a',
  3923. '#91e8e1'
  3924. ]
  3925. };
  3926. return SeriesPalettes;
  3927. });
  3928. _registerModule(_modules, 'Core/Time.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  3929. /* *
  3930. *
  3931. * (c) 2010-2021 Torstein Honsi
  3932. *
  3933. * License: www.highcharts.com/license
  3934. *
  3935. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3936. *
  3937. * */
  3938. const { win } = H;
  3939. const { defined, error, extend, isObject, merge, objectEach, pad, pick, splat, timeUnits } = U;
  3940. /* *
  3941. *
  3942. * Constants
  3943. *
  3944. * */
  3945. const hasNewSafariBug = H.isSafari &&
  3946. win.Intl &&
  3947. win.Intl.DateTimeFormat.prototype.formatRange;
  3948. // To do: Remove this when we no longer need support for Safari < v14.1
  3949. const hasOldSafariBug = H.isSafari &&
  3950. win.Intl &&
  3951. !win.Intl.DateTimeFormat.prototype.formatRange;
  3952. /* *
  3953. *
  3954. * Class
  3955. *
  3956. * */
  3957. /* eslint-disable no-invalid-this, valid-jsdoc */
  3958. /**
  3959. * The Time class. Time settings are applied in general for each page using
  3960. * `Highcharts.setOptions`, or individually for each Chart item through the
  3961. * [time](https://api.highcharts.com/highcharts/time) options set.
  3962. *
  3963. * The Time object is available from {@link Highcharts.Chart#time},
  3964. * which refers to `Highcharts.time` if no individual time settings are
  3965. * applied.
  3966. *
  3967. * @example
  3968. * // Apply time settings globally
  3969. * Highcharts.setOptions({
  3970. * time: {
  3971. * timezone: 'Europe/London'
  3972. * }
  3973. * });
  3974. *
  3975. * // Apply time settings by instance
  3976. * let chart = Highcharts.chart('container', {
  3977. * time: {
  3978. * timezone: 'America/New_York'
  3979. * },
  3980. * series: [{
  3981. * data: [1, 4, 3, 5]
  3982. * }]
  3983. * });
  3984. *
  3985. * // Use the Time object
  3986. * console.log(
  3987. * 'Current time in New York',
  3988. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  3989. * );
  3990. *
  3991. * @since 6.0.5
  3992. *
  3993. * @class
  3994. * @name Highcharts.Time
  3995. *
  3996. * @param {Highcharts.TimeOptions} [options]
  3997. * Time options as defined in [chart.options.time](/highcharts/time).
  3998. */
  3999. class Time {
  4000. /* *
  4001. *
  4002. * Constructors
  4003. *
  4004. * */
  4005. constructor(options) {
  4006. /* *
  4007. *
  4008. * Properties
  4009. *
  4010. * */
  4011. this.options = {};
  4012. this.useUTC = false;
  4013. this.variableTimezone = false;
  4014. this.Date = win.Date;
  4015. /**
  4016. * Get the time zone offset based on the current timezone information as
  4017. * set in the global options.
  4018. *
  4019. * @function Highcharts.Time#getTimezoneOffset
  4020. *
  4021. * @param {number} timestamp
  4022. * The JavaScript timestamp to inspect.
  4023. *
  4024. * @return {number}
  4025. * The timezone offset in minutes compared to UTC.
  4026. */
  4027. this.getTimezoneOffset = this.timezoneOffsetFunction();
  4028. this.update(options);
  4029. }
  4030. /* *
  4031. *
  4032. * Functions
  4033. *
  4034. * */
  4035. /**
  4036. * Time units used in `Time.get` and `Time.set`
  4037. *
  4038. * @typedef {"Date"|"Day"|"FullYear"|"Hours"|"Milliseconds"|"Minutes"|"Month"|"Seconds"} Highcharts.TimeUnitValue
  4039. */
  4040. /**
  4041. * Get the value of a date object in given units, and subject to the Time
  4042. * object's current timezone settings. This function corresponds directly to
  4043. * JavaScripts `Date.getXXX / Date.getUTCXXX`, so instead of calling
  4044. * `date.getHours()` or `date.getUTCHours()` we will call
  4045. * `time.get('Hours')`.
  4046. *
  4047. * @function Highcharts.Time#get
  4048. *
  4049. * @param {Highcharts.TimeUnitValue} unit
  4050. * @param {Date} date
  4051. *
  4052. * @return {number}
  4053. * The given time unit
  4054. */
  4055. get(unit, date) {
  4056. if (this.variableTimezone || this.timezoneOffset) {
  4057. const realMs = date.getTime();
  4058. const ms = realMs - this.getTimezoneOffset(date);
  4059. date.setTime(ms); // Temporary adjust to timezone
  4060. const ret = date['getUTC' + unit]();
  4061. date.setTime(realMs); // Reset
  4062. return ret;
  4063. }
  4064. // UTC time with no timezone handling
  4065. if (this.useUTC) {
  4066. return date['getUTC' + unit]();
  4067. }
  4068. // Else, local time
  4069. return date['get' + unit]();
  4070. }
  4071. /**
  4072. * Set the value of a date object in given units, and subject to the Time
  4073. * object's current timezone settings. This function corresponds directly to
  4074. * JavaScripts `Date.setXXX / Date.setUTCXXX`, so instead of calling
  4075. * `date.setHours(0)` or `date.setUTCHours(0)` we will call
  4076. * `time.set('Hours', 0)`.
  4077. *
  4078. * @function Highcharts.Time#set
  4079. *
  4080. * @param {Highcharts.TimeUnitValue} unit
  4081. * @param {Date} date
  4082. * @param {number} value
  4083. *
  4084. * @return {number}
  4085. * The epoch milliseconds of the updated date
  4086. */
  4087. set(unit, date, value) {
  4088. // UTC time with timezone handling
  4089. if (this.variableTimezone || this.timezoneOffset) {
  4090. // For lower order time units, just set it directly using UTC
  4091. // time
  4092. if (unit === 'Milliseconds' ||
  4093. unit === 'Seconds' ||
  4094. (unit === 'Minutes' &&
  4095. this.getTimezoneOffset(date) % 3600000 === 0) // #13961
  4096. ) {
  4097. return date['setUTC' + unit](value);
  4098. }
  4099. // Higher order time units need to take the time zone into
  4100. // account
  4101. // Adjust by timezone
  4102. const offset = this.getTimezoneOffset(date);
  4103. let ms = date.getTime() - offset;
  4104. date.setTime(ms);
  4105. date['setUTC' + unit](value);
  4106. const newOffset = this.getTimezoneOffset(date);
  4107. ms = date.getTime() + newOffset;
  4108. return date.setTime(ms);
  4109. }
  4110. // UTC time with no timezone handling
  4111. if (this.useUTC ||
  4112. // leap calculation in UTC only
  4113. (hasNewSafariBug && unit === 'FullYear')) {
  4114. return date['setUTC' + unit](value);
  4115. }
  4116. // Else, local time
  4117. return date['set' + unit](value);
  4118. }
  4119. /**
  4120. * Update the Time object with current options. It is called internally on
  4121. * initializing Highcharts, after running `Highcharts.setOptions` and on
  4122. * `Chart.update`.
  4123. *
  4124. * @private
  4125. * @function Highcharts.Time#update
  4126. *
  4127. * @param {Highcharts.TimeOptions} [options]
  4128. *
  4129. */
  4130. update(options = {}) {
  4131. const useUTC = pick(options.useUTC, true);
  4132. this.options = options = merge(true, this.options, options);
  4133. // Allow using a different Date class
  4134. this.Date = options.Date || win.Date || Date;
  4135. this.useUTC = useUTC;
  4136. this.timezoneOffset = (useUTC && options.timezoneOffset) || void 0;
  4137. this.getTimezoneOffset = this.timezoneOffsetFunction();
  4138. /*
  4139. * The time object has options allowing for variable time zones, meaning
  4140. * the axis ticks or series data needs to consider this.
  4141. */
  4142. this.variableTimezone = useUTC && !!(options.getTimezoneOffset ||
  4143. options.timezone);
  4144. }
  4145. /**
  4146. * Make a time and returns milliseconds. Interprets the inputs as UTC time,
  4147. * local time or a specific timezone time depending on the current time
  4148. * settings.
  4149. *
  4150. * @function Highcharts.Time#makeTime
  4151. *
  4152. * @param {number} year
  4153. * The year
  4154. *
  4155. * @param {number} month
  4156. * The month. Zero-based, so January is 0.
  4157. *
  4158. * @param {number} [date=1]
  4159. * The day of the month
  4160. *
  4161. * @param {number} [hours=0]
  4162. * The hour of the day, 0-23.
  4163. *
  4164. * @param {number} [minutes=0]
  4165. * The minutes
  4166. *
  4167. * @param {number} [seconds=0]
  4168. * The seconds
  4169. *
  4170. * @return {number}
  4171. * The time in milliseconds since January 1st 1970.
  4172. */
  4173. makeTime(year, month, date, hours, minutes, seconds) {
  4174. let d, offset, newOffset;
  4175. if (this.useUTC) {
  4176. d = this.Date.UTC.apply(0, arguments);
  4177. offset = this.getTimezoneOffset(d);
  4178. d += offset;
  4179. newOffset = this.getTimezoneOffset(d);
  4180. if (offset !== newOffset) {
  4181. d += newOffset - offset;
  4182. // A special case for transitioning from summer time to winter time.
  4183. // When the clock is set back, the same time is repeated twice, i.e.
  4184. // 02:30 am is repeated since the clock is set back from 3 am to
  4185. // 2 am. We need to make the same time as local Date does.
  4186. }
  4187. else if (offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
  4188. !hasOldSafariBug) {
  4189. d -= 36e5;
  4190. }
  4191. }
  4192. else {
  4193. d = new this.Date(year, month, pick(date, 1), pick(hours, 0), pick(minutes, 0), pick(seconds, 0)).getTime();
  4194. }
  4195. return d;
  4196. }
  4197. /**
  4198. * Sets the getTimezoneOffset function. If the `timezone` option is set, a
  4199. * default getTimezoneOffset function with that timezone is returned. If
  4200. * a `getTimezoneOffset` option is defined, it is returned. If neither are
  4201. * specified, the function using the `timezoneOffset` option or 0 offset is
  4202. * returned.
  4203. *
  4204. * @private
  4205. * @function Highcharts.Time#timezoneOffsetFunction
  4206. *
  4207. * @return {Function}
  4208. * A getTimezoneOffset function
  4209. */
  4210. timezoneOffsetFunction() {
  4211. const time = this, options = this.options, getTimezoneOffset = options.getTimezoneOffset, moment = options.moment || win.moment;
  4212. if (!this.useUTC) {
  4213. return function (timestamp) {
  4214. return new Date(timestamp.toString()).getTimezoneOffset() * 60000;
  4215. };
  4216. }
  4217. if (options.timezone) {
  4218. if (!moment) {
  4219. // getTimezoneOffset-function stays undefined because it depends
  4220. // on Moment.js
  4221. error(25);
  4222. }
  4223. else {
  4224. return function (timestamp) {
  4225. return -moment.tz(timestamp, options.timezone).utcOffset() * 60000;
  4226. };
  4227. }
  4228. }
  4229. // If not timezone is set, look for the getTimezoneOffset callback
  4230. if (this.useUTC && getTimezoneOffset) {
  4231. return function (timestamp) {
  4232. return getTimezoneOffset(timestamp.valueOf()) * 60000;
  4233. };
  4234. }
  4235. // Last, use the `timezoneOffset` option if set
  4236. return function () {
  4237. return (time.timezoneOffset || 0) * 60000;
  4238. };
  4239. }
  4240. /**
  4241. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970)
  4242. * into a human readable date string. The available format keys are listed
  4243. * below. Additional formats can be given in the
  4244. * {@link Highcharts.dateFormats} hook.
  4245. *
  4246. * Supported format keys:
  4247. * - `%a`: Short weekday, like 'Mon'
  4248. * - `%A`: Long weekday, like 'Monday'
  4249. * - `%d`: Two digit day of the month, 01 to 31
  4250. * - `%e`: Day of the month, 1 through 31
  4251. * - `%w`: Day of the week, 0 through 6
  4252. * - `%b`: Short month, like 'Jan'
  4253. * - `%B`: Long month, like 'January'
  4254. * - `%m`: Two digit month number, 01 through 12
  4255. * - `%y`: Two digits year, like 09 for 2009
  4256. * - `%Y`: Four digits year, like 2009
  4257. * - `%H`: Two digits hours in 24h format, 00 through 23
  4258. * - `%k`: Hours in 24h format, 0 through 23
  4259. * - `%I`: Two digits hours in 12h format, 00 through 11
  4260. * - `%l`: Hours in 12h format, 1 through 12
  4261. * - `%M`: Two digits minutes, 00 through 59
  4262. * - `%p`: Upper case AM or PM
  4263. * - `%P`: Lower case AM or PM
  4264. * - `%S`: Two digits seconds, 00 through 59
  4265. * - `%L`: Milliseconds (naming from Ruby)
  4266. *
  4267. * @example
  4268. * const time = new Highcharts.Time();
  4269. * const s = time.dateFormat('%Y-%m-%d %H:%M:%S', Date.UTC(2020, 0, 1));
  4270. * console.log(s); // => 2020-01-01 00:00:00
  4271. *
  4272. * @function Highcharts.Time#dateFormat
  4273. *
  4274. * @param {string} format
  4275. * The desired format where various time representations are
  4276. * prefixed with %.
  4277. *
  4278. * @param {number} [timestamp]
  4279. * The JavaScript timestamp.
  4280. *
  4281. * @param {boolean} [capitalize=false]
  4282. * Upper case first letter in the return.
  4283. *
  4284. * @return {string}
  4285. * The formatted date.
  4286. */
  4287. dateFormat(format, timestamp, capitalize) {
  4288. if (!defined(timestamp) || isNaN(timestamp)) {
  4289. return (H.defaultOptions.lang &&
  4290. H.defaultOptions.lang.invalidDate ||
  4291. '');
  4292. }
  4293. format = pick(format, '%Y-%m-%d %H:%M:%S');
  4294. const time = this, date = new this.Date(timestamp),
  4295. // get the basic time values
  4296. hours = this.get('Hours', date), day = this.get('Day', date), dayOfMonth = this.get('Date', date), month = this.get('Month', date), fullYear = this.get('FullYear', date), lang = H.defaultOptions.lang, langWeekdays = (lang && lang.weekdays), shortWeekdays = (lang && lang.shortWeekdays),
  4297. // List all format keys. Custom formats can be added from the
  4298. // outside.
  4299. replacements = extend({
  4300. // Day
  4301. // Short weekday, like 'Mon'
  4302. a: shortWeekdays ?
  4303. shortWeekdays[day] :
  4304. langWeekdays[day].substr(0, 3),
  4305. // Long weekday, like 'Monday'
  4306. A: langWeekdays[day],
  4307. // Two digit day of the month, 01 to 31
  4308. d: pad(dayOfMonth),
  4309. // Day of the month, 1 through 31
  4310. e: pad(dayOfMonth, 2, ' '),
  4311. // Day of the week, 0 through 6
  4312. w: day,
  4313. // Week (none implemented)
  4314. // 'W': weekNumber(),
  4315. // Month
  4316. // Short month, like 'Jan'
  4317. b: lang.shortMonths[month],
  4318. // Long month, like 'January'
  4319. B: lang.months[month],
  4320. // Two digit month number, 01 through 12
  4321. m: pad(month + 1),
  4322. // Month number, 1 through 12 (#8150)
  4323. o: month + 1,
  4324. // Year
  4325. // Two digits year, like 09 for 2009
  4326. y: fullYear.toString().substr(2, 2),
  4327. // Four digits year, like 2009
  4328. Y: fullYear,
  4329. // Time
  4330. // Two digits hours in 24h format, 00 through 23
  4331. H: pad(hours),
  4332. // Hours in 24h format, 0 through 23
  4333. k: hours,
  4334. // Two digits hours in 12h format, 00 through 11
  4335. I: pad((hours % 12) || 12),
  4336. // Hours in 12h format, 1 through 12
  4337. l: (hours % 12) || 12,
  4338. // Two digits minutes, 00 through 59
  4339. M: pad(this.get('Minutes', date)),
  4340. // Upper case AM or PM
  4341. p: hours < 12 ? 'AM' : 'PM',
  4342. // Lower case AM or PM
  4343. P: hours < 12 ? 'am' : 'pm',
  4344. // Two digits seconds, 00 through 59
  4345. S: pad(date.getSeconds()),
  4346. // Milliseconds (naming from Ruby)
  4347. L: pad(Math.floor(timestamp % 1000), 3)
  4348. }, H.dateFormats);
  4349. // Do the replaces
  4350. objectEach(replacements, function (val, key) {
  4351. // Regex would do it in one line, but this is faster
  4352. while (format.indexOf('%' + key) !== -1) {
  4353. format = format.replace('%' + key, typeof val === 'function' ? val.call(time, timestamp) : val);
  4354. }
  4355. });
  4356. // Optionally capitalize the string and return
  4357. return capitalize ?
  4358. (format.substr(0, 1).toUpperCase() +
  4359. format.substr(1)) :
  4360. format;
  4361. }
  4362. /**
  4363. * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
  4364. * an object.
  4365. * @private
  4366. * @param {string|Array<T>|Highcharts.Dictionary<T>} f
  4367. * General format description
  4368. * @return {Highcharts.Dictionary<T>}
  4369. * The object definition
  4370. */
  4371. resolveDTLFormat(f) {
  4372. if (!isObject(f, true)) { // check for string or array
  4373. f = splat(f);
  4374. return {
  4375. main: f[0],
  4376. from: f[1],
  4377. to: f[2]
  4378. };
  4379. }
  4380. return f;
  4381. }
  4382. /**
  4383. * Return an array with time positions distributed on round time values
  4384. * right and right after min and max. Used in datetime axes as well as for
  4385. * grouping data on a datetime axis.
  4386. *
  4387. * @function Highcharts.Time#getTimeTicks
  4388. *
  4389. * @param {Highcharts.TimeNormalizedObject} normalizedInterval
  4390. * The interval in axis values (ms) and the count
  4391. *
  4392. * @param {number} [min]
  4393. * The minimum in axis values
  4394. *
  4395. * @param {number} [max]
  4396. * The maximum in axis values
  4397. *
  4398. * @param {number} [startOfWeek=1]
  4399. *
  4400. * @return {Highcharts.AxisTickPositionsArray}
  4401. * Time positions
  4402. */
  4403. getTimeTicks(normalizedInterval, min, max, startOfWeek) {
  4404. const time = this, Date = time.Date, tickPositions = [], higherRanks = {},
  4405. // When crossing DST, use the max. Resolves #6278.
  4406. minDate = new Date(min), interval = normalizedInterval.unitRange, count = normalizedInterval.count || 1;
  4407. let i, minYear, // used in months and years as a basis for Date.UTC()
  4408. variableDayLength, minDay;
  4409. startOfWeek = pick(startOfWeek, 1);
  4410. if (defined(min)) { // #1300
  4411. time.set('Milliseconds', minDate, interval >= timeUnits.second ?
  4412. 0 : // #3935
  4413. count * Math.floor(time.get('Milliseconds', minDate) / count)); // #3652, #3654
  4414. if (interval >= timeUnits.second) { // second
  4415. time.set('Seconds', minDate, interval >= timeUnits.minute ?
  4416. 0 : // #3935
  4417. count * Math.floor(time.get('Seconds', minDate) / count));
  4418. }
  4419. if (interval >= timeUnits.minute) { // minute
  4420. time.set('Minutes', minDate, interval >= timeUnits.hour ?
  4421. 0 :
  4422. count * Math.floor(time.get('Minutes', minDate) / count));
  4423. }
  4424. if (interval >= timeUnits.hour) { // hour
  4425. time.set('Hours', minDate, interval >= timeUnits.day ?
  4426. 0 :
  4427. count * Math.floor(time.get('Hours', minDate) / count));
  4428. }
  4429. if (interval >= timeUnits.day) { // day
  4430. time.set('Date', minDate, interval >= timeUnits.month ?
  4431. 1 :
  4432. Math.max(1, count * Math.floor(time.get('Date', minDate) / count)));
  4433. }
  4434. if (interval >= timeUnits.month) { // month
  4435. time.set('Month', minDate, interval >= timeUnits.year ? 0 :
  4436. count * Math.floor(time.get('Month', minDate) / count));
  4437. minYear = time.get('FullYear', minDate);
  4438. }
  4439. if (interval >= timeUnits.year) { // year
  4440. minYear -= minYear % count;
  4441. time.set('FullYear', minDate, minYear);
  4442. }
  4443. // week is a special case that runs outside the hierarchy
  4444. if (interval === timeUnits.week) {
  4445. // get start of current week, independent of count
  4446. minDay = time.get('Day', minDate);
  4447. time.set('Date', minDate, (time.get('Date', minDate) -
  4448. minDay + startOfWeek +
  4449. // We don't want to skip days that are before
  4450. // startOfWeek (#7051)
  4451. (minDay < startOfWeek ? -7 : 0)));
  4452. }
  4453. // Get basics for variable time spans
  4454. minYear = time.get('FullYear', minDate);
  4455. const minMonth = time.get('Month', minDate), minDateDate = time.get('Date', minDate), minHours = time.get('Hours', minDate);
  4456. // Redefine min to the floored/rounded minimum time (#7432)
  4457. min = minDate.getTime();
  4458. // Handle local timezone offset
  4459. if ((time.variableTimezone || !time.useUTC) && defined(max)) {
  4460. // Detect whether we need to take the DST crossover into
  4461. // consideration. If we're crossing over DST, the day length may
  4462. // be 23h or 25h and we need to compute the exact clock time for
  4463. // each tick instead of just adding hours. This comes at a cost,
  4464. // so first we find out if it is needed (#4951).
  4465. variableDayLength = (
  4466. // Long range, assume we're crossing over.
  4467. max - min > 4 * timeUnits.month ||
  4468. // Short range, check if min and max are in different time
  4469. // zones.
  4470. time.getTimezoneOffset(min) !==
  4471. time.getTimezoneOffset(max));
  4472. }
  4473. // Iterate and add tick positions at appropriate values
  4474. let t = minDate.getTime();
  4475. i = 1;
  4476. while (t < max) {
  4477. tickPositions.push(t);
  4478. // if the interval is years, use Date.UTC to increase years
  4479. if (interval === timeUnits.year) {
  4480. t = time.makeTime(minYear + i * count, 0);
  4481. // if the interval is months, use Date.UTC to increase months
  4482. }
  4483. else if (interval === timeUnits.month) {
  4484. t = time.makeTime(minYear, minMonth + i * count);
  4485. // if we're using global time, the interval is not fixed as it
  4486. // jumps one hour at the DST crossover
  4487. }
  4488. else if (variableDayLength &&
  4489. (interval === timeUnits.day || interval === timeUnits.week)) {
  4490. t = time.makeTime(minYear, minMonth, minDateDate +
  4491. i * count * (interval === timeUnits.day ? 1 : 7));
  4492. }
  4493. else if (variableDayLength &&
  4494. interval === timeUnits.hour &&
  4495. count > 1) {
  4496. // make sure higher ranks are preserved across DST (#6797,
  4497. // #7621)
  4498. t = time.makeTime(minYear, minMonth, minDateDate, minHours + i * count);
  4499. // else, the interval is fixed and we use simple addition
  4500. }
  4501. else {
  4502. t += interval * count;
  4503. }
  4504. i++;
  4505. }
  4506. // push the last time
  4507. tickPositions.push(t);
  4508. // Handle higher ranks. Mark new days if the time is on midnight
  4509. // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
  4510. // to prevent looping over dense data grouping (#6156).
  4511. if (interval <= timeUnits.hour && tickPositions.length < 10000) {
  4512. tickPositions.forEach(function (t) {
  4513. if (
  4514. // Speed optimization, no need to run dateFormat unless
  4515. // we're on a full or half hour
  4516. t % 1800000 === 0 &&
  4517. // Check for local or global midnight
  4518. time.dateFormat('%H%M%S%L', t) === '000000000') {
  4519. higherRanks[t] = 'day';
  4520. }
  4521. });
  4522. }
  4523. }
  4524. // record information on the chosen unit - for dynamic label formatter
  4525. tickPositions.info = extend(normalizedInterval, {
  4526. higherRanks,
  4527. totalRange: interval * count
  4528. });
  4529. return tickPositions;
  4530. }
  4531. /**
  4532. * Get the optimal date format for a point, based on a range.
  4533. *
  4534. * @private
  4535. * @function Highcharts.Time#getDateFormat
  4536. *
  4537. * @param {number} range
  4538. * The time range
  4539. *
  4540. * @param {number} timestamp
  4541. * The timestamp of the date
  4542. *
  4543. * @param {number} startOfWeek
  4544. * An integer representing the first day of the week, where 0 is
  4545. * Sunday.
  4546. *
  4547. * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
  4548. * A map of time units to formats.
  4549. *
  4550. * @return {string}
  4551. * The optimal date format for a point.
  4552. */
  4553. getDateFormat(range, timestamp, startOfWeek, dateTimeLabelFormats) {
  4554. const dateStr = this.dateFormat('%m-%d %H:%M:%S.%L', timestamp), blank = '01-01 00:00:00.000', strpos = {
  4555. millisecond: 15,
  4556. second: 12,
  4557. minute: 9,
  4558. hour: 6,
  4559. day: 3
  4560. };
  4561. let n = 'millisecond',
  4562. // for sub-millisecond data, #4223
  4563. lastN = n;
  4564. for (n in timeUnits) { // eslint-disable-line guard-for-in
  4565. // If the range is exactly one week and we're looking at a
  4566. // Sunday/Monday, go for the week format
  4567. if (range === timeUnits.week &&
  4568. +this.dateFormat('%w', timestamp) === startOfWeek &&
  4569. dateStr.substr(6) === blank.substr(6)) {
  4570. n = 'week';
  4571. break;
  4572. }
  4573. // The first format that is too great for the range
  4574. if (timeUnits[n] > range) {
  4575. n = lastN;
  4576. break;
  4577. }
  4578. // If the point is placed every day at 23:59, we need to show
  4579. // the minutes as well. #2637.
  4580. if (strpos[n] &&
  4581. dateStr.substr(strpos[n]) !== blank.substr(strpos[n])) {
  4582. break;
  4583. }
  4584. // Weeks are outside the hierarchy, only apply them on
  4585. // Mondays/Sundays like in the first condition
  4586. if (n !== 'week') {
  4587. lastN = n;
  4588. }
  4589. }
  4590. return this.resolveDTLFormat(dateTimeLabelFormats[n]).main;
  4591. }
  4592. }
  4593. /* *
  4594. *
  4595. * Default export
  4596. *
  4597. * */
  4598. /* *
  4599. *
  4600. * API Declarations
  4601. *
  4602. * */
  4603. /**
  4604. * Normalized interval.
  4605. *
  4606. * @interface Highcharts.TimeNormalizedObject
  4607. */ /**
  4608. * The count.
  4609. *
  4610. * @name Highcharts.TimeNormalizedObject#count
  4611. * @type {number|undefined}
  4612. */ /**
  4613. * The interval in axis values (ms).
  4614. *
  4615. * @name Highcharts.TimeNormalizedObject#unitRange
  4616. * @type {number}
  4617. */
  4618. /**
  4619. * Function of an additional date format specifier.
  4620. *
  4621. * @callback Highcharts.TimeFormatCallbackFunction
  4622. *
  4623. * @param {number} timestamp
  4624. * The time to format.
  4625. *
  4626. * @return {string}
  4627. * The formatted portion of the date.
  4628. */
  4629. /**
  4630. * Time ticks.
  4631. *
  4632. * @interface Highcharts.AxisTickPositionsArray
  4633. * @extends global.Array<number>
  4634. */ /**
  4635. * @name Highcharts.AxisTickPositionsArray#info
  4636. * @type {Highcharts.TimeTicksInfoObject|undefined}
  4637. */
  4638. /**
  4639. * A callback to return the time zone offset for a given datetime. It
  4640. * takes the timestamp in terms of milliseconds since January 1 1970,
  4641. * and returns the timezone offset in minutes. This provides a hook
  4642. * for drawing time based charts in specific time zones using their
  4643. * local DST crossover dates, with the help of external libraries.
  4644. *
  4645. * @callback Highcharts.TimezoneOffsetCallbackFunction
  4646. *
  4647. * @param {number} timestamp
  4648. * Timestamp in terms of milliseconds since January 1 1970.
  4649. *
  4650. * @return {number}
  4651. * Timezone offset in minutes.
  4652. */
  4653. /**
  4654. * Allows to manually load the `moment.js` library from Highcharts options
  4655. * instead of the `window`.
  4656. * In case of loading the library from a `script` tag,
  4657. * this option is not needed, it will be loaded from there by default.
  4658. *
  4659. * @type {Function}
  4660. * @since 8.2.0
  4661. * @apioption time.moment
  4662. */
  4663. ''; // keeps doclets above in JS file
  4664. return Time;
  4665. });
  4666. _registerModule(_modules, 'Core/Defaults.js', [_modules['Core/Chart/ChartDefaults.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Color/Palettes.js'], _modules['Core/Time.js'], _modules['Core/Utilities.js']], function (ChartDefaults, Color, H, Palettes, Time, U) {
  4667. /* *
  4668. *
  4669. * (c) 2010-2021 Torstein Honsi
  4670. *
  4671. * License: www.highcharts.com/license
  4672. *
  4673. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4674. *
  4675. * */
  4676. const { parse: color } = Color;
  4677. const { isTouchDevice, svg } = H;
  4678. const { merge } = U;
  4679. /* *
  4680. *
  4681. * API Options
  4682. *
  4683. * */
  4684. /**
  4685. * Global default settings.
  4686. *
  4687. * @name Highcharts.defaultOptions
  4688. * @type {Highcharts.Options}
  4689. */ /**
  4690. * @optionparent
  4691. * @private
  4692. */
  4693. const defaultOptions = {
  4694. /**
  4695. * An array containing the default colors for the chart's series. When
  4696. * all colors are used, new colors are pulled from the start again.
  4697. *
  4698. * Default colors can also be set on a series or series.type basis,
  4699. * see [column.colors](#plotOptions.column.colors),
  4700. * [pie.colors](#plotOptions.pie.colors).
  4701. *
  4702. * In styled mode, the colors option doesn't exist. Instead, colors
  4703. * are defined in CSS and applied either through series or point class
  4704. * names, or through the [chart.colorCount](#chart.colorCount) option.
  4705. *
  4706. * @sample {highcharts} highcharts/chart/colors/
  4707. * Assign a global color theme
  4708. * @sample highcharts/members/theme-v10/
  4709. * Latest release styled like version 10
  4710. *
  4711. * @type {Array<(Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject)>}
  4712. * @default [
  4713. * "#2caffe",
  4714. * "#544fc5",
  4715. * "#00e272",
  4716. * "#fe6a35",
  4717. * "#6b8abc",
  4718. * "#d568fb",
  4719. * "#2ee0ca",
  4720. * "#fa4b42",
  4721. * "#feb56a",
  4722. * "#91e8e12
  4723. * ]
  4724. */
  4725. colors: Palettes.colors,
  4726. /**
  4727. * Styled mode only. Configuration object for adding SVG definitions for
  4728. * reusable elements. See [gradients, shadows and
  4729. * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
  4730. * for more information and code examples.
  4731. *
  4732. * @type {*}
  4733. * @since 5.0.0
  4734. * @apioption defs
  4735. */
  4736. /**
  4737. * @ignore-option
  4738. */
  4739. symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
  4740. /**
  4741. * The language object is global and it can't be set on each chart
  4742. * initialization. Instead, use `Highcharts.setOptions` to set it before any
  4743. * chart is initialized.
  4744. *
  4745. * ```js
  4746. * Highcharts.setOptions({
  4747. * lang: {
  4748. * months: [
  4749. * 'Janvier', 'Février', 'Mars', 'Avril',
  4750. * 'Mai', 'Juin', 'Juillet', 'Août',
  4751. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  4752. * ],
  4753. * weekdays: [
  4754. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  4755. * 'Jeudi', 'Vendredi', 'Samedi'
  4756. * ]
  4757. * }
  4758. * });
  4759. * ```
  4760. */
  4761. lang: {
  4762. /**
  4763. * The loading text that appears when the chart is set into the loading
  4764. * state following a call to `chart.showLoading`.
  4765. */
  4766. loading: 'Loading...',
  4767. /**
  4768. * An array containing the months names. Corresponds to the `%B` format
  4769. * in `Highcharts.dateFormat()`.
  4770. *
  4771. * @type {Array<string>}
  4772. * @default ["January", "February", "March", "April", "May", "June",
  4773. * "July", "August", "September", "October", "November",
  4774. * "December"]
  4775. */
  4776. months: [
  4777. 'January', 'February', 'March', 'April', 'May', 'June', 'July',
  4778. 'August', 'September', 'October', 'November', 'December'
  4779. ],
  4780. /**
  4781. * An array containing the months names in abbreviated form. Corresponds
  4782. * to the `%b` format in `Highcharts.dateFormat()`.
  4783. *
  4784. * @type {Array<string>}
  4785. * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  4786. * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  4787. */
  4788. shortMonths: [
  4789. 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
  4790. 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
  4791. ],
  4792. /**
  4793. * An array containing the weekday names.
  4794. *
  4795. * @type {Array<string>}
  4796. * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  4797. * "Friday", "Saturday"]
  4798. */
  4799. weekdays: [
  4800. 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
  4801. 'Thursday', 'Friday', 'Saturday'
  4802. ],
  4803. /**
  4804. * Short week days, starting Sunday. If not specified, Highcharts uses
  4805. * the first three letters of the `lang.weekdays` option.
  4806. *
  4807. * @sample highcharts/lang/shortweekdays/
  4808. * Finnish two-letter abbreviations
  4809. *
  4810. * @type {Array<string>}
  4811. * @since 4.2.4
  4812. * @apioption lang.shortWeekdays
  4813. */
  4814. /**
  4815. * What to show in a date field for invalid dates. Defaults to an empty
  4816. * string.
  4817. *
  4818. * @type {string}
  4819. * @since 4.1.8
  4820. * @product highcharts highstock
  4821. * @apioption lang.invalidDate
  4822. */
  4823. /**
  4824. * The title appearing on hovering the zoom in button. The text itself
  4825. * defaults to "+" and can be changed in the button options.
  4826. *
  4827. * @type {string}
  4828. * @default Zoom in
  4829. * @product highmaps
  4830. * @apioption lang.zoomIn
  4831. */
  4832. /**
  4833. * The title appearing on hovering the zoom out button. The text itself
  4834. * defaults to "-" and can be changed in the button options.
  4835. *
  4836. * @type {string}
  4837. * @default Zoom out
  4838. * @product highmaps
  4839. * @apioption lang.zoomOut
  4840. */
  4841. /**
  4842. * The default decimal point used in the `Highcharts.numberFormat`
  4843. * method unless otherwise specified in the function arguments.
  4844. *
  4845. * @since 1.2.2
  4846. */
  4847. decimalPoint: '.',
  4848. /**
  4849. * [Metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix) used
  4850. * to shorten high numbers in axis labels. Replacing any of the
  4851. * positions with `null` causes the full number to be written. Setting
  4852. * `numericSymbols` to `null` disables shortening altogether.
  4853. *
  4854. * @sample {highcharts} highcharts/lang/numericsymbols/
  4855. * Replacing the symbols with text
  4856. * @sample {highstock} highcharts/lang/numericsymbols/
  4857. * Replacing the symbols with text
  4858. *
  4859. * @type {Array<string>}
  4860. * @default ["k", "M", "G", "T", "P", "E"]
  4861. * @since 2.3.0
  4862. */
  4863. numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
  4864. /**
  4865. * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
  4866. * Use 10000 for Japanese, Korean and various Chinese locales, which
  4867. * use symbols for 10^4, 10^8 and 10^12.
  4868. *
  4869. * @sample highcharts/lang/numericsymbolmagnitude/
  4870. * 10000 magnitude for Japanese
  4871. *
  4872. * @type {number}
  4873. * @default 1000
  4874. * @since 5.0.3
  4875. * @apioption lang.numericSymbolMagnitude
  4876. */
  4877. /**
  4878. * The text for the label appearing when a chart is zoomed.
  4879. *
  4880. * @since 1.2.4
  4881. */
  4882. resetZoom: 'Reset zoom',
  4883. /**
  4884. * The tooltip title for the label appearing when a chart is zoomed.
  4885. *
  4886. * @since 1.2.4
  4887. */
  4888. resetZoomTitle: 'Reset zoom level 1:1',
  4889. /**
  4890. * The default thousands separator used in the `Highcharts.numberFormat`
  4891. * method unless otherwise specified in the function arguments. Defaults
  4892. * to a single space character, which is recommended in
  4893. * [ISO 31-0](https://en.wikipedia.org/wiki/ISO_31-0#Numbers) and works
  4894. * across Anglo-American and continental European languages.
  4895. *
  4896. * @default \u0020
  4897. * @since 1.2.2
  4898. */
  4899. thousandsSep: ' '
  4900. },
  4901. /**
  4902. * Global options that don't apply to each chart. These options, like
  4903. * the `lang` options, must be set using the `Highcharts.setOptions`
  4904. * method.
  4905. *
  4906. * ```js
  4907. * Highcharts.setOptions({
  4908. * global: {
  4909. * useUTC: false
  4910. * }
  4911. * });
  4912. * ```
  4913. */
  4914. /**
  4915. * _Canvg rendering for Android 2.x is removed as of Highcharts 5.0\.
  4916. * Use the [libURL](#exporting.libURL) option to configure exporting._
  4917. *
  4918. * The URL to the additional file to lazy load for Android 2.x devices.
  4919. * These devices don't support SVG, so we download a helper file that
  4920. * contains [canvg](https://github.com/canvg/canvg), its dependency
  4921. * rbcolor, and our own CanVG Renderer class. To avoid hotlinking to
  4922. * our site, you can install canvas-tools.js on your own server and
  4923. * change this option accordingly.
  4924. *
  4925. * @deprecated
  4926. *
  4927. * @type {string}
  4928. * @default https://code.highcharts.com/{version}/modules/canvas-tools.js
  4929. * @product highcharts highmaps
  4930. * @apioption global.canvasToolsURL
  4931. */
  4932. /**
  4933. * This option is deprecated since v6.0.5. Instead, use
  4934. * [time.useUTC](#time.useUTC) that supports individual time settings
  4935. * per chart.
  4936. *
  4937. * @deprecated
  4938. *
  4939. * @type {boolean}
  4940. * @apioption global.useUTC
  4941. */
  4942. /**
  4943. * This option is deprecated since v6.0.5. Instead, use
  4944. * [time.Date](#time.Date) that supports individual time settings
  4945. * per chart.
  4946. *
  4947. * @deprecated
  4948. *
  4949. * @type {Function}
  4950. * @product highcharts highstock
  4951. * @apioption global.Date
  4952. */
  4953. /**
  4954. * This option is deprecated since v6.0.5. Instead, use
  4955. * [time.getTimezoneOffset](#time.getTimezoneOffset) that supports
  4956. * individual time settings per chart.
  4957. *
  4958. * @deprecated
  4959. *
  4960. * @type {Function}
  4961. * @product highcharts highstock
  4962. * @apioption global.getTimezoneOffset
  4963. */
  4964. /**
  4965. * This option is deprecated since v6.0.5. Instead, use
  4966. * [time.timezone](#time.timezone) that supports individual time
  4967. * settings per chart.
  4968. *
  4969. * @deprecated
  4970. *
  4971. * @type {string}
  4972. * @product highcharts highstock
  4973. * @apioption global.timezone
  4974. */
  4975. /**
  4976. * This option is deprecated since v6.0.5. Instead, use
  4977. * [time.timezoneOffset](#time.timezoneOffset) that supports individual
  4978. * time settings per chart.
  4979. *
  4980. * @deprecated
  4981. *
  4982. * @type {number}
  4983. * @product highcharts highstock
  4984. * @apioption global.timezoneOffset
  4985. */
  4986. global: {},
  4987. /**
  4988. * Time options that can apply globally or to individual charts. These
  4989. * settings affect how `datetime` axes are laid out, how tooltips are
  4990. * formatted, how series
  4991. * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
  4992. * the Highcharts Stock range selector handles time.
  4993. *
  4994. * The common use case is that all charts in the same Highcharts object
  4995. * share the same time settings, in which case the global settings are set
  4996. * using `setOptions`.
  4997. *
  4998. * ```js
  4999. * // Apply time settings globally
  5000. * Highcharts.setOptions({
  5001. * time: {
  5002. * timezone: 'Europe/London'
  5003. * }
  5004. * });
  5005. * // Apply time settings by instance
  5006. * let chart = Highcharts.chart('container', {
  5007. * time: {
  5008. * timezone: 'America/New_York'
  5009. * },
  5010. * series: [{
  5011. * data: [1, 4, 3, 5]
  5012. * }]
  5013. * });
  5014. *
  5015. * // Use the Time object
  5016. * console.log(
  5017. * 'Current time in New York',
  5018. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  5019. * );
  5020. * ```
  5021. *
  5022. * Since v6.0.5, the time options were moved from the `global` obect to the
  5023. * `time` object, and time options can be set on each individual chart.
  5024. *
  5025. * @sample {highcharts|highstock}
  5026. * highcharts/time/timezone/
  5027. * Set the timezone globally
  5028. * @sample {highcharts}
  5029. * highcharts/time/individual/
  5030. * Set the timezone per chart instance
  5031. * @sample {highstock}
  5032. * stock/time/individual/
  5033. * Set the timezone per chart instance
  5034. *
  5035. * @since 6.0.5
  5036. * @optionparent time
  5037. */
  5038. time: {
  5039. /**
  5040. * A custom `Date` class for advanced date handling. For example,
  5041. * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
  5042. * handle Jalali dates.
  5043. *
  5044. * @type {*}
  5045. * @since 4.0.4
  5046. * @product highcharts highstock gantt
  5047. */
  5048. Date: void 0,
  5049. /**
  5050. * A callback to return the time zone offset for a given datetime. It
  5051. * takes the timestamp in terms of milliseconds since January 1 1970,
  5052. * and returns the timezone offset in minutes. This provides a hook
  5053. * for drawing time based charts in specific time zones using their
  5054. * local DST crossover dates, with the help of external libraries.
  5055. *
  5056. * @see [global.timezoneOffset](#global.timezoneOffset)
  5057. *
  5058. * @sample {highcharts|highstock} highcharts/time/gettimezoneoffset/
  5059. * Use moment.js to draw Oslo time regardless of browser locale
  5060. *
  5061. * @type {Highcharts.TimezoneOffsetCallbackFunction}
  5062. * @since 4.1.0
  5063. * @product highcharts highstock gantt
  5064. */
  5065. getTimezoneOffset: void 0,
  5066. /**
  5067. * Requires [moment.js](https://momentjs.com/). If the timezone option
  5068. * is specified, it creates a default
  5069. * [getTimezoneOffset](#time.getTimezoneOffset) function that looks
  5070. * up the specified timezone in moment.js. If moment.js is not included,
  5071. * this throws a Highcharts error in the console, but does not crash the
  5072. * chart.
  5073. *
  5074. * @see [getTimezoneOffset](#time.getTimezoneOffset)
  5075. *
  5076. * @sample {highcharts|highstock} highcharts/time/timezone/
  5077. * Europe/Oslo
  5078. *
  5079. * @type {string}
  5080. * @since 5.0.7
  5081. * @product highcharts highstock gantt
  5082. */
  5083. timezone: void 0,
  5084. /**
  5085. * The timezone offset in minutes. Positive values are west, negative
  5086. * values are east of UTC, as in the ECMAScript
  5087. * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
  5088. * method. Use this to display UTC based data in a predefined time zone.
  5089. *
  5090. * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
  5091. *
  5092. * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
  5093. * Timezone offset
  5094. *
  5095. * @since 3.0.8
  5096. * @product highcharts highstock gantt
  5097. */
  5098. timezoneOffset: 0,
  5099. /**
  5100. * Whether to use UTC time for axis scaling, tickmark placement and
  5101. * time display in `Highcharts.dateFormat`. Advantages of using UTC
  5102. * is that the time displays equally regardless of the user agent's
  5103. * time zone settings. Local time can be used when the data is loaded
  5104. * in real time or when correct Daylight Saving Time transitions are
  5105. * required.
  5106. *
  5107. * @sample {highcharts} highcharts/time/useutc-true/
  5108. * True by default
  5109. * @sample {highcharts} highcharts/time/useutc-false/
  5110. * False
  5111. */
  5112. useUTC: true
  5113. },
  5114. chart: ChartDefaults,
  5115. /**
  5116. * The chart's main title.
  5117. *
  5118. * @sample {highmaps} maps/title/title/
  5119. * Title options demonstrated
  5120. */
  5121. title: {
  5122. /**
  5123. * When the title is floating, the plot area will not move to make space
  5124. * for it.
  5125. *
  5126. * @sample {highcharts} highcharts/chart/zoomtype-none/
  5127. * False by default
  5128. * @sample {highcharts} highcharts/title/floating/
  5129. * True - title on top of the plot area
  5130. * @sample {highstock} stock/chart/title-floating/
  5131. * True - title on top of the plot area
  5132. *
  5133. * @type {boolean}
  5134. * @default false
  5135. * @since 2.1
  5136. * @apioption title.floating
  5137. */
  5138. /**
  5139. * Whether to
  5140. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  5141. * to render the text.
  5142. *
  5143. * @type {boolean}
  5144. * @default false
  5145. * @apioption title.useHTML
  5146. */
  5147. /**
  5148. * The vertical alignment of the title. Can be one of `"top"`,
  5149. * `"middle"` and `"bottom"`. When a value is given, the title behaves
  5150. * as if [floating](#title.floating) were `true`.
  5151. *
  5152. * @sample {highcharts} highcharts/title/verticalalign/
  5153. * Chart title in bottom right corner
  5154. * @sample {highstock} stock/chart/title-verticalalign/
  5155. * Chart title in bottom right corner
  5156. *
  5157. * @type {Highcharts.VerticalAlignValue}
  5158. * @since 2.1
  5159. * @apioption title.verticalAlign
  5160. */
  5161. /**
  5162. * The x position of the title relative to the alignment within
  5163. * `chart.spacingLeft` and `chart.spacingRight`.
  5164. *
  5165. * @sample {highcharts} highcharts/title/align/
  5166. * Aligned to the plot area (x = 70px = margin left - spacing
  5167. * left)
  5168. * @sample {highstock} stock/chart/title-align/
  5169. * Aligned to the plot area (x = 50px = margin left - spacing
  5170. * left)
  5171. *
  5172. * @type {number}
  5173. * @default 0
  5174. * @since 2.0
  5175. * @apioption title.x
  5176. */
  5177. /**
  5178. * The y position of the title relative to the alignment within
  5179. * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
  5180. * #chart.spacingBottom). By default it depends on the font size.
  5181. *
  5182. * @sample {highcharts} highcharts/title/y/
  5183. * Title inside the plot area
  5184. * @sample {highstock} stock/chart/title-verticalalign/
  5185. * Chart title in bottom right corner
  5186. *
  5187. * @type {number}
  5188. * @since 2.0
  5189. * @apioption title.y
  5190. */
  5191. /**
  5192. * CSS styles for the title. Use this for font styling, but use `align`,
  5193. * `x` and `y` for text alignment.
  5194. *
  5195. * In styled mode, the title style is given in the `.highcharts-title`
  5196. * class.
  5197. *
  5198. * @sample {highcharts} highcharts/title/style/
  5199. * Custom color and weight
  5200. * @sample {highstock} stock/chart/title-style/
  5201. * Custom color and weight
  5202. * @sample highcharts/css/titles/
  5203. * Styled mode
  5204. *
  5205. * @type {Highcharts.CSSObject}
  5206. * @default {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
  5207. * @default {highstock} { "color": "#333333", "fontSize": "16px" }
  5208. */
  5209. style: {
  5210. color: "#333333" /* Palette.neutralColor80 */,
  5211. fontWeight: 'bold'
  5212. },
  5213. /**
  5214. * The title of the chart. To disable the title, set the `text` to
  5215. * `undefined`.
  5216. *
  5217. * @sample {highcharts} highcharts/title/text/
  5218. * Custom title
  5219. * @sample {highstock} stock/chart/title-text/
  5220. * Custom title
  5221. *
  5222. * @default {highcharts|highmaps} Chart title
  5223. * @default {highstock} undefined
  5224. */
  5225. text: 'Chart title',
  5226. /**
  5227. * The horizontal alignment of the title. Can be one of "left", "center"
  5228. * and "right".
  5229. *
  5230. * @sample {highcharts} highcharts/title/align/
  5231. * Aligned to the plot area (x = 70px = margin left - spacing
  5232. * left)
  5233. * @sample {highstock} stock/chart/title-align/
  5234. * Aligned to the plot area (x = 50px = margin left - spacing
  5235. * left)
  5236. *
  5237. * @type {Highcharts.AlignValue}
  5238. * @since 2.0
  5239. */
  5240. align: 'center',
  5241. /**
  5242. * The margin between the title and the plot area, or if a subtitle
  5243. * is present, the margin between the subtitle and the plot area.
  5244. *
  5245. * @sample {highcharts} highcharts/title/margin-50/
  5246. * A chart title margin of 50
  5247. * @sample {highcharts} highcharts/title/margin-subtitle/
  5248. * The same margin applied with a subtitle
  5249. * @sample {highstock} stock/chart/title-margin/
  5250. * A chart title margin of 50
  5251. *
  5252. * @since 2.1
  5253. */
  5254. margin: 15,
  5255. /**
  5256. * Adjustment made to the title width, normally to reserve space for
  5257. * the exporting burger menu.
  5258. *
  5259. * @sample highcharts/title/widthadjust/
  5260. * Wider menu, greater padding
  5261. *
  5262. * @since 4.2.5
  5263. */
  5264. widthAdjust: -44
  5265. },
  5266. /**
  5267. * The chart's subtitle. This can be used both to display a subtitle below
  5268. * the main title, and to display random text anywhere in the chart. The
  5269. * subtitle can be updated after chart initialization through the
  5270. * `Chart.setTitle` method.
  5271. *
  5272. * @sample {highmaps} maps/title/subtitle/
  5273. * Subtitle options demonstrated
  5274. */
  5275. subtitle: {
  5276. /**
  5277. * When the subtitle is floating, the plot area will not move to make
  5278. * space for it.
  5279. *
  5280. * @sample {highcharts} highcharts/subtitle/floating/
  5281. * Floating title and subtitle
  5282. * @sample {highstock} stock/chart/subtitle-footnote
  5283. * Footnote floating at bottom right of plot area
  5284. *
  5285. * @type {boolean}
  5286. * @default false
  5287. * @since 2.1
  5288. * @apioption subtitle.floating
  5289. */
  5290. /**
  5291. * CSS styles for the title.
  5292. *
  5293. * In styled mode, the subtitle style is given in the
  5294. * `.highcharts-subtitle` class.
  5295. *
  5296. * @sample {highcharts} highcharts/subtitle/style/
  5297. * Custom color and weight
  5298. * @sample {highcharts} highcharts/css/titles/
  5299. * Styled mode
  5300. * @sample {highstock} stock/chart/subtitle-style
  5301. * Custom color and weight
  5302. * @sample {highstock} highcharts/css/titles/
  5303. * Styled mode
  5304. * @sample {highmaps} highcharts/css/titles/
  5305. * Styled mode
  5306. *
  5307. * @type {Highcharts.CSSObject}
  5308. * @default {"color": "#666666"}
  5309. * @apioption subtitle.style
  5310. */
  5311. /**
  5312. * Whether to
  5313. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  5314. * to render the text.
  5315. *
  5316. * @type {boolean}
  5317. * @default false
  5318. * @apioption subtitle.useHTML
  5319. */
  5320. /**
  5321. * The vertical alignment of the title. Can be one of `"top"`,
  5322. * `"middle"` and `"bottom"`. When middle, the subtitle behaves as
  5323. * floating.
  5324. *
  5325. * @sample {highcharts} highcharts/subtitle/verticalalign/
  5326. * Footnote at the bottom right of plot area
  5327. * @sample {highstock} stock/chart/subtitle-footnote
  5328. * Footnote at the bottom right of plot area
  5329. *
  5330. * @type {Highcharts.VerticalAlignValue}
  5331. * @since 2.1
  5332. * @apioption subtitle.verticalAlign
  5333. */
  5334. /**
  5335. * The x position of the subtitle relative to the alignment within
  5336. * `chart.spacingLeft` and `chart.spacingRight`.
  5337. *
  5338. * @sample {highcharts} highcharts/subtitle/align/
  5339. * Footnote at right of plot area
  5340. * @sample {highstock} stock/chart/subtitle-footnote
  5341. * Footnote at the bottom right of plot area
  5342. *
  5343. * @type {number}
  5344. * @default 0
  5345. * @since 2.0
  5346. * @apioption subtitle.x
  5347. */
  5348. /**
  5349. * The y position of the subtitle relative to the alignment within
  5350. * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
  5351. * is laid out below the title unless the title is floating.
  5352. *
  5353. * @sample {highcharts} highcharts/subtitle/verticalalign/
  5354. * Footnote at the bottom right of plot area
  5355. * @sample {highstock} stock/chart/subtitle-footnote
  5356. * Footnote at the bottom right of plot area
  5357. *
  5358. * @type {number}
  5359. * @since 2.0
  5360. * @apioption subtitle.y
  5361. */
  5362. /**
  5363. * CSS styles for the title.
  5364. *
  5365. * In styled mode, the subtitle style is given in the
  5366. * `.highcharts-subtitle` class.
  5367. *
  5368. * @sample {highcharts} highcharts/subtitle/style/
  5369. * Custom color and weight
  5370. * @sample {highcharts} highcharts/css/titles/
  5371. * Styled mode
  5372. * @sample {highstock} stock/chart/subtitle-style
  5373. * Custom color and weight
  5374. * @sample {highstock} highcharts/css/titles/
  5375. * Styled mode
  5376. * @sample {highmaps} highcharts/css/titles/
  5377. * Styled mode
  5378. *
  5379. * @type {Highcharts.CSSObject}
  5380. * @default {"color": "#666666"}
  5381. */
  5382. style: {
  5383. color: "#666666" /* Palette.neutralColor60 */,
  5384. fontSize: '0.8em'
  5385. },
  5386. /**
  5387. * The subtitle of the chart.
  5388. *
  5389. * @sample {highcharts|highstock} highcharts/subtitle/text/
  5390. * Custom subtitle
  5391. * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
  5392. * Formatted and linked text.
  5393. */
  5394. text: '',
  5395. /**
  5396. * The horizontal alignment of the subtitle. Can be one of "left",
  5397. * "center" and "right".
  5398. *
  5399. * @sample {highcharts} highcharts/subtitle/align/
  5400. * Footnote at right of plot area
  5401. * @sample {highstock} stock/chart/subtitle-footnote
  5402. * Footnote at bottom right of plot area
  5403. *
  5404. * @type {Highcharts.AlignValue}
  5405. * @since 2.0
  5406. */
  5407. align: 'center',
  5408. /**
  5409. * Adjustment made to the subtitle width, normally to reserve space
  5410. * for the exporting burger menu.
  5411. *
  5412. * @see [title.widthAdjust](#title.widthAdjust)
  5413. *
  5414. * @sample highcharts/title/widthadjust/
  5415. * Wider menu, greater padding
  5416. *
  5417. * @since 4.2.5
  5418. */
  5419. widthAdjust: -44
  5420. },
  5421. /**
  5422. * The chart's caption, which will render below the chart and will be part
  5423. * of exported charts. The caption can be updated after chart initialization
  5424. * through the `Chart.update` or `Chart.caption.update` methods.
  5425. *
  5426. * @sample highcharts/caption/text/
  5427. * A chart with a caption
  5428. * @since 7.2.0
  5429. */
  5430. caption: {
  5431. /**
  5432. * When the caption is floating, the plot area will not move to make
  5433. * space for it.
  5434. *
  5435. * @type {boolean}
  5436. * @default false
  5437. * @apioption caption.floating
  5438. */
  5439. /**
  5440. * The margin between the caption and the plot area.
  5441. */
  5442. margin: 15,
  5443. /**
  5444. * Whether to
  5445. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  5446. * to render the text.
  5447. *
  5448. * @type {boolean}
  5449. * @default false
  5450. * @apioption caption.useHTML
  5451. */
  5452. /**
  5453. * The x position of the caption relative to the alignment within
  5454. * `chart.spacingLeft` and `chart.spacingRight`.
  5455. *
  5456. * @type {number}
  5457. * @default 0
  5458. * @apioption caption.x
  5459. */
  5460. /**
  5461. * The y position of the caption relative to the alignment within
  5462. * `chart.spacingTop` and `chart.spacingBottom`.
  5463. *
  5464. * @type {number}
  5465. * @apioption caption.y
  5466. */
  5467. /**
  5468. * CSS styles for the caption.
  5469. *
  5470. * In styled mode, the caption style is given in the
  5471. * `.highcharts-caption` class.
  5472. *
  5473. * @sample {highcharts} highcharts/css/titles/
  5474. * Styled mode
  5475. *
  5476. * @type {Highcharts.CSSObject}
  5477. * @default {"color": "#666666"}
  5478. */
  5479. style: {
  5480. color: "#666666" /* Palette.neutralColor60 */,
  5481. fontSize: '0.8em'
  5482. },
  5483. /**
  5484. * The caption text of the chart.
  5485. *
  5486. * @sample {highcharts} highcharts/caption/text/
  5487. * Custom caption
  5488. */
  5489. text: '',
  5490. /**
  5491. * The horizontal alignment of the caption. Can be one of "left",
  5492. * "center" and "right".
  5493. *
  5494. * @type {Highcharts.AlignValue}
  5495. */
  5496. align: 'left',
  5497. /**
  5498. * The vertical alignment of the caption. Can be one of `"top"`,
  5499. * `"middle"` and `"bottom"`. When middle, the caption behaves as
  5500. * floating.
  5501. *
  5502. * @type {Highcharts.VerticalAlignValue}
  5503. */
  5504. verticalAlign: 'bottom'
  5505. },
  5506. /**
  5507. * The plotOptions is a wrapper object for config objects for each series
  5508. * type. The config objects for each series can also be overridden for
  5509. * each series item as given in the series array.
  5510. *
  5511. * Configuration options for the series are given in three levels. Options
  5512. * for all series in a chart are given in the [plotOptions.series](
  5513. * #plotOptions.series) object. Then options for all series of a specific
  5514. * type are given in the plotOptions of that type, for example
  5515. * `plotOptions.line`. Next, options for one single series are given in
  5516. * [the series array](#series).
  5517. */
  5518. plotOptions: {},
  5519. /**
  5520. * The legend is a box containing a symbol and name for each series
  5521. * item or point item in the chart. Each series (or points in case
  5522. * of pie charts) is represented by a symbol and its name in the legend.
  5523. *
  5524. * It is possible to override the symbol creator function and create
  5525. * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
  5526. *
  5527. * @productdesc {highmaps}
  5528. * A Highmaps legend by default contains one legend item per series, but if
  5529. * a `colorAxis` is defined, the axis will be displayed in the legend.
  5530. * Either as a gradient, or as multiple legend items for `dataClasses`.
  5531. */
  5532. legend: {
  5533. /**
  5534. * The background color of the legend.
  5535. *
  5536. * @see In styled mode, the legend background fill can be applied with
  5537. * the `.highcharts-legend-box` class.
  5538. *
  5539. * @sample {highcharts} highcharts/legend/backgroundcolor/
  5540. * Yellowish background
  5541. * @sample {highstock} stock/legend/align/
  5542. * Various legend options
  5543. * @sample {highmaps} maps/legend/border-background/
  5544. * Border and background options
  5545. *
  5546. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5547. * @apioption legend.backgroundColor
  5548. */
  5549. /**
  5550. * The width of the drawn border around the legend.
  5551. *
  5552. * @see In styled mode, the legend border stroke width can be applied
  5553. * with the `.highcharts-legend-box` class.
  5554. *
  5555. * @sample {highcharts} highcharts/legend/borderwidth/
  5556. * 2px border width
  5557. * @sample {highstock} stock/legend/align/
  5558. * Various legend options
  5559. * @sample {highmaps} maps/legend/border-background/
  5560. * Border and background options
  5561. *
  5562. * @type {number}
  5563. * @default 0
  5564. * @apioption legend.borderWidth
  5565. */
  5566. /**
  5567. * Enable or disable the legend. There is also a series-specific option,
  5568. * [showInLegend](#plotOptions.series.showInLegend), that can hide the
  5569. * series from the legend. In some series types this is `false` by
  5570. * default, so it must set to `true` in order to show the legend for the
  5571. * series.
  5572. *
  5573. * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
  5574. * @sample {highstock} stock/legend/align/ Various legend options
  5575. * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
  5576. *
  5577. * @default {highstock} false
  5578. * @default {highmaps} true
  5579. * @default {gantt} false
  5580. */
  5581. enabled: true,
  5582. /**
  5583. * The horizontal alignment of the legend box within the chart area.
  5584. * Valid values are `left`, `center` and `right`.
  5585. *
  5586. * In the case that the legend is aligned in a corner position, the
  5587. * `layout` option will determine whether to place it above/below
  5588. * or on the side of the plot area.
  5589. *
  5590. * @sample {highcharts} highcharts/legend/align/
  5591. * Legend at the right of the chart
  5592. * @sample {highstock} stock/legend/align/
  5593. * Various legend options
  5594. * @sample {highmaps} maps/legend/alignment/
  5595. * Legend alignment
  5596. *
  5597. * @type {Highcharts.AlignValue}
  5598. * @since 2.0
  5599. */
  5600. align: 'center',
  5601. /**
  5602. * If the [layout](legend.layout) is `horizontal` and the legend items
  5603. * span over two lines or more, whether to align the items into vertical
  5604. * columns. Setting this to `false` makes room for more items, but will
  5605. * look more messy.
  5606. *
  5607. * @since 6.1.0
  5608. */
  5609. alignColumns: true,
  5610. /**
  5611. * A CSS class name to apply to the legend group.
  5612. */
  5613. className: 'highcharts-no-tooltip',
  5614. /**
  5615. * When the legend is floating, the plot area ignores it and is allowed
  5616. * to be placed below it.
  5617. *
  5618. * @sample {highcharts} highcharts/legend/floating-false/
  5619. * False by default
  5620. * @sample {highcharts} highcharts/legend/floating-true/
  5621. * True
  5622. * @sample {highmaps} maps/legend/alignment/
  5623. * Floating legend
  5624. *
  5625. * @type {boolean}
  5626. * @default false
  5627. * @since 2.1
  5628. * @apioption legend.floating
  5629. */
  5630. /**
  5631. * The layout of the legend items. Can be one of `horizontal` or
  5632. * `vertical` or `proximate`. When `proximate`, the legend items will be
  5633. * placed as close as possible to the graphs they're representing,
  5634. * except in inverted charts or when the legend position doesn't allow
  5635. * it.
  5636. *
  5637. * @sample {highcharts} highcharts/legend/layout-horizontal/
  5638. * Horizontal by default
  5639. * @sample {highcharts} highcharts/legend/layout-vertical/
  5640. * Vertical
  5641. * @sample highcharts/legend/layout-proximate
  5642. * Labels proximate to the data
  5643. * @sample {highstock} stock/legend/layout-horizontal/
  5644. * Horizontal by default
  5645. * @sample {highmaps} maps/legend/padding-itemmargin/
  5646. * Vertical with data classes
  5647. * @sample {highmaps} maps/legend/layout-vertical/
  5648. * Vertical with color axis gradient
  5649. *
  5650. * @validvalue ["horizontal", "vertical", "proximate"]
  5651. */
  5652. layout: 'horizontal',
  5653. /**
  5654. * In a legend with horizontal layout, the itemDistance defines the
  5655. * pixel distance between each item.
  5656. *
  5657. * @sample {highcharts} highcharts/legend/layout-horizontal/
  5658. * 50px item distance
  5659. * @sample {highstock} highcharts/legend/layout-horizontal/
  5660. * 50px item distance
  5661. *
  5662. * @type {number}
  5663. * @default {highcharts} 20
  5664. * @default {highstock} 20
  5665. * @default {highmaps} 8
  5666. * @since 3.0.3
  5667. * @apioption legend.itemDistance
  5668. */
  5669. /**
  5670. * The pixel bottom margin for each legend item.
  5671. *
  5672. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  5673. * Padding and item margins demonstrated
  5674. * @sample {highmaps} maps/legend/padding-itemmargin/
  5675. * Padding and item margins demonstrated
  5676. *
  5677. * @since 2.2.0
  5678. */
  5679. itemMarginBottom: 2,
  5680. /**
  5681. * The pixel top margin for each legend item.
  5682. *
  5683. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  5684. * Padding and item margins demonstrated
  5685. * @sample {highmaps} maps/legend/padding-itemmargin/
  5686. * Padding and item margins demonstrated
  5687. *
  5688. * @since 2.2.0
  5689. */
  5690. itemMarginTop: 2,
  5691. /**
  5692. * The width for each legend item. By default the items are laid out
  5693. * successively. In a [horizontal layout](legend.layout), if the items
  5694. * are laid out across two rows or more, they will be vertically aligned
  5695. * depending on the [legend.alignColumns](legend.alignColumns) option.
  5696. *
  5697. * @sample {highcharts} highcharts/legend/itemwidth-default/
  5698. * Undefined by default
  5699. * @sample {highcharts} highcharts/legend/itemwidth-80/
  5700. * 80 for aligned legend items
  5701. *
  5702. * @type {number}
  5703. * @since 2.0
  5704. * @apioption legend.itemWidth
  5705. */
  5706. /**
  5707. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  5708. * for each legend label. Available variables relates to properties on
  5709. * the series, or the point in case of pies.
  5710. *
  5711. * @type {string}
  5712. * @default {name}
  5713. * @since 1.3
  5714. * @apioption legend.labelFormat
  5715. */
  5716. /* eslint-disable valid-jsdoc */
  5717. /**
  5718. * Callback function to format each of the series' labels. The `this`
  5719. * keyword refers to the series object, or the point object in case of
  5720. * pie charts. By default the series or point name is printed.
  5721. *
  5722. * @productdesc {highmaps}
  5723. * In Highmaps the context can also be a data class in case of a
  5724. * `colorAxis`.
  5725. *
  5726. * @sample {highcharts} highcharts/legend/labelformatter/
  5727. * Add text
  5728. * @sample {highmaps} maps/legend/labelformatter/
  5729. * Data classes with label formatter
  5730. *
  5731. * @type {Highcharts.FormatterCallbackFunction<Point|Series>}
  5732. */
  5733. labelFormatter: function () {
  5734. /** eslint-enable valid-jsdoc */
  5735. return this.name;
  5736. },
  5737. /**
  5738. * Line height for the legend items. Deprecated as of 2.1\. Instead,
  5739. * the line height for each item can be set using
  5740. * `itemStyle.lineHeight`, and the padding between items using
  5741. * `itemMarginTop` and `itemMarginBottom`.
  5742. *
  5743. * @sample {highcharts} highcharts/legend/lineheight/
  5744. * Setting padding
  5745. *
  5746. * @deprecated
  5747. *
  5748. * @type {number}
  5749. * @default 16
  5750. * @since 2.0
  5751. * @product highcharts gantt
  5752. * @apioption legend.lineHeight
  5753. */
  5754. /**
  5755. * If the plot area sized is calculated automatically and the legend is
  5756. * not floating, the legend margin is the space between the legend and
  5757. * the axis labels or plot area.
  5758. *
  5759. * @sample {highcharts} highcharts/legend/margin-default/
  5760. * 12 pixels by default
  5761. * @sample {highcharts} highcharts/legend/margin-30/
  5762. * 30 pixels
  5763. *
  5764. * @type {number}
  5765. * @default 12
  5766. * @since 2.1
  5767. * @apioption legend.margin
  5768. */
  5769. /**
  5770. * Maximum pixel height for the legend. When the maximum height is
  5771. * extended, navigation will show.
  5772. *
  5773. * @type {number}
  5774. * @since 2.3.0
  5775. * @apioption legend.maxHeight
  5776. */
  5777. /**
  5778. * The color of the drawn border around the legend.
  5779. *
  5780. * @see In styled mode, the legend border stroke can be applied with the
  5781. * `.highcharts-legend-box` class.
  5782. *
  5783. * @sample {highcharts} highcharts/legend/bordercolor/
  5784. * Brown border
  5785. * @sample {highstock} stock/legend/align/
  5786. * Various legend options
  5787. * @sample {highmaps} maps/legend/border-background/
  5788. * Border and background options
  5789. *
  5790. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5791. */
  5792. borderColor: "#999999" /* Palette.neutralColor40 */,
  5793. /**
  5794. * The border corner radius of the legend.
  5795. *
  5796. * @sample {highcharts} highcharts/legend/borderradius-default/
  5797. * Square by default
  5798. * @sample {highcharts} highcharts/legend/borderradius-round/
  5799. * 5px rounded
  5800. * @sample {highmaps} maps/legend/border-background/
  5801. * Border and background options
  5802. */
  5803. borderRadius: 0,
  5804. /**
  5805. * Options for the paging or navigation appearing when the legend is
  5806. * overflown. Navigation works well on screen, but not in static
  5807. * exported images. One way of working around that is to
  5808. * [increase the chart height in
  5809. * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
  5810. */
  5811. navigation: {
  5812. /**
  5813. * How to animate the pages when navigating up or down. A value of
  5814. * `true` applies the default navigation given in the
  5815. * `chart.animation` option. Additional options can be given as an
  5816. * object containing values for easing and duration.
  5817. *
  5818. * @sample {highcharts} highcharts/legend/navigation/
  5819. * Legend page navigation demonstrated
  5820. * @sample {highstock} highcharts/legend/navigation/
  5821. * Legend page navigation demonstrated
  5822. *
  5823. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  5824. * @default true
  5825. * @since 2.2.4
  5826. * @apioption legend.navigation.animation
  5827. */
  5828. /**
  5829. * The pixel size of the up and down arrows in the legend paging
  5830. * navigation.
  5831. *
  5832. * @sample {highcharts} highcharts/legend/navigation/
  5833. * Legend page navigation demonstrated
  5834. * @sample {highstock} highcharts/legend/navigation/
  5835. * Legend page navigation demonstrated
  5836. *
  5837. * @type {number}
  5838. * @default 12
  5839. * @since 2.2.4
  5840. * @apioption legend.navigation.arrowSize
  5841. */
  5842. /**
  5843. * Whether to enable the legend navigation. In most cases, disabling
  5844. * the navigation results in an unwanted overflow.
  5845. *
  5846. * See also the
  5847. * [adapt chart to legend](https://github.com/highcharts/adapt-chart-to-legend)
  5848. * plugin for a solution to extend the chart height to make room for
  5849. * the legend, optionally in exported charts only.
  5850. *
  5851. * @type {boolean}
  5852. * @default true
  5853. * @since 4.2.4
  5854. * @apioption legend.navigation.enabled
  5855. */
  5856. /**
  5857. * Text styles for the legend page navigation.
  5858. *
  5859. * @see In styled mode, the navigation items are styled with the
  5860. * `.highcharts-legend-navigation` class.
  5861. *
  5862. * @sample {highcharts} highcharts/legend/navigation/
  5863. * Legend page navigation demonstrated
  5864. * @sample {highstock} highcharts/legend/navigation/
  5865. * Legend page navigation demonstrated
  5866. *
  5867. * @type {Highcharts.CSSObject}
  5868. * @since 2.2.4
  5869. * @apioption legend.navigation.style
  5870. */
  5871. style: {
  5872. fontSize: '0.8em'
  5873. },
  5874. /**
  5875. * The color for the active up or down arrow in the legend page
  5876. * navigation.
  5877. *
  5878. * @see In styled mode, the active arrow be styled with the
  5879. * `.highcharts-legend-nav-active` class.
  5880. *
  5881. * @sample {highcharts} highcharts/legend/navigation/
  5882. * Legend page navigation demonstrated
  5883. * @sample {highstock} highcharts/legend/navigation/
  5884. * Legend page navigation demonstrated
  5885. *
  5886. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5887. * @since 2.2.4
  5888. */
  5889. activeColor: "#0022ff" /* Palette.highlightColor100 */,
  5890. /**
  5891. * The color of the inactive up or down arrow in the legend page
  5892. * navigation. .
  5893. *
  5894. * @see In styled mode, the inactive arrow be styled with the
  5895. * `.highcharts-legend-nav-inactive` class.
  5896. *
  5897. * @sample {highcharts} highcharts/legend/navigation/
  5898. * Legend page navigation demonstrated
  5899. * @sample {highstock} highcharts/legend/navigation/
  5900. * Legend page navigation demonstrated
  5901. *
  5902. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  5903. * @since 2.2.4
  5904. */
  5905. inactiveColor: "#cccccc" /* Palette.neutralColor20 */
  5906. },
  5907. /**
  5908. * The inner padding of the legend box.
  5909. *
  5910. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  5911. * Padding and item margins demonstrated
  5912. * @sample {highmaps} maps/legend/padding-itemmargin/
  5913. * Padding and item margins demonstrated
  5914. *
  5915. * @type {number}
  5916. * @default 8
  5917. * @since 2.2.0
  5918. * @apioption legend.padding
  5919. */
  5920. /**
  5921. * Whether to reverse the order of the legend items compared to the
  5922. * order of the series or points as defined in the configuration object.
  5923. *
  5924. * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
  5925. * [series.legendIndex](#series.legendIndex)
  5926. *
  5927. * @sample {highcharts} highcharts/legend/reversed/
  5928. * Stacked bar with reversed legend
  5929. *
  5930. * @type {boolean}
  5931. * @default false
  5932. * @since 1.2.5
  5933. * @apioption legend.reversed
  5934. */
  5935. /**
  5936. * Whether to show the symbol on the right side of the text rather than
  5937. * the left side. This is common in Arabic and Hebrew.
  5938. *
  5939. * @sample {highcharts} highcharts/legend/rtl/
  5940. * Symbol to the right
  5941. *
  5942. * @type {boolean}
  5943. * @default false
  5944. * @since 2.2
  5945. * @apioption legend.rtl
  5946. */
  5947. /**
  5948. * CSS styles for the legend area. In the 1.x versions the position
  5949. * of the legend area was determined by CSS. In 2.x, the position is
  5950. * determined by properties like `align`, `verticalAlign`, `x` and `y`,
  5951. * but the styles are still parsed for backwards compatibility.
  5952. *
  5953. * @deprecated
  5954. *
  5955. * @type {Highcharts.CSSObject}
  5956. * @product highcharts highstock
  5957. * @apioption legend.style
  5958. */
  5959. /**
  5960. * CSS styles for each legend item. Only a subset of CSS is supported,
  5961. * notably those options related to text. The default `textOverflow`
  5962. * property makes long texts truncate. Set it to `undefined` to wrap
  5963. * text instead. A `width` property can be added to control the text
  5964. * width.
  5965. *
  5966. * @see In styled mode, the legend items can be styled with the
  5967. * `.highcharts-legend-item` class.
  5968. *
  5969. * @sample {highcharts} highcharts/legend/itemstyle/
  5970. * Bold black text
  5971. * @sample {highmaps} maps/legend/itemstyle/
  5972. * Item text styles
  5973. *
  5974. * @type {Highcharts.CSSObject}
  5975. * @default {"color": "#333333", "cursor": "pointer", "fontSize": "0.75em", "fontWeight": "bold", "textOverflow": "ellipsis"}
  5976. */
  5977. itemStyle: {
  5978. /**
  5979. * @ignore
  5980. */
  5981. color: "#333333" /* Palette.neutralColor80 */,
  5982. /**
  5983. * @ignore
  5984. */
  5985. cursor: 'pointer',
  5986. /**
  5987. * @ignore
  5988. */
  5989. fontSize: '0.8em',
  5990. /**
  5991. * @ignore
  5992. */
  5993. textDecoration: 'none',
  5994. /**
  5995. * @ignore
  5996. */
  5997. textOverflow: 'ellipsis'
  5998. },
  5999. /**
  6000. * CSS styles for each legend item in hover mode. Only a subset of
  6001. * CSS is supported, notably those options related to text. Properties
  6002. * are inherited from `style` unless overridden here.
  6003. *
  6004. * @see In styled mode, the hovered legend items can be styled with
  6005. * the `.highcharts-legend-item:hover` pesudo-class.
  6006. *
  6007. * @sample {highcharts} highcharts/legend/itemhoverstyle/
  6008. * Red on hover
  6009. * @sample {highmaps} maps/legend/itemstyle/
  6010. * Item text styles
  6011. *
  6012. * @type {Highcharts.CSSObject}
  6013. * @default {"color": "#000000"}
  6014. */
  6015. itemHoverStyle: {
  6016. /**
  6017. * @ignore
  6018. */
  6019. color: "#000000" /* Palette.neutralColor100 */
  6020. },
  6021. /**
  6022. * CSS styles for each legend item when the corresponding series or
  6023. * point is hidden. Only a subset of CSS is supported, notably those
  6024. * options related to text. Properties are inherited from `style`
  6025. * unless overridden here.
  6026. *
  6027. * @see In styled mode, the hidden legend items can be styled with
  6028. * the `.highcharts-legend-item-hidden` class.
  6029. *
  6030. * @sample {highcharts} highcharts/legend/itemhiddenstyle/
  6031. * Darker gray color
  6032. *
  6033. * @type {Highcharts.CSSObject}
  6034. * @default {"color": "#cccccc"}
  6035. */
  6036. itemHiddenStyle: {
  6037. /**
  6038. * @ignore
  6039. */
  6040. color: "#666666" /* Palette.neutralColor60 */,
  6041. /**
  6042. * @ignore
  6043. */
  6044. textDecoration: 'line-through'
  6045. },
  6046. /**
  6047. * Whether to apply a drop shadow to the legend. A `backgroundColor`
  6048. * also needs to be applied for this to take effect. The shadow can be
  6049. * an object configuration containing `color`, `offsetX`, `offsetY`,
  6050. * `opacity` and `width`.
  6051. *
  6052. * @sample {highcharts} highcharts/legend/shadow/
  6053. * White background and drop shadow
  6054. * @sample {highstock} stock/legend/align/
  6055. * Various legend options
  6056. * @sample {highmaps} maps/legend/border-background/
  6057. * Border and background options
  6058. *
  6059. * @type {boolean|Highcharts.CSSObject}
  6060. */
  6061. shadow: false,
  6062. /**
  6063. * Default styling for the checkbox next to a legend item when
  6064. * `showCheckbox` is true.
  6065. *
  6066. * @type {Highcharts.CSSObject}
  6067. * @default {"width": "13px", "height": "13px", "position":"absolute"}
  6068. */
  6069. itemCheckboxStyle: {
  6070. /**
  6071. * @ignore
  6072. */
  6073. position: 'absolute',
  6074. /**
  6075. * @ignore
  6076. */
  6077. width: '13px',
  6078. /**
  6079. * @ignore
  6080. */
  6081. height: '13px'
  6082. },
  6083. // itemWidth: undefined,
  6084. /**
  6085. * When this is true, the legend symbol width will be the same as
  6086. * the symbol height, which in turn defaults to the font size of the
  6087. * legend items.
  6088. *
  6089. * @since 5.0.0
  6090. */
  6091. squareSymbol: true,
  6092. /**
  6093. * The pixel height of the symbol for series types that use a rectangle
  6094. * in the legend. Defaults to the font size of legend items.
  6095. *
  6096. * @productdesc {highmaps}
  6097. * In Highmaps, when the symbol is the gradient of a vertical color
  6098. * axis, the height defaults to 200.
  6099. *
  6100. * @sample {highmaps} maps/legend/layout-vertical-sized/
  6101. * Sized vertical gradient
  6102. * @sample {highmaps} maps/legend/padding-itemmargin/
  6103. * No distance between data classes
  6104. *
  6105. * @type {number}
  6106. * @since 3.0.8
  6107. * @apioption legend.symbolHeight
  6108. */
  6109. /**
  6110. * The border radius of the symbol for series types that use a rectangle
  6111. * in the legend. Defaults to half the `symbolHeight`, effectively
  6112. * creating a circle.
  6113. *
  6114. * For color axis scales, it defaults to 3.
  6115. *
  6116. * @sample {highcharts} highcharts/legend/symbolradius/
  6117. * Round symbols
  6118. * @sample {highstock} highcharts/legend/symbolradius/
  6119. * Round symbols
  6120. * @sample {highmaps} highcharts/legend/symbolradius/
  6121. * Round symbols
  6122. *
  6123. * @type {number}
  6124. * @since 3.0.8
  6125. * @apioption legend.symbolRadius
  6126. */
  6127. /**
  6128. * The pixel width of the legend item symbol. When the `squareSymbol`
  6129. * option is set, this defaults to the `symbolHeight`, otherwise 16.
  6130. *
  6131. * @productdesc {highmaps}
  6132. * In Highmaps, when the symbol is the gradient of a horizontal color
  6133. * axis, the width defaults to 200.
  6134. *
  6135. * @sample {highcharts} highcharts/legend/symbolwidth/
  6136. * Greater symbol width and padding
  6137. * @sample {highmaps} maps/legend/padding-itemmargin/
  6138. * Padding and item margins demonstrated
  6139. * @sample {highmaps} maps/legend/layout-vertical-sized/
  6140. * Sized vertical gradient
  6141. *
  6142. * @type {number}
  6143. * @apioption legend.symbolWidth
  6144. */
  6145. /**
  6146. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  6147. * to render the legend item texts.
  6148. *
  6149. * Prior to 4.1.7, when using HTML, [legend.navigation](
  6150. * #legend.navigation) was disabled.
  6151. *
  6152. * @type {boolean}
  6153. * @default false
  6154. * @apioption legend.useHTML
  6155. */
  6156. /**
  6157. * For a color axis with data classes, how many decimals to render in
  6158. * the legend. The default preserves the decimals of the range numbers.
  6159. *
  6160. * @type {number}
  6161. * @default -1
  6162. * @product highcharts highmaps
  6163. * @apioption legend.valueDecimals
  6164. */
  6165. /**
  6166. * For a color axis with data classes, a suffix for the range numbers in
  6167. * the legend.
  6168. *
  6169. * @type {string}
  6170. * @default ''
  6171. * @product highcharts highmaps
  6172. * @apioption legend.valueSuffix
  6173. */
  6174. /**
  6175. * The width of the legend box. If a number is set, it translates to
  6176. * pixels. Since v7.0.2 it allows setting a percent string of the full
  6177. * chart width, for example `40%`.
  6178. *
  6179. * Defaults to the full chart width for legends below or above the
  6180. * chart, half the chart width for legends to the left and right.
  6181. *
  6182. * @sample {highcharts} highcharts/legend/width/
  6183. * Aligned to the plot area
  6184. * @sample {highcharts} highcharts/legend/width-percent/
  6185. * A percent of the chart width
  6186. *
  6187. * @type {number|string}
  6188. * @since 2.0
  6189. * @apioption legend.width
  6190. */
  6191. /**
  6192. * The pixel padding between the legend item symbol and the legend
  6193. * item text.
  6194. *
  6195. * @sample {highcharts} highcharts/legend/symbolpadding/
  6196. * Greater symbol width and padding
  6197. */
  6198. symbolPadding: 5,
  6199. /**
  6200. * The vertical alignment of the legend box. Can be one of `top`,
  6201. * `middle` or `bottom`. Vertical position can be further determined
  6202. * by the `y` option.
  6203. *
  6204. * In the case that the legend is aligned in a corner position, the
  6205. * `layout` option will determine whether to place it above/below
  6206. * or on the side of the plot area.
  6207. *
  6208. * When the [layout](#legend.layout) option is `proximate`, the
  6209. * `verticalAlign` option doesn't apply.
  6210. *
  6211. * @sample {highcharts} highcharts/legend/verticalalign/
  6212. * Legend 100px from the top of the chart
  6213. * @sample {highstock} stock/legend/align/
  6214. * Various legend options
  6215. * @sample {highmaps} maps/legend/alignment/
  6216. * Legend alignment
  6217. *
  6218. * @type {Highcharts.VerticalAlignValue}
  6219. * @since 2.0
  6220. */
  6221. verticalAlign: 'bottom',
  6222. // width: undefined,
  6223. /**
  6224. * The x offset of the legend relative to its horizontal alignment
  6225. * `align` within chart.spacingLeft and chart.spacingRight. Negative
  6226. * x moves it to the left, positive x moves it to the right.
  6227. *
  6228. * @sample {highcharts} highcharts/legend/width/
  6229. * Aligned to the plot area
  6230. *
  6231. * @since 2.0
  6232. */
  6233. x: 0,
  6234. /**
  6235. * The vertical offset of the legend relative to it's vertical alignment
  6236. * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
  6237. * Negative y moves it up, positive y moves it down.
  6238. *
  6239. * @sample {highcharts} highcharts/legend/verticalalign/
  6240. * Legend 100px from the top of the chart
  6241. * @sample {highstock} stock/legend/align/
  6242. * Various legend options
  6243. * @sample {highmaps} maps/legend/alignment/
  6244. * Legend alignment
  6245. *
  6246. * @since 2.0
  6247. */
  6248. y: 0,
  6249. /**
  6250. * A title to be added on top of the legend.
  6251. *
  6252. * @sample {highcharts} highcharts/legend/title/
  6253. * Legend title
  6254. * @sample {highmaps} maps/legend/alignment/
  6255. * Legend with title
  6256. *
  6257. * @since 3.0
  6258. */
  6259. title: {
  6260. /**
  6261. * A text or HTML string for the title.
  6262. *
  6263. * @type {string}
  6264. * @since 3.0
  6265. * @apioption legend.title.text
  6266. */
  6267. /**
  6268. * Generic CSS styles for the legend title.
  6269. *
  6270. * @see In styled mode, the legend title is styled with the
  6271. * `.highcharts-legend-title` class.
  6272. *
  6273. * @type {Highcharts.CSSObject}
  6274. * @default {"fontSize": "0.75em", "fontWeight": "bold"}
  6275. * @since 3.0
  6276. */
  6277. style: {
  6278. /**
  6279. * @ignore
  6280. */
  6281. fontSize: '0.8em',
  6282. /**
  6283. * @ignore
  6284. */
  6285. fontWeight: 'bold'
  6286. }
  6287. }
  6288. },
  6289. /**
  6290. * The loading options control the appearance of the loading screen
  6291. * that covers the plot area on chart operations. This screen only
  6292. * appears after an explicit call to `chart.showLoading()`. It is a
  6293. * utility for developers to communicate to the end user that something
  6294. * is going on, for example while retrieving new data via an XHR connection.
  6295. * The "Loading..." text itself is not part of this configuration
  6296. * object, but part of the `lang` object.
  6297. */
  6298. loading: {
  6299. /**
  6300. * The duration in milliseconds of the fade out effect.
  6301. *
  6302. * @sample highcharts/loading/hideduration/
  6303. * Fade in and out over a second
  6304. *
  6305. * @type {number}
  6306. * @default 100
  6307. * @since 1.2.0
  6308. * @apioption loading.hideDuration
  6309. */
  6310. /**
  6311. * The duration in milliseconds of the fade in effect.
  6312. *
  6313. * @sample highcharts/loading/hideduration/
  6314. * Fade in and out over a second
  6315. *
  6316. * @type {number}
  6317. * @default 100
  6318. * @since 1.2.0
  6319. * @apioption loading.showDuration
  6320. */
  6321. /**
  6322. * CSS styles for the loading label `span`.
  6323. *
  6324. * @see In styled mode, the loading label is styled with the
  6325. * `.highcharts-loading-inner` class.
  6326. *
  6327. * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
  6328. * Vertically centered
  6329. * @sample {highstock} stock/loading/general/
  6330. * Label styles
  6331. *
  6332. * @type {Highcharts.CSSObject}
  6333. * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
  6334. * @since 1.2.0
  6335. */
  6336. labelStyle: {
  6337. /**
  6338. * @ignore
  6339. */
  6340. fontWeight: 'bold',
  6341. /**
  6342. * @ignore
  6343. */
  6344. position: 'relative',
  6345. /**
  6346. * @ignore
  6347. */
  6348. top: '45%'
  6349. },
  6350. /**
  6351. * CSS styles for the loading screen that covers the plot area.
  6352. *
  6353. * In styled mode, the loading label is styled with the
  6354. * `.highcharts-loading` class.
  6355. *
  6356. * @sample {highcharts|highmaps} highcharts/loading/style/
  6357. * Gray plot area, white text
  6358. * @sample {highstock} stock/loading/general/
  6359. * Gray plot area, white text
  6360. *
  6361. * @type {Highcharts.CSSObject}
  6362. * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
  6363. * @since 1.2.0
  6364. */
  6365. style: {
  6366. /**
  6367. * @ignore
  6368. */
  6369. position: 'absolute',
  6370. /**
  6371. * @ignore
  6372. */
  6373. backgroundColor: "#ffffff" /* Palette.backgroundColor */,
  6374. /**
  6375. * @ignore
  6376. */
  6377. opacity: 0.5,
  6378. /**
  6379. * @ignore
  6380. */
  6381. textAlign: 'center'
  6382. }
  6383. },
  6384. /**
  6385. * Options for the tooltip that appears when the user hovers over a
  6386. * series or point.
  6387. *
  6388. * @declare Highcharts.TooltipOptions
  6389. */
  6390. tooltip: {
  6391. /**
  6392. * The color of the tooltip border. When `undefined`, the border takes
  6393. * the color of the corresponding series or point.
  6394. *
  6395. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  6396. * Follow series by default
  6397. * @sample {highcharts} highcharts/tooltip/bordercolor-black/
  6398. * Black border
  6399. * @sample {highstock} stock/tooltip/general/
  6400. * Styled tooltip
  6401. * @sample {highmaps} maps/tooltip/background-border/
  6402. * Background and border demo
  6403. *
  6404. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6405. * @apioption tooltip.borderColor
  6406. */
  6407. /**
  6408. * A CSS class name to apply to the tooltip's container div,
  6409. * allowing unique CSS styling for each chart.
  6410. *
  6411. * @type {string}
  6412. * @apioption tooltip.className
  6413. */
  6414. /**
  6415. * Since 4.1, the crosshair definitions are moved to the Axis object
  6416. * in order for a better separation from the tooltip. See
  6417. * [xAxis.crosshair](#xAxis.crosshair).
  6418. *
  6419. * @sample {highcharts} highcharts/tooltip/crosshairs-x/
  6420. * Enable a crosshair for the x value
  6421. *
  6422. * @deprecated
  6423. *
  6424. * @type {*}
  6425. * @default true
  6426. * @apioption tooltip.crosshairs
  6427. */
  6428. /**
  6429. * Distance from point to tooltip in pixels.
  6430. *
  6431. * @type {number}
  6432. * @default 16
  6433. * @apioption tooltip.distance
  6434. */
  6435. /**
  6436. * Whether the tooltip should follow the mouse as it moves across
  6437. * columns, pie slices and other point types with an extent.
  6438. * By default it behaves this way for pie, polygon, map, sankey
  6439. * and wordcloud series by override in the `plotOptions`
  6440. * for those series types.
  6441. *
  6442. * Does not apply if [split](#tooltip.split) is `true`.
  6443. *
  6444. * For touch moves to behave the same way, [followTouchMove](
  6445. * #tooltip.followTouchMove) must be `true` also.
  6446. *
  6447. * @sample highcharts/tooltip/followpointer/
  6448. * Tooltip follow pointer comparison
  6449. *
  6450. * @type {boolean}
  6451. * @default {highcharts} false
  6452. * @default {highstock} false
  6453. * @default {highmaps} true
  6454. * @since 3.0
  6455. * @apioption tooltip.followPointer
  6456. */
  6457. /**
  6458. * Whether the tooltip should update as the finger moves on a touch
  6459. * device. If this is `true` and [chart.panning](#chart.panning) is
  6460. * set,`followTouchMove` will take over one-finger touches, so the user
  6461. * needs to use two fingers for zooming and panning.
  6462. *
  6463. * Note the difference to [followPointer](#tooltip.followPointer) that
  6464. * only defines the _position_ of the tooltip. If `followPointer` is
  6465. * false in for example a column series, the tooltip will show above or
  6466. * below the column, but as `followTouchMove` is true, the tooltip will
  6467. * jump from column to column as the user swipes across the plot area.
  6468. *
  6469. * @type {boolean}
  6470. * @default {highcharts} true
  6471. * @default {highstock} true
  6472. * @default {highmaps} false
  6473. * @since 3.0.1
  6474. * @apioption tooltip.followTouchMove
  6475. */
  6476. /**
  6477. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  6478. * for the whole tooltip. When format strings are a requirement, it is
  6479. * usually more convenient to use `headerFormat`, `pointFormat` and
  6480. * `footerFormat`, but the `format` option allows combining them into
  6481. * one setting.
  6482. *
  6483. * The context of the format string is the same as that of the
  6484. * `formatter` callback.
  6485. *
  6486. * @sample {highcharts} highcharts/tooltip/format-shared/
  6487. * Format for shared tooltip
  6488. *
  6489. * @type {string}
  6490. * @default undefined
  6491. * @since 11.1.0
  6492. * @apioption tooltip.format
  6493. */
  6494. /**
  6495. * Callback function to format the text of the tooltip from scratch. In
  6496. * case of single or [shared](#tooltip.shared) tooltips, a string should
  6497. * be returned. In case of [split](#tooltip.split) tooltips, it should
  6498. * return an array where the first item is the header, and subsequent
  6499. * items are mapped to the points. Return `false` to disable tooltip for
  6500. * a specific point on series.
  6501. *
  6502. * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
  6503. * the tooltip is parsed and converted to SVG, therefore this isn't a
  6504. * complete HTML renderer. The following HTML tags are supported: `b`,
  6505. * `br`, `em`, `i`, `span`, `strong`. Spans can be styled with a `style`
  6506. * attribute, but only text-related CSS, that is shared with SVG, is
  6507. * handled.
  6508. *
  6509. * The available data in the formatter differ a bit depending on whether
  6510. * the tooltip is shared or split, or belongs to a single point. In a
  6511. * shared/split tooltip, all properties except `x`, which is common for
  6512. * all points, are kept in an array, `this.points`.
  6513. *
  6514. * Available data are:
  6515. *
  6516. * - **this.percentage (not shared) /**
  6517. * **this.points[i].percentage (shared)**:
  6518. * Stacked series and pies only. The point's percentage of the total.
  6519. *
  6520. * - **this.point (not shared) / this.points[i].point (shared)**:
  6521. * The point object. The point name, if defined, is available through
  6522. * `this.point.name`.
  6523. *
  6524. * - **this.points**:
  6525. * In a shared tooltip, this is an array containing all other
  6526. * properties for each point.
  6527. *
  6528. * - **this.series (not shared) / this.points[i].series (shared)**:
  6529. * The series object. The series name is available through
  6530. * `this.series.name`.
  6531. *
  6532. * - **this.total (not shared) / this.points[i].total (shared)**:
  6533. * Stacked series only. The total value at this point's x value.
  6534. *
  6535. * - **this.x**:
  6536. * The x value. This property is the same regardless of the tooltip
  6537. * being shared or not.
  6538. *
  6539. * - **this.y (not shared) / this.points[i].y (shared)**:
  6540. * The y value.
  6541. *
  6542. * @sample {highcharts} highcharts/tooltip/formatter-simple/
  6543. * Simple string formatting
  6544. * @sample {highcharts} highcharts/tooltip/formatter-shared/
  6545. * Formatting with shared tooltip
  6546. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  6547. * Formatting with split tooltip
  6548. * @sample highcharts/tooltip/formatter-conditional-default/
  6549. * Extending default formatter
  6550. * @sample {highstock} stock/tooltip/formatter/
  6551. * Formatting with shared tooltip
  6552. * @sample {highmaps} maps/tooltip/formatter/
  6553. * String formatting
  6554. *
  6555. * @type {Highcharts.TooltipFormatterCallbackFunction}
  6556. * @apioption tooltip.formatter
  6557. */
  6558. /**
  6559. * Callback function to format the text of the tooltip for
  6560. * visible null points.
  6561. * Works analogously to [formatter](#tooltip.formatter).
  6562. *
  6563. * @sample highcharts/plotoptions/series-nullformat
  6564. * Format data label and tooltip for null point.
  6565. *
  6566. * @type {Highcharts.TooltipFormatterCallbackFunction}
  6567. * @apioption tooltip.nullFormatter
  6568. */
  6569. /**
  6570. * Whether to allow the tooltip to render outside the chart's SVG
  6571. * element box. By default (`false`), the tooltip is rendered within the
  6572. * chart's SVG element, which results in the tooltip being aligned
  6573. * inside the chart area. For small charts, this may result in clipping
  6574. * or overlapping. When `true`, a separate SVG element is created and
  6575. * overlaid on the page, allowing the tooltip to be aligned inside the
  6576. * page itself.
  6577. *
  6578. * Defaults to `true` if `chart.scrollablePlotArea` is activated,
  6579. * otherwise `false`.
  6580. *
  6581. * @sample highcharts/tooltip/outside
  6582. * Small charts with tooltips outside
  6583. *
  6584. * @type {boolean|undefined}
  6585. * @default undefined
  6586. * @since 6.1.1
  6587. * @apioption tooltip.outside
  6588. */
  6589. /**
  6590. * A callback function for formatting the HTML output for a single point
  6591. * in the tooltip. Like the `pointFormat` string, but with more
  6592. * flexibility.
  6593. *
  6594. * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
  6595. * @since 4.1.0
  6596. * @context Highcharts.Point
  6597. * @apioption tooltip.pointFormatter
  6598. */
  6599. /**
  6600. * A callback function to place the tooltip in a custom position. The
  6601. * callback receives three parameters: `labelWidth`, `labelHeight` and
  6602. * `point`, where point contains values for `plotX` and `plotY` telling
  6603. * where the reference point is in the plot area. Add `chart.plotLeft`
  6604. * and `chart.plotTop` to get the full coordinates.
  6605. *
  6606. * To find the actual hovered `Point` instance, use
  6607. * `this.chart.hoverPoint`. For shared or split tooltips, all the hover
  6608. * points are available in `this.chart.hoverPoints`.
  6609. *
  6610. * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
  6611. * positioner is called for each of the boxes separately, including
  6612. * xAxis header. xAxis header is not a point, instead `point` argument
  6613. * contains info: `{ plotX: Number, plotY: Number, isHeader: Boolean }`
  6614. *
  6615. * The return should be an object containing x and y values, for example
  6616. * `{ x: 100, y: 100 }`.
  6617. *
  6618. * @sample {highcharts} highcharts/tooltip/positioner/
  6619. * A fixed tooltip position
  6620. * @sample {highstock} stock/tooltip/positioner/
  6621. * A fixed tooltip position on top of the chart
  6622. * @sample {highmaps} maps/tooltip/positioner/
  6623. * A fixed tooltip position
  6624. * @sample {highstock} stock/tooltip/split-positioner/
  6625. * Split tooltip with fixed positions
  6626. * @sample {highstock} stock/tooltip/positioner-scrollable-plotarea/
  6627. * Scrollable plot area combined with tooltip positioner
  6628. *
  6629. * @type {Highcharts.TooltipPositionerCallbackFunction}
  6630. * @since 2.2.4
  6631. * @apioption tooltip.positioner
  6632. */
  6633. /**
  6634. * Split the tooltip into one label per series, with the header close
  6635. * to the axis. This is recommended over [shared](#tooltip.shared)
  6636. * tooltips for charts with multiple line series, generally making them
  6637. * easier to read. This option takes precedence over `tooltip.shared`.
  6638. *
  6639. * Not supported for [polar](#chart.polar) and [inverted](#chart.inverted) charts.
  6640. *
  6641. * @productdesc {highstock} In Highcharts Stock, tooltips are split
  6642. * by default since v6.0.0. Stock charts typically contain
  6643. * multi-dimension points and multiple panes, making split tooltips
  6644. * the preferred layout over
  6645. * the previous `shared` tooltip.
  6646. *
  6647. * @sample highcharts/tooltip/split/
  6648. * Split tooltip
  6649. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  6650. * Split tooltip and custom formatter callback
  6651. *
  6652. * @type {boolean}
  6653. * @default {highcharts} false
  6654. * @default {highstock} true
  6655. * @since 5.0.0
  6656. * @product highcharts highstock
  6657. * @apioption tooltip.split
  6658. */
  6659. /**
  6660. * Prevents the tooltip from switching or closing, when touched or
  6661. * pointed.
  6662. *
  6663. * @sample highcharts/tooltip/stickoncontact/
  6664. * Tooltip sticks on pointer contact
  6665. *
  6666. * @type {boolean}
  6667. * @since 8.0.1
  6668. * @apioption tooltip.stickOnContact
  6669. */
  6670. /**
  6671. * Use HTML to render the contents of the tooltip instead of SVG. Using
  6672. * HTML allows advanced formatting like tables and images in the
  6673. * tooltip. It is also recommended for rtl languages as it works around
  6674. * rtl bugs in early Firefox.
  6675. *
  6676. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  6677. * A table for value alignment
  6678. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  6679. * Full HTML tooltip
  6680. * @sample {highmaps} maps/tooltip/usehtml/
  6681. * Pure HTML tooltip
  6682. *
  6683. * @type {boolean}
  6684. * @default false
  6685. * @since 2.2
  6686. * @apioption tooltip.useHTML
  6687. */
  6688. /**
  6689. * How many decimals to show in each series' y value. This is
  6690. * overridable in each series' tooltip options object. The default is to
  6691. * preserve all decimals.
  6692. *
  6693. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  6694. * Set decimals, prefix and suffix for the value
  6695. * @sample {highmaps} maps/tooltip/valuedecimals/
  6696. * Set decimals, prefix and suffix for the value
  6697. *
  6698. * @type {number|undefined}
  6699. * @since 2.2
  6700. * @apioption tooltip.valueDecimals
  6701. */
  6702. /**
  6703. * A string to prepend to each series' y value. Overridable in each
  6704. * series' tooltip options object.
  6705. *
  6706. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  6707. * Set decimals, prefix and suffix for the value
  6708. * @sample {highmaps} maps/tooltip/valuedecimals/
  6709. * Set decimals, prefix and suffix for the value
  6710. *
  6711. * @type {string}
  6712. * @since 2.2
  6713. * @apioption tooltip.valuePrefix
  6714. */
  6715. /**
  6716. * A string to append to each series' y value. Overridable in each
  6717. * series' tooltip options object.
  6718. *
  6719. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  6720. * Set decimals, prefix and suffix for the value
  6721. * @sample {highmaps} maps/tooltip/valuedecimals/
  6722. * Set decimals, prefix and suffix for the value
  6723. *
  6724. * @type {string}
  6725. * @since 2.2
  6726. * @apioption tooltip.valueSuffix
  6727. */
  6728. /**
  6729. * The format for the date in the tooltip header if the X axis is a
  6730. * datetime axis. The default is a best guess based on the smallest
  6731. * distance between points in the chart.
  6732. *
  6733. * @sample {highcharts} highcharts/tooltip/xdateformat/
  6734. * A different format
  6735. *
  6736. * @type {string}
  6737. * @product highcharts highstock gantt
  6738. * @apioption tooltip.xDateFormat
  6739. */
  6740. /**
  6741. * How many decimals to show for the `point.change`
  6742. * or the `point.cumulativeSum` value when the `series.compare`
  6743. * or the `series.cumulative` option is set.
  6744. * This is overridable in each series' tooltip options object.
  6745. *
  6746. * @type {number}
  6747. * @default 2
  6748. * @since 1.0.1
  6749. * @product highstock
  6750. * @apioption tooltip.changeDecimals
  6751. */
  6752. /**
  6753. * Enable or disable the tooltip.
  6754. *
  6755. * @sample {highcharts} highcharts/tooltip/enabled/
  6756. * Disabled
  6757. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  6758. * Disable tooltip and show values on chart instead
  6759. */
  6760. enabled: true,
  6761. /**
  6762. * Enable or disable animation of the tooltip.
  6763. *
  6764. * @type {boolean}
  6765. * @default true
  6766. * @since 2.3.0
  6767. */
  6768. animation: svg,
  6769. /**
  6770. * The radius of the rounded border corners.
  6771. *
  6772. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  6773. * Default border radius
  6774. * @sample {highcharts} highcharts/tooltip/borderradius-0/
  6775. * Square borders
  6776. * @sample {highmaps} maps/tooltip/background-border/
  6777. * Background and border demo
  6778. */
  6779. borderRadius: 3,
  6780. /**
  6781. * For series on datetime axes, the date format in the tooltip's
  6782. * header will by default be guessed based on the closest data points.
  6783. * This member gives the default string representations used for
  6784. * each unit. For an overview of the replacement codes, see
  6785. * [dateFormat](/class-reference/Highcharts.Time#dateFormat).
  6786. *
  6787. * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
  6788. *
  6789. * @type {Highcharts.Dictionary<string>}
  6790. * @product highcharts highstock gantt
  6791. */
  6792. dateTimeLabelFormats: {
  6793. /** @internal */
  6794. millisecond: '%A, %e %b, %H:%M:%S.%L',
  6795. /** @internal */
  6796. second: '%A, %e %b, %H:%M:%S',
  6797. /** @internal */
  6798. minute: '%A, %e %b, %H:%M',
  6799. /** @internal */
  6800. hour: '%A, %e %b, %H:%M',
  6801. /** @internal */
  6802. day: '%A, %e %b %Y',
  6803. /** @internal */
  6804. week: 'Week from %A, %e %b %Y',
  6805. /** @internal */
  6806. month: '%B %Y',
  6807. /** @internal */
  6808. year: '%Y'
  6809. },
  6810. /**
  6811. * A string to append to the tooltip format.
  6812. *
  6813. * @sample {highcharts} highcharts/tooltip/footerformat/
  6814. * A table for value alignment
  6815. * @sample {highmaps} maps/tooltip/format/
  6816. * Format demo
  6817. *
  6818. * @since 2.2
  6819. */
  6820. footerFormat: '',
  6821. /**
  6822. * The name of a symbol to use for the border around the tooltip
  6823. * header. Applies only when [tooltip.split](#tooltip.split) is
  6824. * enabled.
  6825. *
  6826. * Custom callbacks for symbol path generation can also be added to
  6827. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  6828. * [series.marker.symbol](plotOptions.line.marker.symbol).
  6829. *
  6830. * @see [tooltip.shape](#tooltip.shape)
  6831. *
  6832. * @sample {highstock} stock/tooltip/split-positioner/
  6833. * Different shapes for header and split boxes
  6834. *
  6835. * @type {Highcharts.TooltipShapeValue}
  6836. * @validvalue ["callout", "square"]
  6837. * @since 7.0
  6838. */
  6839. headerShape: 'callout',
  6840. /**
  6841. * The number of milliseconds to wait until the tooltip is hidden when
  6842. * mouse out from a point or chart.
  6843. *
  6844. * @since 3.0
  6845. */
  6846. hideDelay: 500,
  6847. /**
  6848. * Padding inside the tooltip, in pixels.
  6849. *
  6850. * @since 5.0.0
  6851. */
  6852. padding: 8,
  6853. /**
  6854. * The name of a symbol to use for the border around the tooltip. Can
  6855. * be one of: `"callout"`, `"circle"` or `"rect"`. When
  6856. * [tooltip.split](#tooltip.split)
  6857. * option is enabled, shape is applied to all boxes except header, which
  6858. * is controlled by
  6859. * [tooltip.headerShape](#tooltip.headerShape).
  6860. *
  6861. * Custom callbacks for symbol path generation can also be added to
  6862. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  6863. * [series.marker.symbol](plotOptions.line.marker.symbol).
  6864. *
  6865. * @type {Highcharts.TooltipShapeValue}
  6866. * @since 4.0
  6867. */
  6868. shape: 'callout',
  6869. /**
  6870. * When the tooltip is shared, the entire plot area will capture mouse
  6871. * movement or touch events. Tooltip texts for series types with ordered
  6872. * data (not pie, scatter, flags etc) will be shown in a single bubble.
  6873. * This is recommended for single series charts and for tablet/mobile
  6874. * optimized charts.
  6875. *
  6876. * See also [tooltip.split](#tooltip.split), that is better suited for
  6877. * charts with many series, especially line-type series. The
  6878. * `tooltip.split` option takes precedence over `tooltip.shared`.
  6879. *
  6880. * @sample {highcharts} highcharts/tooltip/shared-false/
  6881. * False by default
  6882. * @sample {highcharts} highcharts/tooltip/shared-true/
  6883. * True
  6884. * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
  6885. * True with x axis crosshair
  6886. * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
  6887. * True with mixed series types
  6888. *
  6889. * @since 2.1
  6890. * @product highcharts highstock
  6891. */
  6892. shared: false,
  6893. /**
  6894. * Proximity snap for graphs or single points. It defaults to 10 for
  6895. * mouse-powered devices and 25 for touch devices.
  6896. *
  6897. * Note that in most cases the whole plot area captures the mouse
  6898. * movement, and in these cases `tooltip.snap` doesn't make sense. This
  6899. * applies when [stickyTracking](#plotOptions.series.stickyTracking)
  6900. * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
  6901. * or [split](#tooltip.split).
  6902. *
  6903. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  6904. * 10 px by default
  6905. * @sample {highcharts} highcharts/tooltip/snap-50/
  6906. * 50 px on graph
  6907. *
  6908. * @type {number}
  6909. * @default 10/25
  6910. * @since 1.2.0
  6911. * @product highcharts highstock
  6912. */
  6913. snap: isTouchDevice ? 25 : 10,
  6914. /**
  6915. * The HTML of the tooltip header line. Variables are enclosed by
  6916. * curly brackets. Available variables are `point.key`, `series.name`,
  6917. * `series.color` and other members from the `point` and `series`
  6918. * objects. The `point.key` variable contains the category name, x
  6919. * value or datetime string depending on the type of axis. For datetime
  6920. * axes, the `point.key` date format can be set using
  6921. * `tooltip.xDateFormat`.
  6922. *
  6923. * @sample {highcharts} highcharts/tooltip/footerformat/
  6924. * An HTML table in the tooltip
  6925. * @sample {highstock} highcharts/tooltip/footerformat/
  6926. * An HTML table in the tooltip
  6927. * @sample {highmaps} maps/tooltip/format/
  6928. * Format demo
  6929. *
  6930. * @type {string}
  6931. * @apioption tooltip.headerFormat
  6932. */
  6933. headerFormat: '<span style="font-size: 0.8em">{point.key}</span><br/>',
  6934. /**
  6935. * The HTML of the null point's line in the tooltip. Works analogously
  6936. * to [pointFormat](#tooltip.pointFormat).
  6937. *
  6938. * @sample {highcharts} highcharts/plotoptions/series-nullformat
  6939. * Format data label and tooltip for null point.
  6940. *
  6941. * @type {string}
  6942. * @apioption tooltip.nullFormat
  6943. */
  6944. /**
  6945. * The HTML of the point's line in the tooltip. Variables are enclosed
  6946. * by curly brackets. Available variables are `point.x`, `point.y`,
  6947. * `series.name` and `series.color` and other properties on the same
  6948. * form. Furthermore, `point.y` can be extended by the
  6949. * `tooltip.valuePrefix` and `tooltip.valueSuffix` variables. This can
  6950. * also be overridden for each series, which makes it a good hook for
  6951. * displaying units.
  6952. *
  6953. * In styled mode, the dot is colored by a class name rather
  6954. * than the point color.
  6955. *
  6956. * @sample {highcharts} highcharts/tooltip/pointformat/
  6957. * A different point format with value suffix
  6958. * @sample {highcharts|highstock} highcharts/tooltip/pointformat-extra-information/
  6959. * Show extra information about points in the tooltip
  6960. * @sample {highmaps} maps/tooltip/format/
  6961. * Format demo
  6962. *
  6963. * @type {string}
  6964. * @since 2.2
  6965. * @apioption tooltip.pointFormat
  6966. */
  6967. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
  6968. /**
  6969. * The background color or gradient for the tooltip.
  6970. *
  6971. * In styled mode, the stroke width is set in the
  6972. * `.highcharts-tooltip-box` class.
  6973. *
  6974. * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
  6975. * Yellowish background
  6976. * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
  6977. * Gradient
  6978. * @sample {highcharts} highcharts/css/tooltip-border-background/
  6979. * Tooltip in styled mode
  6980. * @sample {highstock} stock/tooltip/general/
  6981. * Custom tooltip
  6982. * @sample {highstock} highcharts/css/tooltip-border-background/
  6983. * Tooltip in styled mode
  6984. * @sample {highmaps} maps/tooltip/background-border/
  6985. * Background and border demo
  6986. * @sample {highmaps} highcharts/css/tooltip-border-background/
  6987. * Tooltip in styled mode
  6988. *
  6989. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  6990. */
  6991. backgroundColor: "#ffffff" /* Palette.backgroundColor */,
  6992. /**
  6993. * The pixel width of the tooltip border. Defaults to 0 for single
  6994. * tooltips and 1 for split tooltips.
  6995. *
  6996. * In styled mode, the stroke width is set in the
  6997. * `.highcharts-tooltip-box` class.
  6998. *
  6999. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  7000. * 2 pixels
  7001. * @sample {highcharts} highcharts/tooltip/borderwidth/
  7002. * No border (shadow only)
  7003. * @sample {highcharts} highcharts/css/tooltip-border-background/
  7004. * Tooltip in styled mode
  7005. * @sample {highstock} stock/tooltip/general/
  7006. * Custom tooltip
  7007. * @sample {highstock} highcharts/css/tooltip-border-background/
  7008. * Tooltip in styled mode
  7009. * @sample {highmaps} maps/tooltip/background-border/
  7010. * Background and border demo
  7011. * @sample {highmaps} highcharts/css/tooltip-border-background/
  7012. * Tooltip in styled mode
  7013. *
  7014. * @type {number}
  7015. */
  7016. borderWidth: void 0,
  7017. /**
  7018. * Whether to apply a drop shadow to the tooltip.
  7019. *
  7020. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  7021. * True by default
  7022. * @sample {highcharts} highcharts/tooltip/shadow/
  7023. * False
  7024. * @sample {highmaps} maps/tooltip/positioner/
  7025. * Fixed tooltip position, border and shadow disabled
  7026. *
  7027. * @type {boolean|Highcharts.ShadowOptionsObject}
  7028. */
  7029. shadow: true,
  7030. /**
  7031. * Prevents the tooltip from switching or closing when touched or
  7032. * pointed.
  7033. *
  7034. * @sample highcharts/tooltip/stickoncontact/
  7035. * Tooltip sticks on pointer contact
  7036. *
  7037. * @since 8.0.1
  7038. */
  7039. stickOnContact: false,
  7040. /**
  7041. * CSS styles for the tooltip. The tooltip can also be styled through
  7042. * the CSS class `.highcharts-tooltip`.
  7043. *
  7044. * Note that the default `pointerEvents` style makes the tooltip ignore
  7045. * mouse events, so in order to use clickable tooltips, this value must
  7046. * be set to `auto`.
  7047. *
  7048. * @sample {highcharts} highcharts/tooltip/style/
  7049. * Greater padding, bold text
  7050. *
  7051. * @type {Highcharts.CSSObject}
  7052. */
  7053. style: {
  7054. /** @internal */
  7055. color: "#333333" /* Palette.neutralColor80 */,
  7056. /** @internal */
  7057. cursor: 'default',
  7058. /** @internal */
  7059. fontSize: '0.8em'
  7060. },
  7061. /**
  7062. * Use HTML to render the contents of the tooltip instead of SVG. Using
  7063. * HTML allows advanced formatting like tables and images in the
  7064. * tooltip. It is also recommended for rtl languages as it works around
  7065. * rtl bugs in early Firefox.
  7066. *
  7067. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  7068. * A table for value alignment
  7069. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  7070. * Full HTML tooltip
  7071. * @sample {highmaps} maps/tooltip/usehtml/
  7072. * Pure HTML tooltip
  7073. *
  7074. * @since 2.2
  7075. */
  7076. useHTML: false
  7077. },
  7078. /**
  7079. * Highchart by default puts a credits label in the lower right corner
  7080. * of the chart. This can be changed using these options.
  7081. */
  7082. credits: {
  7083. /**
  7084. * Credits for map source to be concatenated with conventional credit
  7085. * text. By default this is a format string that collects copyright
  7086. * information from the map if available.
  7087. *
  7088. * @see [mapTextFull](#credits.mapTextFull)
  7089. * @see [text](#credits.text)
  7090. *
  7091. * @type {string}
  7092. * @default \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
  7093. * @since 4.2.2
  7094. * @product highmaps
  7095. * @apioption credits.mapText
  7096. */
  7097. /**
  7098. * Detailed credits for map source to be displayed on hover of credits
  7099. * text. By default this is a format string that collects copyright
  7100. * information from the map if available.
  7101. *
  7102. * @see [mapText](#credits.mapText)
  7103. * @see [text](#credits.text)
  7104. *
  7105. * @type {string}
  7106. * @default {geojson.copyright}
  7107. * @since 4.2.2
  7108. * @product highmaps
  7109. * @apioption credits.mapTextFull
  7110. */
  7111. /**
  7112. * Whether to show the credits text.
  7113. *
  7114. * @sample {highcharts} highcharts/credits/enabled-false/
  7115. * Credits disabled
  7116. * @sample {highstock} stock/credits/enabled/
  7117. * Credits disabled
  7118. * @sample {highmaps} maps/credits/enabled-false/
  7119. * Credits disabled
  7120. */
  7121. enabled: true,
  7122. /**
  7123. * The URL for the credits label.
  7124. *
  7125. * @sample {highcharts} highcharts/credits/href/
  7126. * Custom URL and text
  7127. * @sample {highmaps} maps/credits/customized/
  7128. * Custom URL and text
  7129. */
  7130. href: 'https://www.highcharts.com?credits',
  7131. /**
  7132. * Position configuration for the credits label.
  7133. *
  7134. * @sample {highcharts} highcharts/credits/position-left/
  7135. * Left aligned
  7136. * @sample {highcharts} highcharts/credits/position-left/
  7137. * Left aligned
  7138. * @sample {highmaps} maps/credits/customized/
  7139. * Left aligned
  7140. * @sample {highmaps} maps/credits/customized/
  7141. * Left aligned
  7142. *
  7143. * @type {Highcharts.AlignObject}
  7144. * @since 2.1
  7145. */
  7146. position: {
  7147. /** @internal */
  7148. align: 'right',
  7149. /** @internal */
  7150. x: -10,
  7151. /** @internal */
  7152. verticalAlign: 'bottom',
  7153. /** @internal */
  7154. y: -5
  7155. },
  7156. /**
  7157. * CSS styles for the credits label.
  7158. *
  7159. * @see In styled mode, credits styles can be set with the
  7160. * `.highcharts-credits` class.
  7161. *
  7162. * @type {Highcharts.CSSObject}
  7163. */
  7164. style: {
  7165. /** @internal */
  7166. cursor: 'pointer',
  7167. /** @internal */
  7168. color: "#999999" /* Palette.neutralColor40 */,
  7169. /** @internal */
  7170. fontSize: '0.6em'
  7171. },
  7172. /**
  7173. * The text for the credits label.
  7174. *
  7175. * @productdesc {highmaps}
  7176. * If a map is loaded as GeoJSON, the text defaults to
  7177. * `Highcharts @ {map-credits}`. Otherwise, it defaults to
  7178. * `Highcharts.com`.
  7179. *
  7180. * @sample {highcharts} highcharts/credits/href/
  7181. * Custom URL and text
  7182. * @sample {highmaps} maps/credits/customized/
  7183. * Custom URL and text
  7184. */
  7185. text: 'Highcharts.com'
  7186. }
  7187. };
  7188. /* eslint-disable spaced-comment */
  7189. defaultOptions.chart.styledMode = false;
  7190. '';
  7191. const defaultTime = new Time(defaultOptions.time);
  7192. /**
  7193. * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
  7194. * for outside modules wasn't enough because the setOptions method created a new
  7195. * object.
  7196. *
  7197. * @function Highcharts.getOptions
  7198. *
  7199. * @return {Highcharts.Options}
  7200. * Default options.
  7201. */
  7202. function getOptions() {
  7203. return defaultOptions;
  7204. }
  7205. /**
  7206. * Merge the default options with custom options and return the new options
  7207. * structure. Commonly used for defining reusable templates.
  7208. *
  7209. * @sample highcharts/global/useutc-false Setting a global option
  7210. * @sample highcharts/members/setoptions Applying a global theme
  7211. *
  7212. * @function Highcharts.setOptions
  7213. *
  7214. * @param {Highcharts.Options} options
  7215. * The new custom chart options.
  7216. *
  7217. * @return {Highcharts.Options}
  7218. * Updated options.
  7219. */
  7220. function setOptions(options) {
  7221. // Copy in the default options
  7222. merge(true, defaultOptions, options);
  7223. // Update the time object
  7224. if (options.time || options.global) {
  7225. if (H.time) {
  7226. H.time.update(merge(defaultOptions.global, defaultOptions.time, options.global, options.time));
  7227. }
  7228. else {
  7229. /**
  7230. * Global `Time` object with default options. Since v6.0.5, time
  7231. * settings can be applied individually for each chart. If no
  7232. * individual settings apply, this `Time` object is shared by all
  7233. * instances.
  7234. *
  7235. * @name Highcharts.time
  7236. * @type {Highcharts.Time}
  7237. */
  7238. H.time = defaultTime;
  7239. }
  7240. }
  7241. return defaultOptions;
  7242. }
  7243. /* *
  7244. *
  7245. * Default Export
  7246. *
  7247. * */
  7248. const DefaultOptions = {
  7249. defaultOptions,
  7250. defaultTime,
  7251. getOptions,
  7252. setOptions
  7253. };
  7254. /* *
  7255. *
  7256. * API Declarations
  7257. *
  7258. * */
  7259. /**
  7260. * @typedef {"plotBox"|"spacingBox"} Highcharts.ButtonRelativeToValue
  7261. */
  7262. /**
  7263. * Gets fired when a series is added to the chart after load time, using the
  7264. * `addSeries` method. Returning `false` prevents the series from being added.
  7265. *
  7266. * @callback Highcharts.ChartAddSeriesCallbackFunction
  7267. *
  7268. * @param {Highcharts.Chart} this
  7269. * The chart on which the event occured.
  7270. *
  7271. * @param {Highcharts.ChartAddSeriesEventObject} event
  7272. * The event that occured.
  7273. */
  7274. /**
  7275. * Contains common event information. Through the `options` property you can
  7276. * access the series options that were passed to the `addSeries` method.
  7277. *
  7278. * @interface Highcharts.ChartAddSeriesEventObject
  7279. */ /**
  7280. * The series options that were passed to the `addSeries` method.
  7281. * @name Highcharts.ChartAddSeriesEventObject#options
  7282. * @type {Highcharts.SeriesOptionsType}
  7283. */ /**
  7284. * Prevents the default behaviour of the event.
  7285. * @name Highcharts.ChartAddSeriesEventObject#preventDefault
  7286. * @type {Function}
  7287. */ /**
  7288. * The event target.
  7289. * @name Highcharts.ChartAddSeriesEventObject#target
  7290. * @type {Highcharts.Chart}
  7291. */ /**
  7292. * The event type.
  7293. * @name Highcharts.ChartAddSeriesEventObject#type
  7294. * @type {"addSeries"}
  7295. */
  7296. /**
  7297. * Gets fired when clicking on the plot background.
  7298. *
  7299. * @callback Highcharts.ChartClickCallbackFunction
  7300. *
  7301. * @param {Highcharts.Chart} this
  7302. * The chart on which the event occured.
  7303. *
  7304. * @param {Highcharts.PointerEventObject} event
  7305. * The event that occured.
  7306. */
  7307. /**
  7308. * Contains an axes of the clicked spot.
  7309. *
  7310. * @interface Highcharts.ChartClickEventAxisObject
  7311. */ /**
  7312. * Axis at the clicked spot.
  7313. * @name Highcharts.ChartClickEventAxisObject#axis
  7314. * @type {Highcharts.Axis}
  7315. */ /**
  7316. * Axis value at the clicked spot.
  7317. * @name Highcharts.ChartClickEventAxisObject#value
  7318. * @type {number}
  7319. */
  7320. /**
  7321. * Contains information about the clicked spot on the chart. Remember the unit
  7322. * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
  7323. *
  7324. * @interface Highcharts.ChartClickEventObject
  7325. * @extends Highcharts.PointerEventObject
  7326. */ /**
  7327. * Information about the x-axis on the clicked spot.
  7328. * @name Highcharts.ChartClickEventObject#xAxis
  7329. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  7330. */ /**
  7331. * Information about the y-axis on the clicked spot.
  7332. * @name Highcharts.ChartClickEventObject#yAxis
  7333. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  7334. */ /**
  7335. * Information about the z-axis on the clicked spot.
  7336. * @name Highcharts.ChartClickEventObject#zAxis
  7337. * @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
  7338. */
  7339. /**
  7340. * Gets fired when the chart is finished loading.
  7341. *
  7342. * @callback Highcharts.ChartLoadCallbackFunction
  7343. *
  7344. * @param {Highcharts.Chart} this
  7345. * The chart on which the event occured.
  7346. *
  7347. * @param {global.Event} event
  7348. * The event that occured.
  7349. */
  7350. /**
  7351. * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
  7352. * after an axis, series or point is modified with the `redraw` option set to
  7353. * `true`.
  7354. *
  7355. * @callback Highcharts.ChartRedrawCallbackFunction
  7356. *
  7357. * @param {Highcharts.Chart} this
  7358. * The chart on which the event occured.
  7359. *
  7360. * @param {global.Event} event
  7361. * The event that occured.
  7362. */
  7363. /**
  7364. * Gets fired after initial load of the chart (directly after the `load` event),
  7365. * and after each redraw (directly after the `redraw` event).
  7366. *
  7367. * @callback Highcharts.ChartRenderCallbackFunction
  7368. *
  7369. * @param {Highcharts.Chart} this
  7370. * The chart on which the event occured.
  7371. *
  7372. * @param {global.Event} event
  7373. * The event that occured.
  7374. */
  7375. /**
  7376. * Gets fired when an area of the chart has been selected. The default action
  7377. * for the selection event is to zoom the chart to the selected area. It can be
  7378. * prevented by calling `event.preventDefault()` or return false.
  7379. *
  7380. * @callback Highcharts.ChartSelectionCallbackFunction
  7381. *
  7382. * @param {Highcharts.Chart} this
  7383. * The chart on which the event occured.
  7384. *
  7385. * @param {Highcharts.SelectEventObject} event
  7386. * Event informations
  7387. *
  7388. * @return {boolean|undefined}
  7389. * Return false to prevent the default action, usually zoom.
  7390. */
  7391. (''); // detach doclets above
  7392. return DefaultOptions;
  7393. });
  7394. _registerModule(_modules, 'Core/Animation/Fx.js', [_modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Color, H, U) {
  7395. /* *
  7396. *
  7397. * (c) 2010-2021 Torstein Honsi
  7398. *
  7399. * License: www.highcharts.com/license
  7400. *
  7401. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7402. *
  7403. * */
  7404. const { parse: color } = Color;
  7405. const { win } = H;
  7406. const { isNumber, objectEach } = U;
  7407. /* eslint-disable no-invalid-this, valid-jsdoc */
  7408. /* *
  7409. *
  7410. * Class
  7411. *
  7412. * */
  7413. /**
  7414. * An animator object used internally. One instance applies to one property
  7415. * (attribute or style prop) on one element. Animation is always initiated
  7416. * through {@link SVGElement#animate}.
  7417. *
  7418. * @example
  7419. * let rect = renderer.rect(0, 0, 10, 10).add();
  7420. * rect.animate({ width: 100 });
  7421. *
  7422. * @private
  7423. * @class
  7424. * @name Highcharts.Fx
  7425. *
  7426. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
  7427. * The element to animate.
  7428. *
  7429. * @param {Partial<Highcharts.AnimationOptionsObject>} options
  7430. * Animation options.
  7431. *
  7432. * @param {string} prop
  7433. * The single attribute or CSS property to animate.
  7434. */
  7435. class Fx {
  7436. /* *
  7437. *
  7438. * Constructors
  7439. *
  7440. * */
  7441. constructor(elem, options, prop) {
  7442. this.pos = NaN;
  7443. this.options = options;
  7444. this.elem = elem;
  7445. this.prop = prop;
  7446. }
  7447. /* *
  7448. *
  7449. * Functions
  7450. *
  7451. * */
  7452. /**
  7453. * Set the current step of a path definition on SVGElement.
  7454. *
  7455. * @function Highcharts.Fx#dSetter
  7456. *
  7457. */
  7458. dSetter() {
  7459. const paths = this.paths, start = paths && paths[0], end = paths && paths[1], now = this.now || 0;
  7460. let path = [];
  7461. // Land on the final path without adjustment points appended in the ends
  7462. if (now === 1 || !start || !end) {
  7463. path = this.toD || [];
  7464. }
  7465. else if (start.length === end.length && now < 1) {
  7466. for (let i = 0; i < end.length; i++) {
  7467. // Tween between the start segment and the end segment. Start
  7468. // with a copy of the end segment and tween the appropriate
  7469. // numerics
  7470. const startSeg = start[i];
  7471. const endSeg = end[i];
  7472. const tweenSeg = [];
  7473. for (let j = 0; j < endSeg.length; j++) {
  7474. const startItem = startSeg[j];
  7475. const endItem = endSeg[j];
  7476. // Tween numbers
  7477. if (isNumber(startItem) &&
  7478. isNumber(endItem) &&
  7479. // Arc boolean flags
  7480. !(endSeg[0] === 'A' && (j === 4 || j === 5))) {
  7481. tweenSeg[j] = startItem + now * (endItem - startItem);
  7482. // Strings, take directly from the end segment
  7483. }
  7484. else {
  7485. tweenSeg[j] = endItem;
  7486. }
  7487. }
  7488. path.push(tweenSeg);
  7489. }
  7490. // If animation is finished or length not matching, land on right value
  7491. }
  7492. else {
  7493. path = end;
  7494. }
  7495. this.elem.attr('d', path, void 0, true);
  7496. }
  7497. /**
  7498. * Update the element with the current animation step.
  7499. *
  7500. * @function Highcharts.Fx#update
  7501. *
  7502. */
  7503. update() {
  7504. const elem = this.elem, prop = this.prop, // if destroyed, it is null
  7505. now = this.now, step = this.options.step;
  7506. // Animation setter defined from outside
  7507. if (this[prop + 'Setter']) {
  7508. this[prop + 'Setter']();
  7509. // Other animations on SVGElement
  7510. }
  7511. else if (elem.attr) {
  7512. if (elem.element) {
  7513. elem.attr(prop, now, null, true);
  7514. }
  7515. // HTML styles, raw HTML content like container size
  7516. }
  7517. else {
  7518. elem.style[prop] = now + this.unit;
  7519. }
  7520. if (step) {
  7521. step.call(elem, now, this);
  7522. }
  7523. }
  7524. /**
  7525. * Run an animation.
  7526. *
  7527. * @function Highcharts.Fx#run
  7528. *
  7529. * @param {number} from
  7530. * The current value, value to start from.
  7531. *
  7532. * @param {number} to
  7533. * The end value, value to land on.
  7534. *
  7535. * @param {string} unit
  7536. * The property unit, for example `px`.
  7537. *
  7538. */
  7539. run(from, to, unit) {
  7540. const self = this, options = self.options, timer = function (gotoEnd) {
  7541. return timer.stopped ? false : self.step(gotoEnd);
  7542. }, requestAnimationFrame = win.requestAnimationFrame ||
  7543. function (step) {
  7544. setTimeout(step, 13);
  7545. }, step = function () {
  7546. for (let i = 0; i < Fx.timers.length; i++) {
  7547. if (!Fx.timers[i]()) {
  7548. Fx.timers.splice(i--, 1);
  7549. }
  7550. }
  7551. if (Fx.timers.length) {
  7552. requestAnimationFrame(step);
  7553. }
  7554. };
  7555. if (from === to && !this.elem['forceAnimate:' + this.prop]) {
  7556. delete options.curAnim[this.prop];
  7557. if (options.complete &&
  7558. Object.keys(options.curAnim).length === 0) {
  7559. options.complete.call(this.elem);
  7560. }
  7561. }
  7562. else { // #7166
  7563. this.startTime = +new Date();
  7564. this.start = from;
  7565. this.end = to;
  7566. this.unit = unit;
  7567. this.now = this.start;
  7568. this.pos = 0;
  7569. timer.elem = this.elem;
  7570. timer.prop = this.prop;
  7571. if (timer() && Fx.timers.push(timer) === 1) {
  7572. requestAnimationFrame(step);
  7573. }
  7574. }
  7575. }
  7576. /**
  7577. * Run a single step in the animation.
  7578. *
  7579. * @function Highcharts.Fx#step
  7580. *
  7581. * @param {boolean} [gotoEnd]
  7582. * Whether to go to the endpoint of the animation after abort.
  7583. *
  7584. * @return {boolean}
  7585. * Returns `true` if animation continues.
  7586. */
  7587. step(gotoEnd) {
  7588. const t = +new Date(), options = this.options, elem = this.elem, complete = options.complete, duration = options.duration, curAnim = options.curAnim;
  7589. let ret, done;
  7590. if ((elem.attr) && !elem.element) { // #2616, element is destroyed
  7591. ret = false;
  7592. }
  7593. else if (gotoEnd || t >= duration + this.startTime) {
  7594. this.now = this.end;
  7595. this.pos = 1;
  7596. this.update();
  7597. curAnim[this.prop] = true;
  7598. done = true;
  7599. objectEach(curAnim, function (val) {
  7600. if (val !== true) {
  7601. done = false;
  7602. }
  7603. });
  7604. if (done && complete) {
  7605. complete.call(elem);
  7606. }
  7607. ret = false;
  7608. }
  7609. else {
  7610. this.pos = options.easing((t - this.startTime) / duration);
  7611. this.now = this.start + ((this.end -
  7612. this.start) * this.pos);
  7613. this.update();
  7614. ret = true;
  7615. }
  7616. return ret;
  7617. }
  7618. /**
  7619. * Prepare start and end values so that the path can be animated one to one.
  7620. *
  7621. * @function Highcharts.Fx#initPath
  7622. *
  7623. * @param {Highcharts.SVGElement} elem
  7624. * The SVGElement item.
  7625. *
  7626. * @param {Highcharts.SVGPathArray|undefined} fromD
  7627. * Starting path definition.
  7628. *
  7629. * @param {Highcharts.SVGPathArray} toD
  7630. * Ending path definition.
  7631. *
  7632. * @return {Array<Highcharts.SVGPathArray,Highcharts.SVGPathArray>}
  7633. * An array containing start and end paths in array form so that
  7634. * they can be animated in parallel.
  7635. */
  7636. initPath(elem, fromD, toD) {
  7637. const startX = elem.startX, endX = elem.endX, end = toD.slice(), // copy
  7638. isArea = elem.isArea, positionFactor = isArea ? 2 : 1;
  7639. let shift, fullLength, i, reverse, start = fromD && fromD.slice(); // copy
  7640. if (!start) {
  7641. return [end, end];
  7642. }
  7643. /**
  7644. * If shifting points, prepend a dummy point to the end path.
  7645. * @private
  7646. */
  7647. function prepend(arr, other) {
  7648. while (arr.length < fullLength) {
  7649. // Move to, line to or curve to?
  7650. const moveSegment = arr[0], otherSegment = other[fullLength - arr.length];
  7651. if (otherSegment && moveSegment[0] === 'M') {
  7652. if (otherSegment[0] === 'C') {
  7653. arr[0] = [
  7654. 'C',
  7655. moveSegment[1],
  7656. moveSegment[2],
  7657. moveSegment[1],
  7658. moveSegment[2],
  7659. moveSegment[1],
  7660. moveSegment[2]
  7661. ];
  7662. }
  7663. else {
  7664. arr[0] = ['L', moveSegment[1], moveSegment[2]];
  7665. }
  7666. }
  7667. // Prepend a copy of the first point
  7668. arr.unshift(moveSegment);
  7669. // For areas, the bottom path goes back again to the left, so we
  7670. // need to append a copy of the last point.
  7671. if (isArea) {
  7672. const z = arr.pop();
  7673. arr.push(arr[arr.length - 1], z); // append point and the Z
  7674. }
  7675. }
  7676. }
  7677. /**
  7678. * Copy and append last point until the length matches the end length.
  7679. * @private
  7680. */
  7681. function append(arr, other) {
  7682. while (arr.length < fullLength) {
  7683. // Pull out the slice that is going to be appended or inserted.
  7684. // In a line graph, the positionFactor is 1, and the last point
  7685. // is sliced out. In an area graph, the positionFactor is 2,
  7686. // causing the middle two points to be sliced out, since an area
  7687. // path starts at left, follows the upper path then turns and
  7688. // follows the bottom back.
  7689. const segmentToAdd = arr[Math.floor(arr.length / positionFactor) - 1].slice();
  7690. // Disable the first control point of curve segments
  7691. if (segmentToAdd[0] === 'C') {
  7692. segmentToAdd[1] = segmentToAdd[5];
  7693. segmentToAdd[2] = segmentToAdd[6];
  7694. }
  7695. if (!isArea) {
  7696. arr.push(segmentToAdd);
  7697. }
  7698. else {
  7699. const lowerSegmentToAdd = arr[Math.floor(arr.length / positionFactor)].slice();
  7700. arr.splice(arr.length / 2, 0, segmentToAdd, lowerSegmentToAdd);
  7701. }
  7702. }
  7703. }
  7704. // For sideways animation, find out how much we need to shift to get the
  7705. // start path Xs to match the end path Xs.
  7706. if (startX && endX && endX.length) {
  7707. for (i = 0; i < startX.length; i++) {
  7708. // Moving left, new points coming in on right
  7709. if (startX[i] === endX[0]) {
  7710. shift = i;
  7711. break;
  7712. // Moving right
  7713. }
  7714. else if (startX[0] ===
  7715. endX[endX.length - startX.length + i]) {
  7716. shift = i;
  7717. reverse = true;
  7718. break;
  7719. // Fixed from the right side, "scaling" left
  7720. }
  7721. else if (startX[startX.length - 1] ===
  7722. endX[endX.length - startX.length + i]) {
  7723. shift = startX.length - i;
  7724. break;
  7725. }
  7726. }
  7727. if (typeof shift === 'undefined') {
  7728. start = [];
  7729. }
  7730. }
  7731. if (start.length && isNumber(shift)) {
  7732. // The common target length for the start and end array, where both
  7733. // arrays are padded in opposite ends
  7734. fullLength = end.length + shift * positionFactor;
  7735. if (!reverse) {
  7736. prepend(end, start);
  7737. append(start, end);
  7738. }
  7739. else {
  7740. prepend(start, end);
  7741. append(end, start);
  7742. }
  7743. }
  7744. return [start, end];
  7745. }
  7746. /**
  7747. * Handle animation of the color attributes directly.
  7748. *
  7749. * @function Highcharts.Fx#fillSetter
  7750. *
  7751. */
  7752. fillSetter() {
  7753. Fx.prototype.strokeSetter.apply(this, arguments);
  7754. }
  7755. /**
  7756. * Handle animation of the color attributes directly.
  7757. *
  7758. * @function Highcharts.Fx#strokeSetter
  7759. *
  7760. */
  7761. strokeSetter() {
  7762. this.elem.attr(this.prop, color(this.start).tweenTo(color(this.end), this.pos), void 0, true);
  7763. }
  7764. }
  7765. /* *
  7766. *
  7767. * Static Properties
  7768. *
  7769. * */
  7770. Fx.timers = [];
  7771. /* *
  7772. *
  7773. * Default Export
  7774. *
  7775. * */
  7776. return Fx;
  7777. });
  7778. _registerModule(_modules, 'Core/Animation/AnimationUtilities.js', [_modules['Core/Animation/Fx.js'], _modules['Core/Utilities.js']], function (Fx, U) {
  7779. /* *
  7780. *
  7781. * (c) 2010-2021 Torstein Honsi
  7782. *
  7783. * License: www.highcharts.com/license
  7784. *
  7785. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7786. *
  7787. * */
  7788. const { defined, getStyle, isArray, isNumber, isObject, merge, objectEach, pick } = U;
  7789. /* *
  7790. *
  7791. * Functions
  7792. *
  7793. * */
  7794. /**
  7795. * Set the global animation to either a given value, or fall back to the given
  7796. * chart's animation option.
  7797. *
  7798. * @function Highcharts.setAnimation
  7799. *
  7800. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>|undefined} animation
  7801. * The animation object.
  7802. *
  7803. * @param {Highcharts.Chart} chart
  7804. * The chart instance.
  7805. *
  7806. * @todo
  7807. * This function always relates to a chart, and sets a property on the renderer,
  7808. * so it should be moved to the SVGRenderer.
  7809. */
  7810. function setAnimation(animation, chart) {
  7811. chart.renderer.globalAnimation = pick(animation, chart.options.chart.animation, true);
  7812. }
  7813. /**
  7814. * Get the animation in object form, where a disabled animation is always
  7815. * returned as `{ duration: 0 }`.
  7816. *
  7817. * @function Highcharts.animObject
  7818. *
  7819. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=0]
  7820. * An animation setting. Can be an object with duration, complete and
  7821. * easing properties, or a boolean to enable or disable.
  7822. *
  7823. * @return {Highcharts.AnimationOptionsObject}
  7824. * An object with at least a duration property.
  7825. */
  7826. function animObject(animation) {
  7827. return isObject(animation) ?
  7828. merge({ duration: 500, defer: 0 }, animation) :
  7829. { duration: animation ? 500 : 0, defer: 0 };
  7830. }
  7831. /**
  7832. * Get the defer as a number value from series animation options.
  7833. *
  7834. * @function Highcharts.getDeferredAnimation
  7835. *
  7836. * @param {Highcharts.Chart} chart
  7837. * The chart instance.
  7838. *
  7839. * @param {boolean|Highcharts.AnimationOptionsObject} animation
  7840. * An animation setting. Can be an object with duration, complete and
  7841. * easing properties, or a boolean to enable or disable.
  7842. *
  7843. * @param {Highcharts.Series} [series]
  7844. * Series to defer animation.
  7845. *
  7846. * @return {number}
  7847. * The numeric value.
  7848. */
  7849. function getDeferredAnimation(chart, animation, series) {
  7850. const labelAnimation = animObject(animation), s = series ? [series] : chart.series;
  7851. let defer = 0, duration = 0;
  7852. s.forEach((series) => {
  7853. const seriesAnim = animObject(series.options.animation);
  7854. defer = animation && defined(animation.defer) ?
  7855. labelAnimation.defer :
  7856. Math.max(defer, seriesAnim.duration + seriesAnim.defer);
  7857. duration = Math.min(labelAnimation.duration, seriesAnim.duration);
  7858. });
  7859. // Disable defer for exporting
  7860. if (chart.renderer.forExport) {
  7861. defer = 0;
  7862. }
  7863. const anim = {
  7864. defer: Math.max(0, defer - duration),
  7865. duration: Math.min(defer, duration)
  7866. };
  7867. return anim;
  7868. }
  7869. /**
  7870. * The global animate method, which uses Fx to create individual animators.
  7871. *
  7872. * @function Highcharts.animate
  7873. *
  7874. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
  7875. * The element to animate.
  7876. *
  7877. * @param {Highcharts.CSSObject|Highcharts.SVGAttributes} params
  7878. * An object containing key-value pairs of the properties to animate.
  7879. * Supports numeric as pixel-based CSS properties for HTML objects and
  7880. * attributes for SVGElements.
  7881. *
  7882. * @param {Partial<Highcharts.AnimationOptionsObject>} [opt]
  7883. * Animation options.
  7884. *
  7885. * @return {void}
  7886. */
  7887. function animate(el, params, opt) {
  7888. let start, unit = '', end, fx, args;
  7889. if (!isObject(opt)) { // Number or undefined/null
  7890. args = arguments;
  7891. opt = {
  7892. duration: args[2],
  7893. easing: args[3],
  7894. complete: args[4]
  7895. };
  7896. }
  7897. if (!isNumber(opt.duration)) {
  7898. opt.duration = 400;
  7899. }
  7900. opt.easing = typeof opt.easing === 'function' ?
  7901. opt.easing :
  7902. (Math[opt.easing] || Math.easeInOutSine);
  7903. opt.curAnim = merge(params);
  7904. objectEach(params, function (val, prop) {
  7905. // Stop current running animation of this property
  7906. stop(el, prop);
  7907. fx = new Fx(el, opt, prop);
  7908. end = void 0;
  7909. if (prop === 'd' && isArray(params.d)) {
  7910. fx.paths = fx.initPath(el, el.pathArray, params.d);
  7911. fx.toD = params.d;
  7912. start = 0;
  7913. end = 1;
  7914. }
  7915. else if (el.attr) {
  7916. start = el.attr(prop);
  7917. }
  7918. else {
  7919. start = parseFloat(getStyle(el, prop)) || 0;
  7920. if (prop !== 'opacity') {
  7921. unit = 'px';
  7922. }
  7923. }
  7924. if (!end) {
  7925. end = val;
  7926. }
  7927. if (typeof end === 'string' && end.match('px')) {
  7928. end = end.replace(/px/g, ''); // #4351
  7929. }
  7930. fx.run(start, end, unit);
  7931. });
  7932. }
  7933. /**
  7934. * Stop running animation.
  7935. *
  7936. * @function Highcharts.stop
  7937. *
  7938. * @param {Highcharts.SVGElement} el
  7939. * The SVGElement to stop animation on.
  7940. *
  7941. * @param {string} [prop]
  7942. * The property to stop animating. If given, the stop method will stop a
  7943. * single property from animating, while others continue.
  7944. *
  7945. * @return {void}
  7946. *
  7947. * @todo
  7948. * A possible extension to this would be to stop a single property, when
  7949. * we want to continue animating others. Then assign the prop to the timer
  7950. * in the Fx.run method, and check for the prop here. This would be an
  7951. * improvement in all cases where we stop the animation from .attr. Instead of
  7952. * stopping everything, we can just stop the actual attributes we're setting.
  7953. */
  7954. function stop(el, prop) {
  7955. let i = Fx.timers.length;
  7956. // Remove timers related to this element (#4519)
  7957. while (i--) {
  7958. if (Fx.timers[i].elem === el && (!prop || prop === Fx.timers[i].prop)) {
  7959. Fx.timers[i].stopped = true; // #4667
  7960. }
  7961. }
  7962. }
  7963. const animationExports = {
  7964. animate,
  7965. animObject,
  7966. getDeferredAnimation,
  7967. setAnimation,
  7968. stop
  7969. };
  7970. /* *
  7971. *
  7972. * Default Export
  7973. *
  7974. * */
  7975. return animationExports;
  7976. });
  7977. _registerModule(_modules, 'Core/Renderer/HTML/AST.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  7978. /* *
  7979. *
  7980. * (c) 2010-2020 Torstein Honsi
  7981. *
  7982. * License: www.highcharts.com/license
  7983. *
  7984. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  7985. *
  7986. * */
  7987. const { SVG_NS, win } = H;
  7988. const { attr, createElement, css, error, isFunction, isString, objectEach, splat } = U;
  7989. const { trustedTypes } = win;
  7990. /* *
  7991. *
  7992. * Constants
  7993. *
  7994. * */
  7995. // Create the trusted type policy. This should not be exposed.
  7996. const trustedTypesPolicy = (trustedTypes &&
  7997. isFunction(trustedTypes.createPolicy) &&
  7998. trustedTypes.createPolicy('highcharts', {
  7999. createHTML: (s) => s
  8000. }));
  8001. const emptyHTML = trustedTypesPolicy ?
  8002. trustedTypesPolicy.createHTML('') :
  8003. '';
  8004. // IE9 and PhantomJS are only able to parse XML.
  8005. const hasValidDOMParser = (function () {
  8006. try {
  8007. return Boolean(new DOMParser().parseFromString(emptyHTML, 'text/html'));
  8008. }
  8009. catch (e) {
  8010. return false;
  8011. }
  8012. }());
  8013. /* *
  8014. *
  8015. * Class
  8016. *
  8017. * */
  8018. /**
  8019. * The AST class represents an abstract syntax tree of HTML or SVG content. It
  8020. * can take HTML as an argument, parse it, optionally transform it to SVG, then
  8021. * perform sanitation before inserting it into the DOM.
  8022. *
  8023. * @class
  8024. * @name Highcharts.AST
  8025. *
  8026. * @param {string|Array<Highcharts.ASTNode>} source
  8027. * Either an HTML string or an ASTNode list to populate the tree.
  8028. */
  8029. class AST {
  8030. /* *
  8031. *
  8032. * Static Functions
  8033. *
  8034. * */
  8035. /**
  8036. * Filter an object of SVG or HTML attributes against the allow list.
  8037. *
  8038. * @static
  8039. *
  8040. * @function Highcharts.AST#filterUserAttributes
  8041. *
  8042. * @param {Highcharts.SVGAttributes} attributes The attributes to filter
  8043. *
  8044. * @return {Highcharts.SVGAttributes}
  8045. * The filtered attributes
  8046. */
  8047. static filterUserAttributes(attributes) {
  8048. objectEach(attributes, (val, key) => {
  8049. let valid = true;
  8050. if (AST.allowedAttributes.indexOf(key) === -1) {
  8051. valid = false;
  8052. }
  8053. if (['background', 'dynsrc', 'href', 'lowsrc', 'src']
  8054. .indexOf(key) !== -1) {
  8055. valid = isString(val) && AST.allowedReferences.some((ref) => val.indexOf(ref) === 0);
  8056. }
  8057. if (!valid) {
  8058. error(33, false, void 0, {
  8059. 'Invalid attribute in config': `${key}`
  8060. });
  8061. delete attributes[key];
  8062. }
  8063. // #17753, < is not allowed in SVG attributes
  8064. if (isString(val) && attributes[key]) {
  8065. attributes[key] = val.replace(/</g, '&lt;');
  8066. }
  8067. });
  8068. return attributes;
  8069. }
  8070. static parseStyle(style) {
  8071. return style
  8072. .split(';')
  8073. .reduce((styles, line) => {
  8074. const pair = line.split(':').map((s) => s.trim()), key = pair.shift();
  8075. if (key && pair.length) {
  8076. styles[key.replace(/-([a-z])/g, (g) => g[1].toUpperCase())] = pair.join(':'); // #17146
  8077. }
  8078. return styles;
  8079. }, {});
  8080. }
  8081. /**
  8082. * Utility function to set html content for an element by passing in a
  8083. * markup string. The markup is safely parsed by the AST class to avoid
  8084. * XSS vulnerabilities. This function should be used instead of setting
  8085. * `innerHTML` in all cases where the content is not fully trusted.
  8086. *
  8087. * @static
  8088. * @function Highcharts.AST#setElementHTML
  8089. *
  8090. * @param {SVGDOMElement|HTMLDOMElement} el
  8091. * Node to set content of.
  8092. *
  8093. * @param {string} html
  8094. * Markup string
  8095. */
  8096. static setElementHTML(el, html) {
  8097. el.innerHTML = AST.emptyHTML; // Clear previous
  8098. if (html) {
  8099. const ast = new AST(html);
  8100. ast.addToDOM(el);
  8101. }
  8102. }
  8103. /* *
  8104. *
  8105. * Constructor
  8106. *
  8107. * */
  8108. // Construct an AST from HTML markup, or wrap an array of existing AST nodes
  8109. constructor(source) {
  8110. this.nodes = typeof source === 'string' ?
  8111. this.parseMarkup(source) : source;
  8112. }
  8113. /* *
  8114. *
  8115. * Functions
  8116. *
  8117. * */
  8118. /**
  8119. * Add the tree defined as a hierarchical JS structure to the DOM
  8120. *
  8121. * @function Highcharts.AST#addToDOM
  8122. *
  8123. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} parent
  8124. * The node where it should be added
  8125. *
  8126. * @return {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement}
  8127. * The inserted node.
  8128. */
  8129. addToDOM(parent) {
  8130. /**
  8131. * @private
  8132. * @param {Highcharts.ASTNode} subtree
  8133. * HTML/SVG definition
  8134. * @param {Element} [subParent]
  8135. * parent node
  8136. * @return {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  8137. * The inserted node.
  8138. */
  8139. function recurse(subtree, subParent) {
  8140. let ret;
  8141. splat(subtree).forEach(function (item) {
  8142. const tagName = item.tagName;
  8143. const textNode = item.textContent ?
  8144. H.doc.createTextNode(item.textContent) :
  8145. void 0;
  8146. // Whether to ignore the AST filtering totally, #15345
  8147. const bypassHTMLFiltering = AST.bypassHTMLFiltering;
  8148. let node;
  8149. if (tagName) {
  8150. if (tagName === '#text') {
  8151. node = textNode;
  8152. }
  8153. else if (AST.allowedTags.indexOf(tagName) !== -1 ||
  8154. bypassHTMLFiltering) {
  8155. const NS = tagName === 'svg' ?
  8156. SVG_NS :
  8157. (subParent.namespaceURI || SVG_NS);
  8158. const element = H.doc.createElementNS(NS, tagName);
  8159. const attributes = item.attributes || {};
  8160. // Apply attributes from root of AST node, legacy from
  8161. // from before TextBuilder
  8162. objectEach(item, function (val, key) {
  8163. if (key !== 'tagName' &&
  8164. key !== 'attributes' &&
  8165. key !== 'children' &&
  8166. key !== 'style' &&
  8167. key !== 'textContent') {
  8168. attributes[key] = val;
  8169. }
  8170. });
  8171. attr(element, bypassHTMLFiltering ?
  8172. attributes :
  8173. AST.filterUserAttributes(attributes));
  8174. if (item.style) {
  8175. css(element, item.style);
  8176. }
  8177. // Add text content
  8178. if (textNode) {
  8179. element.appendChild(textNode);
  8180. }
  8181. // Recurse
  8182. recurse(item.children || [], element);
  8183. node = element;
  8184. }
  8185. else {
  8186. error(33, false, void 0, {
  8187. 'Invalid tagName in config': tagName
  8188. });
  8189. }
  8190. }
  8191. // Add to the tree
  8192. if (node) {
  8193. subParent.appendChild(node);
  8194. }
  8195. ret = node;
  8196. });
  8197. // Return last node added (on top level it's the only one)
  8198. return ret;
  8199. }
  8200. return recurse(this.nodes, parent);
  8201. }
  8202. /**
  8203. * Parse HTML/SVG markup into AST Node objects. Used internally from the
  8204. * constructor.
  8205. *
  8206. * @private
  8207. *
  8208. * @function Highcharts.AST#getNodesFromMarkup
  8209. *
  8210. * @param {string} markup The markup string.
  8211. *
  8212. * @return {Array<Highcharts.ASTNode>} The parsed nodes.
  8213. */
  8214. parseMarkup(markup) {
  8215. const nodes = [];
  8216. markup = markup
  8217. .trim()
  8218. // The style attribute throws a warning when parsing when CSP is
  8219. // enabled (#6884), so use an alias and pick it up below
  8220. // Make all quotation marks parse correctly to DOM (#17627)
  8221. .replace(/ style=(["'])/g, ' data-style=$1');
  8222. let doc;
  8223. if (hasValidDOMParser) {
  8224. doc = new DOMParser().parseFromString(trustedTypesPolicy ?
  8225. trustedTypesPolicy.createHTML(markup) :
  8226. markup, 'text/html');
  8227. }
  8228. else {
  8229. const body = createElement('div');
  8230. body.innerHTML = markup;
  8231. doc = { body };
  8232. }
  8233. const appendChildNodes = (node, addTo) => {
  8234. const tagName = node.nodeName.toLowerCase();
  8235. // Add allowed tags
  8236. const astNode = {
  8237. tagName
  8238. };
  8239. if (tagName === '#text') {
  8240. astNode.textContent = node.textContent || '';
  8241. }
  8242. const parsedAttributes = node.attributes;
  8243. // Add attributes
  8244. if (parsedAttributes) {
  8245. const attributes = {};
  8246. [].forEach.call(parsedAttributes, (attrib) => {
  8247. if (attrib.name === 'data-style') {
  8248. astNode.style = AST.parseStyle(attrib.value);
  8249. }
  8250. else {
  8251. attributes[attrib.name] = attrib.value;
  8252. }
  8253. });
  8254. astNode.attributes = attributes;
  8255. }
  8256. // Handle children
  8257. if (node.childNodes.length) {
  8258. const children = [];
  8259. [].forEach.call(node.childNodes, (childNode) => {
  8260. appendChildNodes(childNode, children);
  8261. });
  8262. if (children.length) {
  8263. astNode.children = children;
  8264. }
  8265. }
  8266. addTo.push(astNode);
  8267. };
  8268. [].forEach.call(doc.body.childNodes, (childNode) => appendChildNodes(childNode, nodes));
  8269. return nodes;
  8270. }
  8271. }
  8272. /* *
  8273. *
  8274. * Static Properties
  8275. *
  8276. * */
  8277. /**
  8278. * The list of allowed SVG or HTML attributes, used for sanitizing
  8279. * potentially harmful content from the chart configuration before adding to
  8280. * the DOM.
  8281. *
  8282. * @see [Source code with default values](
  8283. * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedAttributes)
  8284. *
  8285. * @example
  8286. * // Allow a custom, trusted attribute
  8287. * Highcharts.AST.allowedAttributes.push('data-value');
  8288. *
  8289. * @name Highcharts.AST.allowedAttributes
  8290. * @type {Array<string>}
  8291. */
  8292. AST.allowedAttributes = [
  8293. 'alt',
  8294. 'aria-controls',
  8295. 'aria-describedby',
  8296. 'aria-expanded',
  8297. 'aria-haspopup',
  8298. 'aria-hidden',
  8299. 'aria-label',
  8300. 'aria-labelledby',
  8301. 'aria-live',
  8302. 'aria-pressed',
  8303. 'aria-readonly',
  8304. 'aria-roledescription',
  8305. 'aria-selected',
  8306. 'class',
  8307. 'clip-path',
  8308. 'color',
  8309. 'colspan',
  8310. 'cx',
  8311. 'cy',
  8312. 'd',
  8313. 'dx',
  8314. 'dy',
  8315. 'disabled',
  8316. 'fill',
  8317. 'flood-color',
  8318. 'flood-opacity',
  8319. 'height',
  8320. 'href',
  8321. 'id',
  8322. 'in',
  8323. 'markerHeight',
  8324. 'markerWidth',
  8325. 'offset',
  8326. 'opacity',
  8327. 'orient',
  8328. 'padding',
  8329. 'paddingLeft',
  8330. 'paddingRight',
  8331. 'patternUnits',
  8332. 'r',
  8333. 'refX',
  8334. 'refY',
  8335. 'role',
  8336. 'scope',
  8337. 'slope',
  8338. 'src',
  8339. 'startOffset',
  8340. 'stdDeviation',
  8341. 'stroke',
  8342. 'stroke-linecap',
  8343. 'stroke-width',
  8344. 'style',
  8345. 'tableValues',
  8346. 'result',
  8347. 'rowspan',
  8348. 'summary',
  8349. 'target',
  8350. 'tabindex',
  8351. 'text-align',
  8352. 'text-anchor',
  8353. 'textAnchor',
  8354. 'textLength',
  8355. 'title',
  8356. 'type',
  8357. 'valign',
  8358. 'width',
  8359. 'x',
  8360. 'x1',
  8361. 'x2',
  8362. 'xlink:href',
  8363. 'y',
  8364. 'y1',
  8365. 'y2',
  8366. 'zIndex'
  8367. ];
  8368. /**
  8369. * The list of allowed references for referring attributes like `href` and
  8370. * `src`. Attribute values will only be allowed if they start with one of
  8371. * these strings.
  8372. *
  8373. * @see [Source code with default values](
  8374. * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedReferences)
  8375. *
  8376. * @example
  8377. * // Allow tel:
  8378. * Highcharts.AST.allowedReferences.push('tel:');
  8379. *
  8380. * @name Highcharts.AST.allowedReferences
  8381. * @type {Array<string>}
  8382. */
  8383. AST.allowedReferences = [
  8384. 'https://',
  8385. 'http://',
  8386. 'mailto:',
  8387. '/',
  8388. '../',
  8389. './',
  8390. '#'
  8391. ];
  8392. /**
  8393. * The list of allowed SVG or HTML tags, used for sanitizing potentially
  8394. * harmful content from the chart configuration before adding to the DOM.
  8395. *
  8396. * @see [Source code with default values](
  8397. * https://github.com/highcharts/highcharts/blob/master/ts/Core/Renderer/HTML/AST.ts#:~:text=public%20static%20allowedTags)
  8398. *
  8399. * @example
  8400. * // Allow a custom, trusted tag
  8401. * Highcharts.AST.allowedTags.push('blink'); // ;)
  8402. *
  8403. * @name Highcharts.AST.allowedTags
  8404. * @type {Array<string>}
  8405. */
  8406. AST.allowedTags = [
  8407. 'a',
  8408. 'abbr',
  8409. 'b',
  8410. 'br',
  8411. 'button',
  8412. 'caption',
  8413. 'circle',
  8414. 'clipPath',
  8415. 'code',
  8416. 'dd',
  8417. 'defs',
  8418. 'div',
  8419. 'dl',
  8420. 'dt',
  8421. 'em',
  8422. 'feComponentTransfer',
  8423. 'feDropShadow',
  8424. 'feFuncA',
  8425. 'feFuncB',
  8426. 'feFuncG',
  8427. 'feFuncR',
  8428. 'feGaussianBlur',
  8429. 'feOffset',
  8430. 'feMerge',
  8431. 'feMergeNode',
  8432. 'filter',
  8433. 'h1',
  8434. 'h2',
  8435. 'h3',
  8436. 'h4',
  8437. 'h5',
  8438. 'h6',
  8439. 'hr',
  8440. 'i',
  8441. 'img',
  8442. 'li',
  8443. 'linearGradient',
  8444. 'marker',
  8445. 'ol',
  8446. 'p',
  8447. 'path',
  8448. 'pattern',
  8449. 'pre',
  8450. 'rect',
  8451. 'small',
  8452. 'span',
  8453. 'stop',
  8454. 'strong',
  8455. 'style',
  8456. 'sub',
  8457. 'sup',
  8458. 'svg',
  8459. 'table',
  8460. 'text',
  8461. 'textPath',
  8462. 'thead',
  8463. 'title',
  8464. 'tbody',
  8465. 'tspan',
  8466. 'td',
  8467. 'th',
  8468. 'tr',
  8469. 'u',
  8470. 'ul',
  8471. '#text'
  8472. ];
  8473. AST.emptyHTML = emptyHTML;
  8474. /**
  8475. * Allow all custom SVG and HTML attributes, references and tags (together
  8476. * with potentially harmful ones) to be added to the DOM from the chart
  8477. * configuration. In other words, disable the the allow-listing which is the
  8478. * primary functionality of the AST.
  8479. *
  8480. * WARNING: Setting this property to `true` while allowing untrusted user
  8481. * data in the chart configuration will expose your application to XSS
  8482. * security risks!
  8483. *
  8484. * Note that in case you want to allow a known set of tags or attributes,
  8485. * you should allow-list them instead of disabling the filtering totally.
  8486. * See [allowedAttributes](Highcharts.AST#.allowedAttributes),
  8487. * [allowedReferences](Highcharts.AST#.allowedReferences) and
  8488. * [allowedTags](Highcharts.AST#.allowedTags). The `bypassHTMLFiltering`
  8489. * setting is intended only for those cases where allow-listing is not
  8490. * practical, and the chart configuration already comes from a secure
  8491. * source.
  8492. *
  8493. * @example
  8494. * // Allow all custom attributes, references and tags (disable DOM XSS
  8495. * // filtering)
  8496. * Highcharts.AST.bypassHTMLFiltering = true;
  8497. *
  8498. * @name Highcharts.AST.bypassHTMLFiltering
  8499. * @static
  8500. */
  8501. AST.bypassHTMLFiltering = false;
  8502. /* *
  8503. *
  8504. * Default Export
  8505. *
  8506. * */
  8507. /* *
  8508. *
  8509. * API Declarations
  8510. *
  8511. * */
  8512. /**
  8513. * Serialized form of an SVG/HTML definition, including children.
  8514. *
  8515. * @interface Highcharts.ASTNode
  8516. */ /**
  8517. * @name Highcharts.ASTNode#attributes
  8518. * @type {Highcharts.SVGAttributes|undefined}
  8519. */ /**
  8520. * @name Highcharts.ASTNode#children
  8521. * @type {Array<Highcharts.ASTNode>|undefined}
  8522. */ /**
  8523. * @name Highcharts.ASTNode#tagName
  8524. * @type {string|undefined}
  8525. */ /**
  8526. * @name Highcharts.ASTNode#textContent
  8527. * @type {string|undefined}
  8528. */
  8529. (''); // keeps doclets above in file
  8530. return AST;
  8531. });
  8532. _registerModule(_modules, 'Core/Templating.js', [_modules['Core/Defaults.js'], _modules['Core/Utilities.js']], function (D, U) {
  8533. /* *
  8534. *
  8535. * (c) 2010-2021 Torstein Honsi
  8536. *
  8537. * License: www.highcharts.com/license
  8538. *
  8539. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8540. *
  8541. * */
  8542. const { defaultOptions, defaultTime } = D;
  8543. const { extend, getNestedProperty, isArray, isNumber, isObject, isString, pick, pInt } = U;
  8544. const helpers = {
  8545. // Built-in helpers
  8546. add: (a, b) => a + b,
  8547. divide: (a, b) => (b !== 0 ? a / b : ''),
  8548. // eslint-disable-next-line eqeqeq
  8549. eq: (a, b) => a == b,
  8550. each: function (arr) {
  8551. const match = arguments[arguments.length - 1];
  8552. return isArray(arr) ?
  8553. arr.map((item, i) => format(match.body, extend(isObject(item) ? item : { '@this': item }, {
  8554. '@index': i,
  8555. '@first': i === 0,
  8556. '@last': i === arr.length - 1
  8557. }))).join('') :
  8558. false;
  8559. },
  8560. ge: (a, b) => a >= b,
  8561. gt: (a, b) => a > b,
  8562. 'if': (condition) => !!condition,
  8563. le: (a, b) => a <= b,
  8564. lt: (a, b) => a < b,
  8565. multiply: (a, b) => a * b,
  8566. // eslint-disable-next-line eqeqeq
  8567. ne: (a, b) => a != b,
  8568. subtract: (a, b) => a - b,
  8569. unless: (condition) => !condition
  8570. };
  8571. /* *
  8572. *
  8573. * Functions
  8574. *
  8575. * */
  8576. /**
  8577. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
  8578. * human readable date string. The format is a subset of the formats for PHP's
  8579. * [strftime](https://www.php.net/manual/en/function.strftime.php) function.
  8580. * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
  8581. *
  8582. * Since v6.0.5, all internal dates are formatted through the
  8583. * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
  8584. * The `Highcharts.dateFormat` function only reflects global time settings set
  8585. * with `setOptions`.
  8586. *
  8587. * Supported format keys:
  8588. * - `%a`: Short weekday, like 'Mon'
  8589. * - `%A`: Long weekday, like 'Monday'
  8590. * - `%d`: Two digit day of the month, 01 to 31
  8591. * - `%e`: Day of the month, 1 through 31
  8592. * - `%w`: Day of the week, 0 through 6
  8593. * - `%b`: Short month, like 'Jan'
  8594. * - `%B`: Long month, like 'January'
  8595. * - `%m`: Two digit month number, 01 through 12
  8596. * - `%y`: Two digits year, like 09 for 2009
  8597. * - `%Y`: Four digits year, like 2009
  8598. * - `%H`: Two digits hours in 24h format, 00 through 23
  8599. * - `%k`: Hours in 24h format, 0 through 23
  8600. * - `%I`: Two digits hours in 12h format, 00 through 11
  8601. * - `%l`: Hours in 12h format, 1 through 12
  8602. * - `%M`: Two digits minutes, 00 through 59
  8603. * - `%p`: Upper case AM or PM
  8604. * - `%P`: Lower case AM or PM
  8605. * - `%S`: Two digits seconds, 00 through 59
  8606. * - `%L`: Milliseconds (naming from Ruby)
  8607. *
  8608. * @function Highcharts.dateFormat
  8609. *
  8610. * @param {string} format
  8611. * The desired format where various time representations are prefixed
  8612. * with `%`.
  8613. *
  8614. * @param {number} timestamp
  8615. * The JavaScript timestamp.
  8616. *
  8617. * @param {boolean} [capitalize=false]
  8618. * Upper case first letter in the return.
  8619. *
  8620. * @return {string}
  8621. * The formatted date.
  8622. */
  8623. function dateFormat(format, timestamp, capitalize) {
  8624. return defaultTime.dateFormat(format, timestamp, capitalize);
  8625. }
  8626. /**
  8627. * Format a string according to a subset of the rules of Python's String.format
  8628. * method.
  8629. *
  8630. * @example
  8631. * let s = Highcharts.format(
  8632. * 'The {color} fox was {len:.2f} feet long',
  8633. * { color: 'red', len: Math.PI }
  8634. * );
  8635. * // => The red fox was 3.14 feet long
  8636. *
  8637. * @function Highcharts.format
  8638. *
  8639. * @param {string} str
  8640. * The string to format.
  8641. *
  8642. * @param {Record<string, *>} ctx
  8643. * The context, a collection of key-value pairs where each key is
  8644. * replaced by its value.
  8645. *
  8646. * @param {Highcharts.Chart} [chart]
  8647. * A `Chart` instance used to get numberFormatter and time.
  8648. *
  8649. * @return {string}
  8650. * The formatted string.
  8651. */
  8652. function format(str = '', ctx, chart) {
  8653. const regex = /\{([a-zA-Z0-9\:\.\,;\-\/<>%_@"'= #\(\)]+)\}/g,
  8654. // The sub expression regex is the same as the top expression regex,
  8655. // but except parens and block helpers (#), and surrounded by parens
  8656. // instead of curly brackets.
  8657. subRegex = /\(([a-zA-Z0-9\:\.\,;\-\/<>%_@"'= ]+)\)/g, matches = [], floatRegex = /f$/, decRegex = /\.([0-9])/, lang = defaultOptions.lang, time = chart && chart.time || defaultTime, numberFormatter = chart && chart.numberFormatter || numberFormat;
  8658. /*
  8659. * Get a literal or variable value inside a template expression. May be
  8660. * extended with other types like string or null if needed, but keep it
  8661. * small for now.
  8662. */
  8663. const resolveProperty = (key = '') => {
  8664. let n;
  8665. // Literals
  8666. if (key === 'true') {
  8667. return true;
  8668. }
  8669. if (key === 'false') {
  8670. return false;
  8671. }
  8672. if ((n = Number(key)).toString() === key) {
  8673. return n;
  8674. }
  8675. // Variables and constants
  8676. return getNestedProperty(key, ctx);
  8677. };
  8678. let match, currentMatch, depth = 0, hasSub;
  8679. // Parse and create tree
  8680. while ((match = regex.exec(str)) !== null) {
  8681. // When a sub expression is found, it is evaluated first, and the
  8682. // results recursively evaluated until no subexpression exists.
  8683. const subMatch = subRegex.exec(match[1]);
  8684. if (subMatch) {
  8685. match = subMatch;
  8686. hasSub = true;
  8687. }
  8688. if (!currentMatch || !currentMatch.isBlock) {
  8689. currentMatch = {
  8690. ctx,
  8691. expression: match[1],
  8692. find: match[0],
  8693. isBlock: match[1].charAt(0) === '#',
  8694. start: match.index,
  8695. startInner: match.index + match[0].length,
  8696. length: match[0].length
  8697. };
  8698. }
  8699. // Identify helpers
  8700. const fn = match[1].split(' ')[0].replace('#', '');
  8701. if (helpers[fn]) {
  8702. // Block helper, only 0 level is handled
  8703. if (currentMatch.isBlock && fn === currentMatch.fn) {
  8704. depth++;
  8705. }
  8706. if (!currentMatch.fn) {
  8707. currentMatch.fn = fn;
  8708. }
  8709. }
  8710. // Closing a block helper
  8711. const startingElseSection = match[1] === 'else';
  8712. if (currentMatch.isBlock &&
  8713. currentMatch.fn && (match[1] === `/${currentMatch.fn}` ||
  8714. startingElseSection)) {
  8715. if (!depth) { // === 0
  8716. const start = currentMatch.startInner, body = str.substr(start, match.index - start);
  8717. // Either closing without an else section, or when encountering
  8718. // an else section
  8719. if (currentMatch.body === void 0) {
  8720. currentMatch.body = body;
  8721. currentMatch.startInner = match.index + match[0].length;
  8722. // The body exists already, so this is the else section
  8723. }
  8724. else {
  8725. currentMatch.elseBody = body;
  8726. }
  8727. currentMatch.find += body + match[0];
  8728. if (!startingElseSection) {
  8729. matches.push(currentMatch);
  8730. currentMatch = void 0;
  8731. }
  8732. }
  8733. else if (!startingElseSection) {
  8734. depth--;
  8735. }
  8736. // Common expression
  8737. }
  8738. else if (!currentMatch.isBlock) {
  8739. matches.push(currentMatch);
  8740. }
  8741. // Evaluate sub-matches one by one to prevent orphaned block closers
  8742. if (subMatch && !(currentMatch === null || currentMatch === void 0 ? void 0 : currentMatch.isBlock)) {
  8743. break;
  8744. }
  8745. }
  8746. // Execute
  8747. matches.forEach((match) => {
  8748. const { body, elseBody, expression, fn } = match;
  8749. let replacement, i;
  8750. // Helper function
  8751. if (fn) {
  8752. // Pass the helpers the amount of arguments defined by the function,
  8753. // then the match as the last argument.
  8754. const args = [match], parts = expression.split(' ');
  8755. i = helpers[fn].length;
  8756. while (i--) {
  8757. args.unshift(resolveProperty(parts[i + 1]));
  8758. }
  8759. replacement = helpers[fn].apply(ctx, args);
  8760. // Block helpers may return true or false. They may also return a
  8761. // string, like the `each` helper.
  8762. if (match.isBlock && typeof replacement === 'boolean') {
  8763. replacement = format(replacement ? body : elseBody, ctx);
  8764. }
  8765. // Simple variable replacement
  8766. }
  8767. else {
  8768. const valueAndFormat = expression.split(':');
  8769. replacement = resolveProperty(valueAndFormat.shift() || '');
  8770. // Format the replacement
  8771. if (valueAndFormat.length && typeof replacement === 'number') {
  8772. const segment = valueAndFormat.join(':');
  8773. if (floatRegex.test(segment)) { // float
  8774. const decimals = parseInt((segment.match(decRegex) || ['', '-1'])[1], 10);
  8775. if (replacement !== null) {
  8776. replacement = numberFormatter(replacement, decimals, lang.decimalPoint, segment.indexOf(',') > -1 ? lang.thousandsSep : '');
  8777. }
  8778. }
  8779. else {
  8780. replacement = time.dateFormat(segment, replacement);
  8781. }
  8782. }
  8783. }
  8784. str = str.replace(match.find, pick(replacement, ''));
  8785. });
  8786. return hasSub ? format(str, ctx, chart) : str;
  8787. }
  8788. /**
  8789. * Format a number and return a string based on input settings.
  8790. *
  8791. * @sample highcharts/members/highcharts-numberformat/
  8792. * Custom number format
  8793. *
  8794. * @function Highcharts.numberFormat
  8795. *
  8796. * @param {number} number
  8797. * The input number to format.
  8798. *
  8799. * @param {number} decimals
  8800. * The amount of decimals. A value of -1 preserves the amount in the
  8801. * input number.
  8802. *
  8803. * @param {string} [decimalPoint]
  8804. * The decimal point, defaults to the one given in the lang options, or
  8805. * a dot.
  8806. *
  8807. * @param {string} [thousandsSep]
  8808. * The thousands separator, defaults to the one given in the lang
  8809. * options, or a space character.
  8810. *
  8811. * @return {string}
  8812. * The formatted number.
  8813. */
  8814. function numberFormat(number, decimals, decimalPoint, thousandsSep) {
  8815. number = +number || 0;
  8816. decimals = +decimals;
  8817. let ret, fractionDigits;
  8818. const lang = defaultOptions.lang, origDec = (number.toString().split('.')[1] || '').split('e')[0].length, exponent = number.toString().split('e'), firstDecimals = decimals;
  8819. if (decimals === -1) {
  8820. // Preserve decimals. Not huge numbers (#3793).
  8821. decimals = Math.min(origDec, 20);
  8822. }
  8823. else if (!isNumber(decimals)) {
  8824. decimals = 2;
  8825. }
  8826. else if (decimals && exponent[1] && exponent[1] < 0) {
  8827. // Expose decimals from exponential notation (#7042)
  8828. fractionDigits = decimals + +exponent[1];
  8829. if (fractionDigits >= 0) {
  8830. // remove too small part of the number while keeping the notation
  8831. exponent[0] = (+exponent[0]).toExponential(fractionDigits)
  8832. .split('e')[0];
  8833. decimals = fractionDigits;
  8834. }
  8835. else {
  8836. // fractionDigits < 0
  8837. exponent[0] = exponent[0].split('.')[0] || 0;
  8838. if (decimals < 20) {
  8839. // use number instead of exponential notation (#7405)
  8840. number = (exponent[0] * Math.pow(10, exponent[1]))
  8841. .toFixed(decimals);
  8842. }
  8843. else {
  8844. // or zero
  8845. number = 0;
  8846. }
  8847. exponent[1] = 0;
  8848. }
  8849. }
  8850. // Add another decimal to avoid rounding errors of float numbers. (#4573)
  8851. // Then use toFixed to handle rounding.
  8852. const roundedNumber = (Math.abs(exponent[1] ? exponent[0] : number) +
  8853. Math.pow(10, -Math.max(decimals, origDec) - 1)).toFixed(decimals);
  8854. // A string containing the positive integer component of the number
  8855. const strinteger = String(pInt(roundedNumber));
  8856. // Leftover after grouping into thousands. Can be 0, 1 or 2.
  8857. const thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
  8858. // Language
  8859. decimalPoint = pick(decimalPoint, lang.decimalPoint);
  8860. thousandsSep = pick(thousandsSep, lang.thousandsSep);
  8861. // Start building the return
  8862. ret = number < 0 ? '-' : '';
  8863. // Add the leftover after grouping into thousands. For example, in the
  8864. // number 42 000 000, this line adds 42.
  8865. ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
  8866. if (+exponent[1] < 0 && !firstDecimals) {
  8867. ret = '0';
  8868. }
  8869. else {
  8870. // Add the remaining thousands groups, joined by the thousands separator
  8871. ret += strinteger
  8872. .substr(thousands)
  8873. .replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
  8874. }
  8875. // Add the decimal point and the decimal component
  8876. if (decimals) {
  8877. // Get the decimal component
  8878. ret += decimalPoint + roundedNumber.slice(-decimals);
  8879. }
  8880. if (exponent[1] && +ret !== 0) {
  8881. ret += 'e' + exponent[1];
  8882. }
  8883. return ret;
  8884. }
  8885. /* *
  8886. *
  8887. * Default Export
  8888. *
  8889. * */
  8890. const Templating = {
  8891. dateFormat,
  8892. format,
  8893. helpers,
  8894. numberFormat
  8895. };
  8896. return Templating;
  8897. });
  8898. _registerModule(_modules, 'Core/Renderer/RendererUtilities.js', [_modules['Core/Utilities.js']], function (U) {
  8899. /* *
  8900. *
  8901. * (c) 2010-2021 Torstein Honsi
  8902. *
  8903. * License: www.highcharts.com/license
  8904. *
  8905. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8906. *
  8907. * */
  8908. /* *
  8909. *
  8910. * Imports
  8911. *
  8912. * */
  8913. const { clamp, pick, stableSort } = U;
  8914. /* *
  8915. *
  8916. * Namespace
  8917. *
  8918. * */
  8919. var RendererUtilities;
  8920. (function (RendererUtilities) {
  8921. /* *
  8922. *
  8923. * Declarations
  8924. *
  8925. * */
  8926. /* *
  8927. *
  8928. * Functions
  8929. *
  8930. * */
  8931. /* eslint-disable valid-jsdoc */
  8932. /**
  8933. * General distribution algorithm for distributing labels of differing size
  8934. * along a confined length in two dimensions. The algorithm takes an array
  8935. * of objects containing a size, a target and a rank. It will place the
  8936. * labels as close as possible to their targets, skipping the lowest ranked
  8937. * labels if necessary.
  8938. * @private
  8939. */
  8940. function distribute(boxes, len, maxDistance) {
  8941. // Original array will be altered with added .pos
  8942. const origBoxes = boxes, reducedLen = origBoxes.reducedLen || len, sortByRank = (a, b) => (b.rank || 0) - (a.rank || 0), sortByTarget = (a, b) => a.target - b.target;
  8943. let i, overlapping = true, restBoxes = [], // The outranked overshoot
  8944. box, target, total = 0;
  8945. // If the total size exceeds the len, remove those boxes with the lowest
  8946. // rank
  8947. i = boxes.length;
  8948. while (i--) {
  8949. total += boxes[i].size;
  8950. }
  8951. // Sort by rank, then slice away overshoot
  8952. if (total > reducedLen) {
  8953. stableSort(boxes, sortByRank);
  8954. i = 0;
  8955. total = 0;
  8956. while (total <= reducedLen) {
  8957. total += boxes[i].size;
  8958. i++;
  8959. }
  8960. restBoxes = boxes.splice(i - 1, boxes.length);
  8961. }
  8962. // Order by target
  8963. stableSort(boxes, sortByTarget);
  8964. // So far we have been mutating the original array. Now
  8965. // create a copy with target arrays
  8966. boxes = boxes.map((box) => ({
  8967. size: box.size,
  8968. targets: [box.target],
  8969. align: pick(box.align, 0.5)
  8970. }));
  8971. while (overlapping) {
  8972. // Initial positions: target centered in box
  8973. i = boxes.length;
  8974. while (i--) {
  8975. box = boxes[i];
  8976. // Composite box, average of targets
  8977. target = (Math.min.apply(0, box.targets) +
  8978. Math.max.apply(0, box.targets)) / 2;
  8979. box.pos = clamp(target - box.size * box.align, 0, len - box.size);
  8980. }
  8981. // Detect overlap and join boxes
  8982. i = boxes.length;
  8983. overlapping = false;
  8984. while (i--) {
  8985. // Overlap
  8986. if (i > 0 &&
  8987. boxes[i - 1].pos + boxes[i - 1].size >
  8988. boxes[i].pos) {
  8989. // Add this size to the previous box
  8990. boxes[i - 1].size += boxes[i].size;
  8991. boxes[i - 1].targets = boxes[i - 1]
  8992. .targets
  8993. .concat(boxes[i].targets);
  8994. boxes[i - 1].align = 0.5;
  8995. // Overlapping right, push left
  8996. if (boxes[i - 1].pos + boxes[i - 1].size > len) {
  8997. boxes[i - 1].pos = len - boxes[i - 1].size;
  8998. }
  8999. boxes.splice(i, 1); // Remove this item
  9000. overlapping = true;
  9001. }
  9002. }
  9003. }
  9004. // Add the rest (hidden boxes)
  9005. origBoxes.push.apply(origBoxes, restBoxes);
  9006. // Now the composite boxes are placed, we need to put the original boxes
  9007. // within them
  9008. i = 0;
  9009. boxes.some((box) => {
  9010. let posInCompositeBox = 0;
  9011. // Exceeded maxDistance => abort
  9012. return (box.targets || []).some(() => {
  9013. origBoxes[i].pos = box.pos + posInCompositeBox;
  9014. // If the distance between the position and the target exceeds
  9015. // maxDistance, abort the loop and decrease the length in
  9016. // increments of 10% to recursively reduce the number of
  9017. // visible boxes by rank. Once all boxes are within the
  9018. // maxDistance, we're good.
  9019. if (typeof maxDistance !== 'undefined' &&
  9020. Math.abs(origBoxes[i].pos - origBoxes[i].target) > maxDistance) {
  9021. // Reset the positions that are already set
  9022. origBoxes
  9023. .slice(0, i + 1)
  9024. .forEach((box) => delete box.pos);
  9025. // Try with a smaller length
  9026. origBoxes.reducedLen =
  9027. (origBoxes.reducedLen || len) - (len * 0.1);
  9028. // Recurse
  9029. if (origBoxes.reducedLen > len * 0.1) {
  9030. distribute(origBoxes, len, maxDistance);
  9031. }
  9032. // Exceeded maxDistance => abort
  9033. return true;
  9034. }
  9035. posInCompositeBox += origBoxes[i].size;
  9036. i++;
  9037. return false;
  9038. });
  9039. });
  9040. // Add the rest (hidden) boxes and sort by target
  9041. stableSort(origBoxes, sortByTarget);
  9042. return origBoxes;
  9043. }
  9044. RendererUtilities.distribute = distribute;
  9045. })(RendererUtilities || (RendererUtilities = {}));
  9046. /* *
  9047. *
  9048. * Default Export
  9049. *
  9050. * */
  9051. return RendererUtilities;
  9052. });
  9053. _registerModule(_modules, 'Core/Renderer/SVG/SVGElement.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (A, Color, H, U) {
  9054. /* *
  9055. *
  9056. * (c) 2010-2021 Torstein Honsi
  9057. *
  9058. * License: www.highcharts.com/license
  9059. *
  9060. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  9061. *
  9062. * */
  9063. const { animate, animObject, stop } = A;
  9064. const { deg2rad, doc, noop, svg, SVG_NS, win } = H;
  9065. const { addEvent, attr, createElement, css, defined, erase, extend, fireEvent, isArray, isFunction, isObject, isString, merge, objectEach, pick, pInt, syncTimeout, uniqueKey } = U;
  9066. /* *
  9067. *
  9068. * Class
  9069. *
  9070. * */
  9071. /* eslint-disable no-invalid-this, valid-jsdoc */
  9072. /**
  9073. * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
  9074. * rendering layer of Highcharts. Combined with the
  9075. * {@link Highcharts.SVGRenderer}
  9076. * object, these prototypes allow freeform annotation in the charts or even in
  9077. * HTML pages without instanciating a chart. The SVGElement can also wrap HTML
  9078. * labels, when `text` or `label` elements are created with the `useHTML`
  9079. * parameter.
  9080. *
  9081. * The SVGElement instances are created through factory functions on the
  9082. * {@link Highcharts.SVGRenderer}
  9083. * object, like
  9084. * {@link Highcharts.SVGRenderer#rect|rect},
  9085. * {@link Highcharts.SVGRenderer#path|path},
  9086. * {@link Highcharts.SVGRenderer#text|text},
  9087. * {@link Highcharts.SVGRenderer#label|label},
  9088. * {@link Highcharts.SVGRenderer#g|g}
  9089. * and more.
  9090. *
  9091. * @class
  9092. * @name Highcharts.SVGElement
  9093. */
  9094. class SVGElement {
  9095. constructor() {
  9096. /* *
  9097. *
  9098. * Properties
  9099. *
  9100. * */
  9101. this.element = void 0;
  9102. this.onEvents = {};
  9103. this.opacity = 1; // Default base for animation
  9104. this.renderer = void 0;
  9105. this.SVG_NS = SVG_NS;
  9106. }
  9107. // @todo public zIndex?: number;
  9108. /* *
  9109. *
  9110. * Functions
  9111. *
  9112. * */
  9113. /**
  9114. * Get the current value of an attribute or pseudo attribute,
  9115. * used mainly for animation. Called internally from
  9116. * the {@link Highcharts.SVGRenderer#attr} function.
  9117. *
  9118. * @private
  9119. * @function Highcharts.SVGElement#_defaultGetter
  9120. *
  9121. * @param {string} key
  9122. * Property key.
  9123. *
  9124. * @return {number|string}
  9125. * Property value.
  9126. */
  9127. _defaultGetter(key) {
  9128. let ret = pick(this[key + 'Value'], // align getter
  9129. this[key], this.element ? this.element.getAttribute(key) : null, 0);
  9130. if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
  9131. ret = parseFloat(ret);
  9132. }
  9133. return ret;
  9134. }
  9135. /**
  9136. * @private
  9137. * @function Highcharts.SVGElement#_defaultSetter
  9138. *
  9139. * @param {string} value
  9140. *
  9141. * @param {string} key
  9142. *
  9143. * @param {Highcharts.SVGDOMElement} element
  9144. *
  9145. */
  9146. _defaultSetter(value, key, element) {
  9147. element.setAttribute(key, value);
  9148. }
  9149. /**
  9150. * Add the element to the DOM. All elements must be added this way.
  9151. *
  9152. * @sample highcharts/members/renderer-g
  9153. * Elements added to a group
  9154. *
  9155. * @function Highcharts.SVGElement#add
  9156. *
  9157. * @param {Highcharts.SVGElement} [parent]
  9158. * The parent item to add it to. If undefined, the element is added
  9159. * to the {@link Highcharts.SVGRenderer.box}.
  9160. *
  9161. * @return {Highcharts.SVGElement}
  9162. * Returns the SVGElement for chaining.
  9163. */
  9164. add(parent) {
  9165. const renderer = this.renderer, element = this.element;
  9166. let inserted;
  9167. if (parent) {
  9168. this.parentGroup = parent;
  9169. }
  9170. // Build formatted text
  9171. if (typeof this.textStr !== 'undefined' &&
  9172. this.element.nodeName === 'text' // Not for SVGLabel instances
  9173. ) {
  9174. renderer.buildText(this);
  9175. }
  9176. // Mark as added
  9177. this.added = true;
  9178. // If we're adding to renderer root, or other elements in the group
  9179. // have a z index, we need to handle it
  9180. if (!parent || parent.handleZ || this.zIndex) {
  9181. inserted = this.zIndexSetter();
  9182. }
  9183. // If zIndex is not handled, append at the end
  9184. if (!inserted) {
  9185. (parent ?
  9186. parent.element :
  9187. renderer.box).appendChild(element);
  9188. }
  9189. // fire an event for internal hooks
  9190. if (this.onAdd) {
  9191. this.onAdd();
  9192. }
  9193. return this;
  9194. }
  9195. /**
  9196. * Add a class name to an element.
  9197. *
  9198. * @function Highcharts.SVGElement#addClass
  9199. *
  9200. * @param {string} className
  9201. * The new class name to add.
  9202. *
  9203. * @param {boolean} [replace=false]
  9204. * When true, the existing class name(s) will be overwritten with the new
  9205. * one. When false, the new one is added.
  9206. *
  9207. * @return {Highcharts.SVGElement}
  9208. * Return the SVG element for chainability.
  9209. */
  9210. addClass(className, replace) {
  9211. const currentClassName = replace ? '' : (this.attr('class') || '');
  9212. // Trim the string and remove duplicates
  9213. className = (className || '')
  9214. .split(/ /g)
  9215. .reduce(function (newClassName, name) {
  9216. if (currentClassName.indexOf(name) === -1) {
  9217. newClassName.push(name);
  9218. }
  9219. return newClassName;
  9220. }, (currentClassName ?
  9221. [currentClassName] :
  9222. []))
  9223. .join(' ');
  9224. if (className !== currentClassName) {
  9225. this.attr('class', className);
  9226. }
  9227. return this;
  9228. }
  9229. /**
  9230. * This method is executed in the end of `attr()`, after setting all
  9231. * attributes in the hash. In can be used to efficiently consolidate
  9232. * multiple attributes in one SVG property -- e.g., translate, rotate and
  9233. * scale are merged in one "transform" attribute in the SVG node.
  9234. *
  9235. * @private
  9236. * @function Highcharts.SVGElement#afterSetters
  9237. */
  9238. afterSetters() {
  9239. // Update transform. Do this outside the loop to prevent redundant
  9240. // updating for batch setting of attributes.
  9241. if (this.doTransform) {
  9242. this.updateTransform();
  9243. this.doTransform = false;
  9244. }
  9245. }
  9246. /**
  9247. * Align the element relative to the chart or another box.
  9248. *
  9249. * @function Highcharts.SVGElement#align
  9250. *
  9251. * @param {Highcharts.AlignObject} [alignOptions]
  9252. * The alignment options. The function can be called without this
  9253. * parameter in order to re-align an element after the box has been
  9254. * updated.
  9255. *
  9256. * @param {boolean} [alignByTranslate]
  9257. * Align element by translation.
  9258. *
  9259. * @param {string|Highcharts.BBoxObject} [box]
  9260. * The box to align to, needs a width and height. When the box is a
  9261. * string, it refers to an object in the Renderer. For example, when
  9262. * box is `spacingBox`, it refers to `Renderer.spacingBox` which
  9263. * holds `width`, `height`, `x` and `y` properties.
  9264. *
  9265. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  9266. */
  9267. align(alignOptions, alignByTranslate, box) {
  9268. const attribs = {}, renderer = this.renderer, alignedObjects = renderer.alignedObjects;
  9269. let x, y, alignTo, alignFactor, vAlignFactor;
  9270. // First call on instanciate
  9271. if (alignOptions) {
  9272. this.alignOptions = alignOptions;
  9273. this.alignByTranslate = alignByTranslate;
  9274. if (!box || isString(box)) {
  9275. this.alignTo = alignTo = box || 'renderer';
  9276. // prevent duplicates, like legendGroup after resize
  9277. erase(alignedObjects, this);
  9278. alignedObjects.push(this);
  9279. box = void 0; // reassign it below
  9280. }
  9281. // When called on resize, no arguments are supplied
  9282. }
  9283. else {
  9284. alignOptions = this.alignOptions;
  9285. alignByTranslate = this.alignByTranslate;
  9286. alignTo = this.alignTo;
  9287. }
  9288. box = pick(box, renderer[alignTo], alignTo === 'scrollablePlotBox' ?
  9289. renderer.plotBox : void 0, renderer);
  9290. // Assign variables
  9291. const align = alignOptions.align, vAlign = alignOptions.verticalAlign;
  9292. // default: left align
  9293. x = (box.x || 0) + (alignOptions.x || 0);
  9294. // default: top align
  9295. y = (box.y || 0) + (alignOptions.y || 0);
  9296. // Align
  9297. if (align === 'right') {
  9298. alignFactor = 1;
  9299. }
  9300. else if (align === 'center') {
  9301. alignFactor = 2;
  9302. }
  9303. if (alignFactor) {
  9304. x += (box.width - (alignOptions.width || 0)) /
  9305. alignFactor;
  9306. }
  9307. attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
  9308. // Vertical align
  9309. if (vAlign === 'bottom') {
  9310. vAlignFactor = 1;
  9311. }
  9312. else if (vAlign === 'middle') {
  9313. vAlignFactor = 2;
  9314. }
  9315. if (vAlignFactor) {
  9316. y += (box.height - (alignOptions.height || 0)) /
  9317. vAlignFactor;
  9318. }
  9319. attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
  9320. // Animate only if already placed
  9321. this[this.placed ? 'animate' : 'attr'](attribs);
  9322. this.placed = true;
  9323. this.alignAttr = attribs;
  9324. return this;
  9325. }
  9326. /**
  9327. * @private
  9328. * @function Highcharts.SVGElement#alignSetter
  9329. * @param {"left"|"center"|"right"} value
  9330. */
  9331. alignSetter(value) {
  9332. const convert = {
  9333. left: 'start',
  9334. center: 'middle',
  9335. right: 'end'
  9336. };
  9337. if (convert[value]) {
  9338. this.alignValue = value;
  9339. this.element.setAttribute('text-anchor', convert[value]);
  9340. }
  9341. }
  9342. /**
  9343. * Animate to given attributes or CSS properties.
  9344. *
  9345. * @sample highcharts/members/element-on/
  9346. * Setting some attributes by animation
  9347. *
  9348. * @function Highcharts.SVGElement#animate
  9349. *
  9350. * @param {Highcharts.SVGAttributes} params
  9351. * SVG attributes or CSS to animate.
  9352. *
  9353. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [options]
  9354. * Animation options.
  9355. *
  9356. * @param {Function} [complete]
  9357. * Function to perform at the end of animation.
  9358. *
  9359. * @return {Highcharts.SVGElement}
  9360. * Returns the SVGElement for chaining.
  9361. */
  9362. animate(params, options, complete) {
  9363. const animOptions = animObject(pick(options, this.renderer.globalAnimation, true)), deferTime = animOptions.defer;
  9364. // When the page is hidden save resources in the background by not
  9365. // running animation at all (#9749).
  9366. if (doc.hidden) {
  9367. animOptions.duration = 0;
  9368. }
  9369. if (animOptions.duration !== 0) {
  9370. // allows using a callback with the global animation without
  9371. // overwriting it
  9372. if (complete) {
  9373. animOptions.complete = complete;
  9374. }
  9375. // If defer option is defined delay the animation #12901
  9376. syncTimeout(() => {
  9377. if (this.element) {
  9378. animate(this, params, animOptions);
  9379. }
  9380. }, deferTime);
  9381. }
  9382. else {
  9383. this.attr(params, void 0, complete || animOptions.complete);
  9384. // Call the end step synchronously
  9385. objectEach(params, function (val, prop) {
  9386. if (animOptions.step) {
  9387. animOptions.step.call(this, val, { prop: prop, pos: 1, elem: this });
  9388. }
  9389. }, this);
  9390. }
  9391. return this;
  9392. }
  9393. /**
  9394. * Apply a text outline through a custom CSS property, by copying the text
  9395. * element and apply stroke to the copy. Used internally. Contrast checks at
  9396. * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
  9397. *
  9398. * @example
  9399. * // Specific color
  9400. * text.css({
  9401. * textOutline: '1px black'
  9402. * });
  9403. * // Automatic contrast
  9404. * text.css({
  9405. * color: '#000000', // black text
  9406. * textOutline: '1px contrast' // => white outline
  9407. * });
  9408. *
  9409. * @private
  9410. * @function Highcharts.SVGElement#applyTextOutline
  9411. *
  9412. * @param {string} textOutline
  9413. * A custom CSS `text-outline` setting, defined by `width color`.
  9414. */
  9415. applyTextOutline(textOutline) {
  9416. const elem = this.element, hasContrast = textOutline.indexOf('contrast') !== -1, styles = {};
  9417. // When the text shadow is set to contrast, use dark stroke for light
  9418. // text and vice versa.
  9419. if (hasContrast) {
  9420. styles.textOutline = textOutline = textOutline.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
  9421. }
  9422. // Extract the stroke width and color
  9423. const parts = textOutline.split(' ');
  9424. const color = parts[parts.length - 1];
  9425. let strokeWidth = parts[0];
  9426. if (strokeWidth && strokeWidth !== 'none' && H.svg) {
  9427. this.fakeTS = true; // Fake text shadow
  9428. // Since the stroke is applied on center of the actual outline, we
  9429. // need to double it to get the correct stroke-width outside the
  9430. // glyphs.
  9431. strokeWidth = strokeWidth.replace(/(^[\d\.]+)(.*?)$/g, function (match, digit, unit) {
  9432. return (2 * Number(digit)) + unit;
  9433. });
  9434. // Remove shadows from previous runs.
  9435. this.removeTextOutline();
  9436. const outline = doc.createElementNS(SVG_NS, 'tspan');
  9437. attr(outline, {
  9438. 'class': 'highcharts-text-outline',
  9439. fill: color,
  9440. stroke: color,
  9441. 'stroke-width': strokeWidth,
  9442. 'stroke-linejoin': 'round'
  9443. });
  9444. // For each of the tspans and text nodes, create a copy in the
  9445. // outline.
  9446. const parentElem = elem.querySelector('textPath') || elem;
  9447. [].forEach.call(parentElem.childNodes, (childNode) => {
  9448. const clone = childNode.cloneNode(true);
  9449. if (clone.removeAttribute) {
  9450. ['fill', 'stroke', 'stroke-width', 'stroke'].forEach((prop) => clone
  9451. .removeAttribute(prop));
  9452. }
  9453. outline.appendChild(clone);
  9454. });
  9455. // Collect the sum of dy from all children, included nested ones
  9456. let totalHeight = 0;
  9457. [].forEach.call(parentElem.querySelectorAll('text tspan'), (element) => {
  9458. totalHeight += Number(element.getAttribute('dy'));
  9459. });
  9460. // Insert an absolutely positioned break before the original text
  9461. // to keep it in place
  9462. const br = doc.createElementNS(SVG_NS, 'tspan');
  9463. br.textContent = '\u200B';
  9464. // Reset the position for the following text
  9465. attr(br, {
  9466. x: Number(elem.getAttribute('x')),
  9467. dy: -totalHeight
  9468. });
  9469. // Insert the outline
  9470. outline.appendChild(br);
  9471. parentElem.insertBefore(outline, parentElem.firstChild);
  9472. }
  9473. }
  9474. /**
  9475. * @function Highcharts.SVGElement#attr
  9476. * @param {string} key
  9477. * @return {number|string}
  9478. */ /**
  9479. * Apply native and custom attributes to the SVG elements.
  9480. *
  9481. * In order to set the rotation center for rotation, set x and y to 0 and
  9482. * use `translateX` and `translateY` attributes to position the element
  9483. * instead.
  9484. *
  9485. * Attributes frequently used in Highcharts are `fill`, `stroke`,
  9486. * `stroke-width`.
  9487. *
  9488. * @sample highcharts/members/renderer-rect/
  9489. * Setting some attributes
  9490. *
  9491. * @example
  9492. * // Set multiple attributes
  9493. * element.attr({
  9494. * stroke: 'red',
  9495. * fill: 'blue',
  9496. * x: 10,
  9497. * y: 10
  9498. * });
  9499. *
  9500. * // Set a single attribute
  9501. * element.attr('stroke', 'red');
  9502. *
  9503. * // Get an attribute
  9504. * element.attr('stroke'); // => 'red'
  9505. *
  9506. * @function Highcharts.SVGElement#attr
  9507. *
  9508. * @param {string|Highcharts.SVGAttributes} [hash]
  9509. * The native and custom SVG attributes.
  9510. *
  9511. * @param {number|string|Highcharts.SVGPathArray} [val]
  9512. * If the type of the first argument is `string`, the second can be a
  9513. * value, which will serve as a single attribute setter. If the first
  9514. * argument is a string and the second is undefined, the function
  9515. * serves as a getter and the current value of the property is
  9516. * returned.
  9517. *
  9518. * @param {Function} [complete]
  9519. * A callback function to execute after setting the attributes. This
  9520. * makes the function compliant and interchangeable with the
  9521. * {@link SVGElement#animate} function.
  9522. *
  9523. * @param {boolean} [continueAnimation=true]
  9524. * Used internally when `.attr` is called as part of an animation
  9525. * step. Otherwise, calling `.attr` for an attribute will stop
  9526. * animation for that attribute.
  9527. *
  9528. * @return {Highcharts.SVGElement}
  9529. * If used as a setter, it returns the current
  9530. * {@link Highcharts.SVGElement} so the calls can be chained. If
  9531. * used as a getter, the current value of the attribute is returned.
  9532. */
  9533. attr(hash, val, complete, continueAnimation) {
  9534. const element = this.element, symbolCustomAttribs = SVGElement.symbolCustomAttribs;
  9535. let key, hasSetSymbolSize, ret = this, skipAttr, setter;
  9536. // single key-value pair
  9537. if (typeof hash === 'string' && typeof val !== 'undefined') {
  9538. key = hash;
  9539. hash = {};
  9540. hash[key] = val;
  9541. }
  9542. // used as a getter: first argument is a string, second is undefined
  9543. if (typeof hash === 'string') {
  9544. ret = (this[hash + 'Getter'] ||
  9545. this._defaultGetter).call(this, hash, element);
  9546. // setter
  9547. }
  9548. else {
  9549. objectEach(hash, function eachAttribute(val, key) {
  9550. skipAttr = false;
  9551. // Unless .attr is from the animator update, stop current
  9552. // running animation of this property
  9553. if (!continueAnimation) {
  9554. stop(this, key);
  9555. }
  9556. // Special handling of symbol attributes
  9557. if (this.symbolName &&
  9558. symbolCustomAttribs.indexOf(key) !== -1) {
  9559. if (!hasSetSymbolSize) {
  9560. this.symbolAttr(hash);
  9561. hasSetSymbolSize = true;
  9562. }
  9563. skipAttr = true;
  9564. }
  9565. if (this.rotation && (key === 'x' || key === 'y')) {
  9566. this.doTransform = true;
  9567. }
  9568. if (!skipAttr) {
  9569. setter = (this[key + 'Setter'] ||
  9570. this._defaultSetter);
  9571. setter.call(this, val, key, element);
  9572. }
  9573. }, this);
  9574. this.afterSetters();
  9575. }
  9576. // In accordance with animate, run a complete callback
  9577. if (complete) {
  9578. complete.call(this);
  9579. }
  9580. return ret;
  9581. }
  9582. /**
  9583. * Apply a clipping rectangle to this element.
  9584. *
  9585. * @function Highcharts.SVGElement#clip
  9586. *
  9587. * @param {Highcharts.ClipRectElement} [clipRect]
  9588. * The clipping rectangle. If skipped, the current clip is removed.
  9589. *
  9590. * @return {Highcharts.SVGElement}
  9591. * Returns the SVG element to allow chaining.
  9592. */
  9593. clip(clipRect) {
  9594. return this.attr('clip-path', clipRect ?
  9595. 'url(' + this.renderer.url + '#' + clipRect.id + ')' :
  9596. 'none');
  9597. }
  9598. /**
  9599. * Calculate the coordinates needed for drawing a rectangle crisply and
  9600. * return the calculated attributes.
  9601. *
  9602. * @function Highcharts.SVGElement#crisp
  9603. *
  9604. * @param {Highcharts.RectangleObject} rect
  9605. * Rectangle to crisp.
  9606. *
  9607. * @param {number} [strokeWidth]
  9608. * The stroke width to consider when computing crisp positioning. It can
  9609. * also be set directly on the rect parameter.
  9610. *
  9611. * @return {Highcharts.RectangleObject}
  9612. * The modified rectangle arguments.
  9613. */
  9614. crisp(rect, strokeWidth) {
  9615. const wrapper = this;
  9616. strokeWidth = strokeWidth || rect.strokeWidth || 0;
  9617. // Math.round because strokeWidth can sometimes have roundoff errors
  9618. const normalizer = Math.round(strokeWidth) % 2 / 2;
  9619. // normalize for crisp edges
  9620. rect.x = Math.floor(rect.x || wrapper.x || 0) + normalizer;
  9621. rect.y = Math.floor(rect.y || wrapper.y || 0) + normalizer;
  9622. rect.width = Math.floor((rect.width || wrapper.width || 0) - 2 * normalizer);
  9623. rect.height = Math.floor((rect.height || wrapper.height || 0) - 2 * normalizer);
  9624. if (defined(rect.strokeWidth)) {
  9625. rect.strokeWidth = strokeWidth;
  9626. }
  9627. return rect;
  9628. }
  9629. /**
  9630. * Build and apply an SVG gradient out of a common JavaScript configuration
  9631. * object. This function is called from the attribute setters. An event
  9632. * hook is added for supporting other complex color types.
  9633. *
  9634. * @private
  9635. * @function Highcharts.SVGElement#complexColor
  9636. *
  9637. * @param {Highcharts.GradientColorObject|Highcharts.PatternObject} colorOptions
  9638. * The gradient or pattern options structure.
  9639. *
  9640. * @param {string} prop
  9641. * The property to apply, can either be `fill` or `stroke`.
  9642. *
  9643. * @param {Highcharts.SVGDOMElement} elem
  9644. * SVG element to apply the gradient on.
  9645. */
  9646. complexColor(colorOptions, prop, elem) {
  9647. const renderer = this.renderer;
  9648. let colorObject, gradName, gradAttr, radAttr, gradients, stops, stopColor, stopOpacity, radialReference, id, key = [], value;
  9649. fireEvent(this.renderer, 'complexColor', {
  9650. args: arguments
  9651. }, function () {
  9652. // Apply linear or radial gradients
  9653. if (colorOptions.radialGradient) {
  9654. gradName = 'radialGradient';
  9655. }
  9656. else if (colorOptions.linearGradient) {
  9657. gradName = 'linearGradient';
  9658. }
  9659. if (gradName) {
  9660. gradAttr = colorOptions[gradName];
  9661. gradients = renderer.gradients;
  9662. stops = colorOptions.stops;
  9663. radialReference = elem.radialReference;
  9664. // Keep < 2.2 kompatibility
  9665. if (isArray(gradAttr)) {
  9666. colorOptions[gradName] = gradAttr = {
  9667. x1: gradAttr[0],
  9668. y1: gradAttr[1],
  9669. x2: gradAttr[2],
  9670. y2: gradAttr[3],
  9671. gradientUnits: 'userSpaceOnUse'
  9672. };
  9673. }
  9674. // Correct the radial gradient for the radial reference system
  9675. if (gradName === 'radialGradient' &&
  9676. radialReference &&
  9677. !defined(gradAttr.gradientUnits)) {
  9678. // Save the radial attributes for updating
  9679. radAttr = gradAttr;
  9680. gradAttr = merge(gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' });
  9681. }
  9682. // Build the unique key to detect whether we need to create a
  9683. // new element (#1282)
  9684. objectEach(gradAttr, function (value, n) {
  9685. if (n !== 'id') {
  9686. key.push(n, value);
  9687. }
  9688. });
  9689. objectEach(stops, function (val) {
  9690. key.push(val);
  9691. });
  9692. key = key.join(',');
  9693. // Check if a gradient object with the same config object is
  9694. // created within this renderer
  9695. if (gradients[key]) {
  9696. id = gradients[key].attr('id');
  9697. }
  9698. else {
  9699. // Set the id and create the element
  9700. gradAttr.id = id = uniqueKey();
  9701. const gradientObject = gradients[key] =
  9702. renderer.createElement(gradName)
  9703. .attr(gradAttr)
  9704. .add(renderer.defs);
  9705. gradientObject.radAttr = radAttr;
  9706. // The gradient needs to keep a list of stops to be able to
  9707. // destroy them
  9708. gradientObject.stops = [];
  9709. stops.forEach(function (stop) {
  9710. if (stop[1].indexOf('rgba') === 0) {
  9711. colorObject = Color.parse(stop[1]);
  9712. stopColor = colorObject.get('rgb');
  9713. stopOpacity = colorObject.get('a');
  9714. }
  9715. else {
  9716. stopColor = stop[1];
  9717. stopOpacity = 1;
  9718. }
  9719. const stopObject = renderer.createElement('stop').attr({
  9720. offset: stop[0],
  9721. 'stop-color': stopColor,
  9722. 'stop-opacity': stopOpacity
  9723. }).add(gradientObject);
  9724. // Add the stop element to the gradient
  9725. gradientObject.stops.push(stopObject);
  9726. });
  9727. }
  9728. // Set the reference to the gradient object
  9729. value = 'url(' + renderer.url + '#' + id + ')';
  9730. elem.setAttribute(prop, value);
  9731. elem.gradient = key;
  9732. // Allow the color to be concatenated into tooltips formatters
  9733. // etc. (#2995)
  9734. colorOptions.toString = function () {
  9735. return value;
  9736. };
  9737. }
  9738. });
  9739. }
  9740. /**
  9741. * Set styles for the element. In addition to CSS styles supported by
  9742. * native SVG and HTML elements, there are also some custom made for
  9743. * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
  9744. * elements.
  9745. *
  9746. * @sample highcharts/members/renderer-text-on-chart/
  9747. * Styled text
  9748. *
  9749. * @function Highcharts.SVGElement#css
  9750. *
  9751. * @param {Highcharts.CSSObject} styles
  9752. * The new CSS styles.
  9753. *
  9754. * @return {Highcharts.SVGElement}
  9755. * Return the SVG element for chaining.
  9756. */
  9757. css(styles) {
  9758. const oldStyles = this.styles, newStyles = {}, elem = this.element;
  9759. let textWidth, hasNew = !oldStyles;
  9760. // Filter out existing styles to increase performance (#2640)
  9761. if (oldStyles) {
  9762. objectEach(styles, function (value, n) {
  9763. if (oldStyles && oldStyles[n] !== value) {
  9764. newStyles[n] = value;
  9765. hasNew = true;
  9766. }
  9767. });
  9768. }
  9769. if (hasNew) {
  9770. // Merge the new styles with the old ones
  9771. if (oldStyles) {
  9772. styles = extend(oldStyles, newStyles);
  9773. }
  9774. // Get the text width from style
  9775. // Previously set, unset it (#8234)
  9776. if (styles.width === null || styles.width === 'auto') {
  9777. delete this.textWidth;
  9778. // Apply new
  9779. }
  9780. else if (elem.nodeName.toLowerCase() === 'text' &&
  9781. styles.width) {
  9782. textWidth = this.textWidth = pInt(styles.width);
  9783. }
  9784. // store object
  9785. this.styles = styles;
  9786. if (textWidth && (!svg && this.renderer.forExport)) {
  9787. delete styles.width;
  9788. }
  9789. const stylesToApply = merge(styles);
  9790. if (elem.namespaceURI === this.SVG_NS) {
  9791. // These CSS properties are interpreted internally by the SVG
  9792. // renderer, but are not supported by SVG and should not be
  9793. // added to the DOM. In styled mode, no CSS should find its way
  9794. // to the DOM whatsoever (#6173, #6474).
  9795. ['textOutline', 'textOverflow', 'width'].forEach((key) => (stylesToApply &&
  9796. delete stylesToApply[key]));
  9797. // SVG requires fill for text
  9798. if (stylesToApply.color) {
  9799. stylesToApply.fill = stylesToApply.color;
  9800. }
  9801. }
  9802. css(elem, stylesToApply);
  9803. }
  9804. if (this.added) {
  9805. // Rebuild text after added. Cache mechanisms in the buildText will
  9806. // prevent building if there are no significant changes.
  9807. if (this.element.nodeName === 'text') {
  9808. this.renderer.buildText(this);
  9809. }
  9810. // Apply text outline after added
  9811. if (styles.textOutline) {
  9812. this.applyTextOutline(styles.textOutline);
  9813. }
  9814. }
  9815. return this;
  9816. }
  9817. /**
  9818. * @private
  9819. * @function Highcharts.SVGElement#dashstyleSetter
  9820. * @param {string} value
  9821. */
  9822. dashstyleSetter(value) {
  9823. let i, strokeWidth = this['stroke-width'];
  9824. // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
  9825. // strokeWidth function, we should be able to use that instead.
  9826. if (strokeWidth === 'inherit') {
  9827. strokeWidth = 1;
  9828. }
  9829. value = value && value.toLowerCase();
  9830. if (value) {
  9831. const v = value
  9832. .replace('shortdashdotdot', '3,1,1,1,1,1,')
  9833. .replace('shortdashdot', '3,1,1,1')
  9834. .replace('shortdot', '1,1,')
  9835. .replace('shortdash', '3,1,')
  9836. .replace('longdash', '8,3,')
  9837. .replace(/dot/g, '1,3,')
  9838. .replace('dash', '4,3,')
  9839. .replace(/,$/, '')
  9840. .split(','); // ending comma
  9841. i = v.length;
  9842. while (i--) {
  9843. v[i] = '' + (pInt(v[i]) * pick(strokeWidth, NaN));
  9844. }
  9845. value = v.join(',').replace(/NaN/g, 'none'); // #3226
  9846. this.element.setAttribute('stroke-dasharray', value);
  9847. }
  9848. }
  9849. /**
  9850. * Destroy the element and element wrapper and clear up the DOM and event
  9851. * hooks.
  9852. *
  9853. * @function Highcharts.SVGElement#destroy
  9854. */
  9855. destroy() {
  9856. const wrapper = this, element = wrapper.element || {}, renderer = wrapper.renderer, ownerSVGElement = element.ownerSVGElement;
  9857. let parentToClean = (element.nodeName === 'SPAN' &&
  9858. wrapper.parentGroup ||
  9859. void 0), grandParent, i;
  9860. // remove events
  9861. element.onclick = element.onmouseout = element.onmouseover =
  9862. element.onmousemove = element.point = null;
  9863. stop(wrapper); // stop running animations
  9864. if (wrapper.clipPath && ownerSVGElement) {
  9865. const clipPath = wrapper.clipPath;
  9866. // Look for existing references to this clipPath and remove them
  9867. // before destroying the element (#6196).
  9868. // The upper case version is for Edge
  9869. [].forEach.call(ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'), function (el) {
  9870. if (el.getAttribute('clip-path').indexOf(clipPath.element.id) > -1) {
  9871. el.removeAttribute('clip-path');
  9872. }
  9873. });
  9874. wrapper.clipPath = clipPath.destroy();
  9875. }
  9876. // Destroy stops in case this is a gradient object @todo old code?
  9877. if (wrapper.stops) {
  9878. for (i = 0; i < wrapper.stops.length; i++) {
  9879. wrapper.stops[i].destroy();
  9880. }
  9881. wrapper.stops.length = 0;
  9882. wrapper.stops = void 0;
  9883. }
  9884. // remove element
  9885. wrapper.safeRemoveChild(element);
  9886. // In case of useHTML, clean up empty containers emulating SVG groups
  9887. // (#1960, #2393, #2697).
  9888. while (parentToClean &&
  9889. parentToClean.div &&
  9890. parentToClean.div.childNodes.length === 0) {
  9891. grandParent = parentToClean.parentGroup;
  9892. wrapper.safeRemoveChild(parentToClean.div);
  9893. delete parentToClean.div;
  9894. parentToClean = grandParent;
  9895. }
  9896. // remove from alignObjects
  9897. if (wrapper.alignTo) {
  9898. erase(renderer.alignedObjects, wrapper);
  9899. }
  9900. objectEach(wrapper, function (val, key) {
  9901. // Destroy child elements of a group
  9902. if (wrapper[key] &&
  9903. wrapper[key].parentGroup === wrapper &&
  9904. wrapper[key].destroy) {
  9905. wrapper[key].destroy();
  9906. }
  9907. // Delete all properties
  9908. delete wrapper[key];
  9909. });
  9910. return;
  9911. }
  9912. /**
  9913. * @private
  9914. * @function Highcharts.SVGElement#dSettter
  9915. * @param {number|string|Highcharts.SVGPathArray} value
  9916. * @param {string} key
  9917. * @param {Highcharts.SVGDOMElement} element
  9918. */
  9919. dSetter(value, key, element) {
  9920. if (isArray(value)) {
  9921. // Backwards compatibility, convert one-dimensional array into an
  9922. // array of segments
  9923. if (typeof value[0] === 'string') {
  9924. value = this.renderer.pathToSegments(value);
  9925. }
  9926. this.pathArray = value;
  9927. value = value.reduce((acc, seg, i) => {
  9928. if (!seg || !seg.join) {
  9929. return (seg || '').toString();
  9930. }
  9931. return (i ? acc + ' ' : '') + seg.join(' ');
  9932. }, '');
  9933. }
  9934. if (/(NaN| {2}|^$)/.test(value)) {
  9935. value = 'M 0 0';
  9936. }
  9937. // Check for cache before resetting. Resetting causes disturbance in the
  9938. // DOM, causing flickering in some cases in Edge/IE (#6747). Also
  9939. // possible performance gain.
  9940. if (this[key] !== value) {
  9941. element.setAttribute(key, value);
  9942. this[key] = value;
  9943. }
  9944. }
  9945. /**
  9946. * Fade out an element by animating its opacity down to 0, and hide it on
  9947. * complete. Used internally for the tooltip.
  9948. *
  9949. * @function Highcharts.SVGElement#fadeOut
  9950. *
  9951. * @param {number} [duration=150]
  9952. * The fade duration in milliseconds.
  9953. */
  9954. fadeOut(duration) {
  9955. const elemWrapper = this;
  9956. elemWrapper.animate({
  9957. opacity: 0
  9958. }, {
  9959. duration: pick(duration, 150),
  9960. complete: function () {
  9961. // #3088, assuming we're only using this for tooltips
  9962. elemWrapper.hide();
  9963. }
  9964. });
  9965. }
  9966. /**
  9967. * @private
  9968. * @function Highcharts.SVGElement#fillSetter
  9969. * @param {Highcharts.ColorType} value
  9970. * @param {string} key
  9971. * @param {Highcharts.SVGDOMElement} element
  9972. */
  9973. fillSetter(value, key, element) {
  9974. if (typeof value === 'string') {
  9975. element.setAttribute(key, value);
  9976. }
  9977. else if (value) {
  9978. this.complexColor(value, key, element);
  9979. }
  9980. }
  9981. /**
  9982. * Get the bounding box (width, height, x and y) for the element. Generally
  9983. * used to get rendered text size. Since this is called a lot in charts,
  9984. * the results are cached based on text properties, in order to save DOM
  9985. * traffic. The returned bounding box includes the rotation, so for example
  9986. * a single text line of rotation 90 will report a greater height, and a
  9987. * width corresponding to the line-height.
  9988. *
  9989. * @sample highcharts/members/renderer-on-chart/
  9990. * Draw a rectangle based on a text's bounding box
  9991. *
  9992. * @function Highcharts.SVGElement#getBBox
  9993. *
  9994. * @param {boolean} [reload]
  9995. * Skip the cache and get the updated DOM bouding box.
  9996. *
  9997. * @param {number} [rot]
  9998. * Override the element's rotation. This is internally used on axis
  9999. * labels with a value of 0 to find out what the bounding box would
  10000. * be have been if it were not rotated.
  10001. *
  10002. * @return {Highcharts.BBoxObject}
  10003. * The bounding box with `x`, `y`, `width` and `height` properties.
  10004. */
  10005. getBBox(reload, rot) {
  10006. const wrapper = this, { alignValue, element, renderer, styles, textStr } = wrapper, { cache, cacheKeys } = renderer, isSVG = element.namespaceURI === wrapper.SVG_NS, rotation = pick(rot, wrapper.rotation, 0), fontSize = renderer.styledMode ? (element &&
  10007. SVGElement.prototype.getStyle.call(element, 'font-size')) : (styles && styles.fontSize);
  10008. let bBox, width, height, toggleTextShadowShim, cacheKey;
  10009. // Avoid undefined and null (#7316)
  10010. if (defined(textStr)) {
  10011. cacheKey = textStr.toString();
  10012. // Since numbers are monospaced, and numerical labels appear a lot
  10013. // in a chart, we assume that a label of n characters has the same
  10014. // bounding box as others of the same length. Unless there is inner
  10015. // HTML in the label. In that case, leave the numbers as is (#5899).
  10016. if (cacheKey.indexOf('<') === -1) {
  10017. cacheKey = cacheKey.replace(/[0-9]/g, '0');
  10018. }
  10019. // Properties that affect bounding box
  10020. cacheKey += [
  10021. '',
  10022. renderer.rootFontSize,
  10023. fontSize,
  10024. rotation,
  10025. wrapper.textWidth,
  10026. alignValue,
  10027. styles && styles.textOverflow,
  10028. styles && styles.fontWeight // #12163
  10029. ].join(',');
  10030. }
  10031. if (cacheKey && !reload) {
  10032. bBox = cache[cacheKey];
  10033. }
  10034. // No cache found
  10035. if (!bBox) {
  10036. // SVG elements
  10037. if (isSVG || renderer.forExport) {
  10038. try { // Fails in Firefox if the container has display: none.
  10039. // When the text shadow shim is used, we need to hide the
  10040. // fake shadows to get the correct bounding box (#3872)
  10041. toggleTextShadowShim = this.fakeTS && function (display) {
  10042. const outline = element.querySelector('.highcharts-text-outline');
  10043. if (outline) {
  10044. css(outline, { display });
  10045. }
  10046. };
  10047. // Workaround for #3842, Firefox reporting wrong bounding
  10048. // box for shadows
  10049. if (isFunction(toggleTextShadowShim)) {
  10050. toggleTextShadowShim('none');
  10051. }
  10052. bBox = element.getBBox ?
  10053. // SVG: use extend because IE9 is not allowed to change
  10054. // width and height in case of rotation (below)
  10055. extend({}, element.getBBox()) : {
  10056. // HTML elements with `exporting.allowHTML` and
  10057. // legacy IE in export mode
  10058. width: element.offsetWidth,
  10059. height: element.offsetHeight,
  10060. x: 0,
  10061. y: 0
  10062. };
  10063. // #3842
  10064. if (isFunction(toggleTextShadowShim)) {
  10065. toggleTextShadowShim('');
  10066. }
  10067. }
  10068. catch (e) {
  10069. '';
  10070. }
  10071. // If the bBox is not set, the try-catch block above failed. The
  10072. // other condition is for Opera that returns a width of
  10073. // -Infinity on hidden elements.
  10074. if (!bBox || bBox.width < 0) {
  10075. bBox = { x: 0, y: 0, width: 0, height: 0 };
  10076. }
  10077. // useHTML within SVG
  10078. }
  10079. else {
  10080. bBox = wrapper.htmlGetBBox();
  10081. }
  10082. // True SVG elements as well as HTML elements in modern browsers
  10083. // using the .useHTML option need to compensated for rotation
  10084. width = bBox.width;
  10085. height = bBox.height;
  10086. // Workaround for wrong bounding box in IE, Edge and Chrome on
  10087. // Windows. With Highcharts' default font, IE and Edge report
  10088. // a box height of 16.899 and Chrome rounds it to 17. If this
  10089. // stands uncorrected, it results in more padding added below
  10090. // the text than above when adding a label border or background.
  10091. // Also vertical positioning is affected.
  10092. // https://jsfiddle.net/highcharts/em37nvuj/
  10093. // (#1101, #1505, #1669, #2568, #6213).
  10094. if (isSVG) {
  10095. bBox.height = height = ({
  10096. '11px,17': 14,
  10097. '13px,20': 16
  10098. }[`${fontSize || ''},${Math.round(height)}`] ||
  10099. height);
  10100. }
  10101. // Adjust for rotated text
  10102. if (rotation) {
  10103. const baseline = Number(element.getAttribute('y') || 0) - bBox.y, alignFactor = {
  10104. 'right': 1,
  10105. 'center': 0.5
  10106. }[alignValue || 0] || 0, rad = rotation * deg2rad, rad90 = (rotation - 90) * deg2rad, wCosRad = width * Math.cos(rad), wSinRad = width * Math.sin(rad), cosRad90 = Math.cos(rad90), sinRad90 = Math.sin(rad90),
  10107. // Find the starting point on the left side baseline of
  10108. // the text
  10109. pX = bBox.x + alignFactor * (width - wCosRad), pY = bBox.y + baseline - alignFactor * wSinRad,
  10110. // Find all corners
  10111. aX = pX + baseline * cosRad90, bX = aX + wCosRad, cX = bX - height * cosRad90, dX = cX - wCosRad, aY = pY + baseline * sinRad90, bY = aY + wSinRad, cY = bY - height * sinRad90, dY = cY - wSinRad;
  10112. // Deduct the bounding box from the corners
  10113. bBox.x = Math.min(aX, bX, cX, dX);
  10114. bBox.y = Math.min(aY, bY, cY, dY);
  10115. bBox.width = Math.max(aX, bX, cX, dX) - bBox.x;
  10116. bBox.height = Math.max(aY, bY, cY, dY) - bBox.y;
  10117. }
  10118. }
  10119. // Cache it. When loading a chart in a hidden iframe in Firefox and
  10120. // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
  10121. if (cacheKey && (textStr === '' || bBox.height > 0)) {
  10122. // Rotate (#4681)
  10123. while (cacheKeys.length > 250) {
  10124. delete cache[cacheKeys.shift()];
  10125. }
  10126. if (!cache[cacheKey]) {
  10127. cacheKeys.push(cacheKey);
  10128. }
  10129. cache[cacheKey] = bBox;
  10130. }
  10131. return bBox;
  10132. }
  10133. /**
  10134. * Get the computed style. Only in styled mode.
  10135. *
  10136. * @example
  10137. * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
  10138. *
  10139. * @function Highcharts.SVGElement#getStyle
  10140. *
  10141. * @param {string} prop
  10142. * The property name to check for.
  10143. *
  10144. * @return {string}
  10145. * The current computed value.
  10146. */
  10147. getStyle(prop) {
  10148. return win
  10149. .getComputedStyle(this.element || this, '')
  10150. .getPropertyValue(prop);
  10151. }
  10152. /**
  10153. * Check if an element has the given class name.
  10154. *
  10155. * @function Highcharts.SVGElement#hasClass
  10156. *
  10157. * @param {string} className
  10158. * The class name to check for.
  10159. *
  10160. * @return {boolean}
  10161. * Whether the class name is found.
  10162. */
  10163. hasClass(className) {
  10164. return ('' + this.attr('class'))
  10165. .split(' ')
  10166. .indexOf(className) !== -1;
  10167. }
  10168. /**
  10169. * Hide the element, similar to setting the `visibility` attribute to
  10170. * `hidden`.
  10171. *
  10172. * @function Highcharts.SVGElement#hide
  10173. *
  10174. * @return {Highcharts.SVGElement}
  10175. * Returns the SVGElement for chaining.
  10176. */
  10177. hide() {
  10178. return this.attr({ visibility: 'hidden' });
  10179. }
  10180. /**
  10181. * @private
  10182. */
  10183. htmlGetBBox() {
  10184. return { height: 0, width: 0, x: 0, y: 0 };
  10185. }
  10186. /**
  10187. * Initialize the SVG element. This function only exists to make the
  10188. * initialization process overridable. It should not be called directly.
  10189. *
  10190. * @function Highcharts.SVGElement#init
  10191. *
  10192. * @param {Highcharts.SVGRenderer} renderer
  10193. * The SVGRenderer instance to initialize to.
  10194. *
  10195. * @param {string} nodeName
  10196. * The SVG node name.
  10197. */
  10198. init(renderer, nodeName) {
  10199. /**
  10200. * The primary DOM node. Each `SVGElement` instance wraps a main DOM
  10201. * node, but may also represent more nodes.
  10202. *
  10203. * @name Highcharts.SVGElement#element
  10204. * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  10205. */
  10206. this.element = nodeName === 'span' ?
  10207. createElement(nodeName) :
  10208. doc.createElementNS(this.SVG_NS, nodeName);
  10209. /**
  10210. * The renderer that the SVGElement belongs to.
  10211. *
  10212. * @name Highcharts.SVGElement#renderer
  10213. * @type {Highcharts.SVGRenderer}
  10214. */
  10215. this.renderer = renderer;
  10216. fireEvent(this, 'afterInit');
  10217. }
  10218. /**
  10219. * Add an event listener. This is a simple setter that replaces the
  10220. * previous event of the same type added by this function, as opposed to
  10221. * the {@link Highcharts#addEvent} function.
  10222. *
  10223. * @sample highcharts/members/element-on/
  10224. * A clickable rectangle
  10225. *
  10226. * @function Highcharts.SVGElement#on
  10227. *
  10228. * @param {string} eventType
  10229. * The event type.
  10230. *
  10231. * @param {Function} handler
  10232. * The handler callback.
  10233. *
  10234. * @return {Highcharts.SVGElement}
  10235. * The SVGElement for chaining.
  10236. */
  10237. on(eventType, handler) {
  10238. const { onEvents } = this;
  10239. if (onEvents[eventType]) {
  10240. // Unbind existing event
  10241. onEvents[eventType]();
  10242. }
  10243. onEvents[eventType] = addEvent(this.element, eventType, handler);
  10244. return this;
  10245. }
  10246. /**
  10247. * @private
  10248. * @function Highcharts.SVGElement#opacitySetter
  10249. * @param {string} value
  10250. * @param {string} key
  10251. * @param {Highcharts.SVGDOMElement} element
  10252. */
  10253. opacitySetter(value, key, element) {
  10254. // Round off to avoid float errors, like tests where opacity lands on
  10255. // 9.86957e-06 instead of 0
  10256. const opacity = Number(Number(value).toFixed(3));
  10257. this.opacity = opacity;
  10258. element.setAttribute(key, opacity);
  10259. }
  10260. /**
  10261. * Remove a class name from the element.
  10262. *
  10263. * @function Highcharts.SVGElement#removeClass
  10264. *
  10265. * @param {string|RegExp} className
  10266. * The class name to remove.
  10267. *
  10268. * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
  10269. */
  10270. removeClass(className) {
  10271. return this.attr('class', ('' + this.attr('class'))
  10272. .replace(isString(className) ?
  10273. new RegExp(`(^| )${className}( |$)`) : // #12064, #13590
  10274. className, ' ')
  10275. .replace(/ +/g, ' ')
  10276. .trim());
  10277. }
  10278. /**
  10279. *
  10280. * @private
  10281. */
  10282. removeTextOutline() {
  10283. const outline = this.element
  10284. .querySelector('tspan.highcharts-text-outline');
  10285. if (outline) {
  10286. this.safeRemoveChild(outline);
  10287. }
  10288. }
  10289. /**
  10290. * Removes an element from the DOM.
  10291. *
  10292. * @private
  10293. * @function Highcharts.SVGElement#safeRemoveChild
  10294. *
  10295. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  10296. * The DOM node to remove.
  10297. */
  10298. safeRemoveChild(element) {
  10299. const parentNode = element.parentNode;
  10300. if (parentNode) {
  10301. parentNode.removeChild(element);
  10302. }
  10303. }
  10304. /**
  10305. * Set the coordinates needed to draw a consistent radial gradient across
  10306. * a shape regardless of positioning inside the chart. Used on pie slices
  10307. * to make all the slices have the same radial reference point.
  10308. *
  10309. * @function Highcharts.SVGElement#setRadialReference
  10310. *
  10311. * @param {Array<number>} coordinates
  10312. * The center reference. The format is `[centerX, centerY, diameter]` in
  10313. * pixels.
  10314. *
  10315. * @return {Highcharts.SVGElement}
  10316. * Returns the SVGElement for chaining.
  10317. */
  10318. setRadialReference(coordinates) {
  10319. const existingGradient = (this.element.gradient &&
  10320. this.renderer.gradients[this.element.gradient]);
  10321. this.element.radialReference = coordinates;
  10322. // On redrawing objects with an existing gradient, the gradient needs
  10323. // to be repositioned (#3801)
  10324. if (existingGradient && existingGradient.radAttr) {
  10325. existingGradient.animate(this.renderer.getRadialAttr(coordinates, existingGradient.radAttr));
  10326. }
  10327. return this;
  10328. }
  10329. /**
  10330. * Set a text path for a `text` or `label` element, allowing the text to
  10331. * flow along a path.
  10332. *
  10333. * In order to unset the path for an existing element, call `setTextPath`
  10334. * with `{ enabled: false }` as the second argument.
  10335. *
  10336. * @sample highcharts/members/renderer-textpath/ Text path demonstrated
  10337. *
  10338. * @function Highcharts.SVGElement#setTextPath
  10339. *
  10340. * @param {Highcharts.SVGElement|undefined} path
  10341. * Path to follow. If undefined, it allows changing options for the
  10342. * existing path.
  10343. *
  10344. * @param {Highcharts.DataLabelsTextPathOptionsObject} textPathOptions
  10345. * Options.
  10346. *
  10347. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  10348. */
  10349. setTextPath(path, textPathOptions) {
  10350. // Defaults
  10351. textPathOptions = merge(true, {
  10352. enabled: true,
  10353. attributes: {
  10354. dy: -5,
  10355. startOffset: '50%',
  10356. textAnchor: 'middle'
  10357. }
  10358. }, textPathOptions);
  10359. const url = this.renderer.url, textWrapper = this.text || this, textPath = textWrapper.textPath, { attributes, enabled } = textPathOptions;
  10360. path = path || (textPath && textPath.path);
  10361. // Remove previously added event
  10362. if (textPath) {
  10363. textPath.undo();
  10364. }
  10365. if (path && enabled) {
  10366. const undo = addEvent(textWrapper, 'afterModifyTree', (e) => {
  10367. if (path && enabled) {
  10368. // Set ID for the path
  10369. let textPathId = path.attr('id');
  10370. if (!textPathId) {
  10371. path.attr('id', textPathId = uniqueKey());
  10372. }
  10373. // Set attributes for the <text>
  10374. const textAttribs = {
  10375. // dx/dy options must by set on <text> (parent), the
  10376. // rest should be set on <textPath>
  10377. x: 0,
  10378. y: 0
  10379. };
  10380. if (defined(attributes.dx)) {
  10381. textAttribs.dx = attributes.dx;
  10382. delete attributes.dx;
  10383. }
  10384. if (defined(attributes.dy)) {
  10385. textAttribs.dy = attributes.dy;
  10386. delete attributes.dy;
  10387. }
  10388. textWrapper.attr(textAttribs);
  10389. // Handle label properties
  10390. this.attr({ transform: '' });
  10391. if (this.box) {
  10392. this.box = this.box.destroy();
  10393. }
  10394. // Wrap the nodes in a textPath
  10395. const children = e.nodes.slice(0);
  10396. e.nodes.length = 0;
  10397. e.nodes[0] = {
  10398. tagName: 'textPath',
  10399. attributes: extend(attributes, {
  10400. 'text-anchor': attributes.textAnchor,
  10401. href: `${url}#${textPathId}`
  10402. }),
  10403. children
  10404. };
  10405. }
  10406. });
  10407. // Set the reference
  10408. textWrapper.textPath = { path, undo };
  10409. }
  10410. else {
  10411. textWrapper.attr({ dx: 0, dy: 0 });
  10412. delete textWrapper.textPath;
  10413. }
  10414. if (this.added) {
  10415. // Rebuild text after added
  10416. textWrapper.textCache = '';
  10417. this.renderer.buildText(textWrapper);
  10418. }
  10419. return this;
  10420. }
  10421. /**
  10422. * Add a shadow to the element. In styled mode, this method is not used,
  10423. * instead use `defs` and filters.
  10424. *
  10425. * @example
  10426. * renderer.rect(10, 100, 100, 100)
  10427. * .attr({ fill: 'red' })
  10428. * .shadow(true);
  10429. *
  10430. * @function Highcharts.SVGElement#shadow
  10431. *
  10432. * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions] The
  10433. * shadow options. If `true`, the default options are applied. If
  10434. * `false`, the current shadow will be removed.
  10435. *
  10436. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  10437. */
  10438. shadow(shadowOptions) {
  10439. var _a;
  10440. const { renderer } = this, options = merge(((_a = this.parentGroup) === null || _a === void 0 ? void 0 : _a.rotation) === 90 ? {
  10441. offsetX: -1,
  10442. offsetY: -1
  10443. } : {}, isObject(shadowOptions) ? shadowOptions : {}), id = renderer.shadowDefinition(options);
  10444. return this.attr({
  10445. filter: shadowOptions ?
  10446. `url(${renderer.url}#${id})` :
  10447. 'none'
  10448. });
  10449. }
  10450. /**
  10451. * Show the element after it has been hidden.
  10452. *
  10453. * @function Highcharts.SVGElement#show
  10454. *
  10455. * @param {boolean} [inherit=true]
  10456. * Set the visibility attribute to `inherit` rather than `visible`.
  10457. * The difference is that an element with `visibility="visible"`
  10458. * will be visible even if the parent is hidden.
  10459. *
  10460. * @return {Highcharts.SVGElement}
  10461. * Returns the SVGElement for chaining.
  10462. */
  10463. show(inherit = true) {
  10464. return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
  10465. }
  10466. /**
  10467. * Set the stroke-width and record it on the SVGElement
  10468. *
  10469. * @private
  10470. * @function Highcharts.SVGElement#strokeSetter
  10471. * @param {number|string|ColorType} value
  10472. * @param {string} key
  10473. * @param {Highcharts.SVGDOMElement} element
  10474. */
  10475. 'stroke-widthSetter'(value, key, element) {
  10476. // Record it for quick access in getter
  10477. this[key] = value;
  10478. element.setAttribute(key, value);
  10479. }
  10480. /**
  10481. * Get the computed stroke width in pixel values. This is used extensively
  10482. * when drawing shapes to ensure the shapes are rendered crisp and
  10483. * positioned correctly relative to each other. Using
  10484. * `shape-rendering: crispEdges` leaves us less control over positioning,
  10485. * for example when we want to stack columns next to each other, or position
  10486. * things pixel-perfectly within the plot box.
  10487. *
  10488. * The common pattern when placing a shape is:
  10489. * - Create the SVGElement and add it to the DOM. In styled mode, it will
  10490. * now receive a stroke width from the style sheet. In classic mode we
  10491. * will add the `stroke-width` attribute.
  10492. * - Read the computed `elem.strokeWidth()`.
  10493. * - Place it based on the stroke width.
  10494. *
  10495. * @function Highcharts.SVGElement#strokeWidth
  10496. *
  10497. * @return {number}
  10498. * The stroke width in pixels. Even if the given stroke widtch (in CSS or by
  10499. * attributes) is based on `em` or other units, the pixel size is returned.
  10500. */
  10501. strokeWidth() {
  10502. // In non-styled mode, read the stroke width as set by .attr
  10503. if (!this.renderer.styledMode) {
  10504. return this['stroke-width'] || 0;
  10505. }
  10506. // In styled mode, read computed stroke width
  10507. const val = this.getStyle('stroke-width');
  10508. let ret = 0, dummy;
  10509. // Read pixel values directly
  10510. if (val.indexOf('px') === val.length - 2) {
  10511. ret = pInt(val);
  10512. // Other values like em, pt etc need to be measured
  10513. }
  10514. else if (val !== '') {
  10515. dummy = doc.createElementNS(SVG_NS, 'rect');
  10516. attr(dummy, {
  10517. width: val,
  10518. 'stroke-width': 0
  10519. });
  10520. this.element.parentNode.appendChild(dummy);
  10521. ret = dummy.getBBox().width;
  10522. dummy.parentNode.removeChild(dummy);
  10523. }
  10524. return ret;
  10525. }
  10526. /**
  10527. * If one of the symbol size affecting parameters are changed,
  10528. * check all the others only once for each call to an element's
  10529. * .attr() method
  10530. *
  10531. * @private
  10532. * @function Highcharts.SVGElement#symbolAttr
  10533. *
  10534. * @param {Highcharts.SVGAttributes} hash
  10535. * The attributes to set.
  10536. */
  10537. symbolAttr(hash) {
  10538. const wrapper = this;
  10539. SVGElement.symbolCustomAttribs.forEach(function (key) {
  10540. wrapper[key] = pick(hash[key], wrapper[key]);
  10541. });
  10542. wrapper.attr({
  10543. d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
  10544. });
  10545. }
  10546. /**
  10547. * @private
  10548. * @function Highcharts.SVGElement#textSetter
  10549. * @param {string} value
  10550. */
  10551. textSetter(value) {
  10552. if (value !== this.textStr) {
  10553. // Delete size caches when the text changes
  10554. // delete this.bBox; // old code in series-label
  10555. delete this.textPxLength;
  10556. this.textStr = value;
  10557. if (this.added) {
  10558. this.renderer.buildText(this);
  10559. }
  10560. }
  10561. }
  10562. /**
  10563. * @private
  10564. * @function Highcharts.SVGElement#titleSetter
  10565. * @param {string} value
  10566. */
  10567. titleSetter(value) {
  10568. const el = this.element;
  10569. const titleNode = el.getElementsByTagName('title')[0] ||
  10570. doc.createElementNS(this.SVG_NS, 'title');
  10571. // Move to first child
  10572. if (el.insertBefore) {
  10573. el.insertBefore(titleNode, el.firstChild);
  10574. }
  10575. else {
  10576. el.appendChild(titleNode);
  10577. }
  10578. // Replace text content and escape markup
  10579. titleNode.textContent =
  10580. // #3276, #3895
  10581. String(pick(value, ''))
  10582. .replace(/<[^>]*>/g, '')
  10583. .replace(/&lt;/g, '<')
  10584. .replace(/&gt;/g, '>');
  10585. }
  10586. /**
  10587. * Bring the element to the front. Alternatively, a new zIndex can be set.
  10588. *
  10589. * @sample highcharts/members/element-tofront/
  10590. * Click an element to bring it to front
  10591. *
  10592. * @function Highcharts.SVGElement#toFront
  10593. *
  10594. * @return {Highcharts.SVGElement}
  10595. * Returns the SVGElement for chaining.
  10596. */
  10597. toFront() {
  10598. const element = this.element;
  10599. element.parentNode.appendChild(element);
  10600. return this;
  10601. }
  10602. /**
  10603. * Move an object and its children by x and y values.
  10604. *
  10605. * @function Highcharts.SVGElement#translate
  10606. *
  10607. * @param {number} x
  10608. * The x value.
  10609. *
  10610. * @param {number} y
  10611. * The y value.
  10612. *
  10613. * @return {Highcharts.SVGElement}
  10614. * Translated element.
  10615. */
  10616. translate(x, y) {
  10617. return this.attr({
  10618. translateX: x,
  10619. translateY: y
  10620. });
  10621. }
  10622. /**
  10623. * Update the transform attribute based on internal properties. Deals with
  10624. * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
  10625. * attributes and updates the SVG `transform` attribute.
  10626. *
  10627. * @private
  10628. * @function Highcharts.SVGElement#updateTransform
  10629. */
  10630. updateTransform() {
  10631. const { element, matrix, rotation = 0, scaleX, scaleY, translateX = 0, translateY = 0 } = this;
  10632. // Apply translate. Nearly all transformed elements have translation,
  10633. // so instead of checking for translate = 0, do it always (#1767,
  10634. // #1846).
  10635. const transform = ['translate(' + translateX + ',' + translateY + ')'];
  10636. // apply matrix
  10637. if (defined(matrix)) {
  10638. transform.push('matrix(' + matrix.join(',') + ')');
  10639. }
  10640. // Apply rotation
  10641. if (rotation) { // text rotation or inverted chart
  10642. transform.push('rotate(' + rotation + ' ' +
  10643. pick(this.rotationOriginX, element.getAttribute('x'), 0) +
  10644. ' ' +
  10645. pick(this.rotationOriginY, element.getAttribute('y') || 0) + ')');
  10646. }
  10647. // apply scale
  10648. if (defined(scaleX) || defined(scaleY)) {
  10649. transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
  10650. }
  10651. if (transform.length && !(this.text || this).textPath) {
  10652. element.setAttribute('transform', transform.join(' '));
  10653. }
  10654. }
  10655. /**
  10656. * @private
  10657. * @function Highcharts.SVGElement#visibilitySetter
  10658. *
  10659. * @param {string} value
  10660. *
  10661. * @param {string} key
  10662. *
  10663. * @param {Highcharts.SVGDOMElement} element
  10664. *
  10665. */
  10666. visibilitySetter(value, key, element) {
  10667. // IE9-11 doesn't handle visibilty:inherit well, so we remove the
  10668. // attribute instead (#2881, #3909)
  10669. if (value === 'inherit') {
  10670. element.removeAttribute(key);
  10671. }
  10672. else if (this[key] !== value) { // #6747
  10673. element.setAttribute(key, value);
  10674. }
  10675. this[key] = value;
  10676. }
  10677. /**
  10678. * @private
  10679. * @function Highcharts.SVGElement#xGetter
  10680. */
  10681. xGetter(key) {
  10682. if (this.element.nodeName === 'circle') {
  10683. if (key === 'x') {
  10684. key = 'cx';
  10685. }
  10686. else if (key === 'y') {
  10687. key = 'cy';
  10688. }
  10689. }
  10690. return this._defaultGetter(key);
  10691. }
  10692. /**
  10693. * @private
  10694. * @function Highcharts.SVGElement#zIndexSetter
  10695. */
  10696. zIndexSetter(value, key) {
  10697. const renderer = this.renderer, parentGroup = this.parentGroup, parentWrapper = parentGroup || renderer, parentNode = parentWrapper.element || renderer.box, element = this.element, svgParent = parentNode === renderer.box;
  10698. let childNodes, otherElement, otherZIndex, inserted = false, undefinedOtherZIndex, run = this.added, i;
  10699. if (defined(value)) {
  10700. // So we can read it for other elements in the group
  10701. element.setAttribute('data-z-index', value);
  10702. value = +value;
  10703. if (this[key] === value) {
  10704. // Only update when needed (#3865)
  10705. run = false;
  10706. }
  10707. }
  10708. else if (defined(this[key])) {
  10709. element.removeAttribute('data-z-index');
  10710. }
  10711. this[key] = value;
  10712. // Insert according to this and other elements' zIndex. Before .add() is
  10713. // called, nothing is done. Then on add, or by later calls to
  10714. // zIndexSetter, the node is placed on the right place in the DOM.
  10715. if (run) {
  10716. value = this.zIndex;
  10717. if (value && parentGroup) {
  10718. parentGroup.handleZ = true;
  10719. }
  10720. childNodes = parentNode.childNodes;
  10721. for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
  10722. otherElement = childNodes[i];
  10723. otherZIndex = otherElement.getAttribute('data-z-index');
  10724. undefinedOtherZIndex = !defined(otherZIndex);
  10725. if (otherElement !== element) {
  10726. if (
  10727. // Negative zIndex versus no zIndex:
  10728. // On all levels except the highest. If the parent is
  10729. // <svg>, then we don't want to put items before <desc>
  10730. // or <defs>
  10731. value < 0 &&
  10732. undefinedOtherZIndex &&
  10733. !svgParent &&
  10734. !i) {
  10735. parentNode.insertBefore(element, childNodes[i]);
  10736. inserted = true;
  10737. }
  10738. else if (
  10739. // Insert after the first element with a lower zIndex
  10740. pInt(otherZIndex) <= value ||
  10741. // If negative zIndex, add this before first undefined
  10742. // zIndex element
  10743. (undefinedOtherZIndex &&
  10744. (!defined(value) || value >= 0))) {
  10745. parentNode.insertBefore(element, childNodes[i + 1]);
  10746. inserted = true;
  10747. }
  10748. }
  10749. }
  10750. if (!inserted) {
  10751. parentNode.insertBefore(element, childNodes[svgParent ? 3 : 0]);
  10752. inserted = true;
  10753. }
  10754. }
  10755. return inserted;
  10756. }
  10757. }
  10758. // Custom attributes used for symbols, these should be filtered out when
  10759. // setting SVGElement attributes (#9375).
  10760. SVGElement.symbolCustomAttribs = [
  10761. 'anchorX',
  10762. 'anchorY',
  10763. 'clockwise',
  10764. 'end',
  10765. 'height',
  10766. 'innerR',
  10767. 'r',
  10768. 'start',
  10769. 'width',
  10770. 'x',
  10771. 'y'
  10772. ];
  10773. // Some shared setters and getters
  10774. SVGElement.prototype.strokeSetter = SVGElement.prototype.fillSetter;
  10775. SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
  10776. SVGElement.prototype.matrixSetter =
  10777. SVGElement.prototype.rotationOriginXSetter =
  10778. SVGElement.prototype.rotationOriginYSetter =
  10779. SVGElement.prototype.rotationSetter =
  10780. SVGElement.prototype.scaleXSetter =
  10781. SVGElement.prototype.scaleYSetter =
  10782. SVGElement.prototype.translateXSetter =
  10783. SVGElement.prototype.translateYSetter =
  10784. SVGElement.prototype.verticalAlignSetter = function (value, key) {
  10785. this[key] = value;
  10786. this.doTransform = true;
  10787. };
  10788. /* *
  10789. *
  10790. * Default Export
  10791. *
  10792. * */
  10793. /* *
  10794. *
  10795. * API Declarations
  10796. *
  10797. * */
  10798. /**
  10799. * Reference to the global SVGElement class as a workaround for a name conflict
  10800. * in the Highcharts namespace.
  10801. *
  10802. * @global
  10803. * @typedef {global.SVGElement} GlobalSVGElement
  10804. *
  10805. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  10806. */
  10807. /**
  10808. * The horizontal alignment of an element.
  10809. *
  10810. * @typedef {"center"|"left"|"right"} Highcharts.AlignValue
  10811. */
  10812. /**
  10813. * Options to align the element relative to the chart or another box.
  10814. *
  10815. * @interface Highcharts.AlignObject
  10816. */ /**
  10817. * Horizontal alignment. Can be one of `left`, `center` and `right`.
  10818. *
  10819. * @name Highcharts.AlignObject#align
  10820. * @type {Highcharts.AlignValue|undefined}
  10821. *
  10822. * @default left
  10823. */ /**
  10824. * Vertical alignment. Can be one of `top`, `middle` and `bottom`.
  10825. *
  10826. * @name Highcharts.AlignObject#verticalAlign
  10827. * @type {Highcharts.VerticalAlignValue|undefined}
  10828. *
  10829. * @default top
  10830. */ /**
  10831. * Horizontal pixel offset from alignment.
  10832. *
  10833. * @name Highcharts.AlignObject#x
  10834. * @type {number|undefined}
  10835. *
  10836. * @default 0
  10837. */ /**
  10838. * Vertical pixel offset from alignment.
  10839. *
  10840. * @name Highcharts.AlignObject#y
  10841. * @type {number|undefined}
  10842. *
  10843. * @default 0
  10844. */ /**
  10845. * Use the `transform` attribute with translateX and translateY custom
  10846. * attributes to align this elements rather than `x` and `y` attributes.
  10847. *
  10848. * @name Highcharts.AlignObject#alignByTranslate
  10849. * @type {boolean|undefined}
  10850. *
  10851. * @default false
  10852. */
  10853. /**
  10854. * Bounding box of an element.
  10855. *
  10856. * @interface Highcharts.BBoxObject
  10857. * @extends Highcharts.PositionObject
  10858. */ /**
  10859. * Height of the bounding box.
  10860. *
  10861. * @name Highcharts.BBoxObject#height
  10862. * @type {number}
  10863. */ /**
  10864. * Width of the bounding box.
  10865. *
  10866. * @name Highcharts.BBoxObject#width
  10867. * @type {number}
  10868. */ /**
  10869. * Horizontal position of the bounding box.
  10870. *
  10871. * @name Highcharts.BBoxObject#x
  10872. * @type {number}
  10873. */ /**
  10874. * Vertical position of the bounding box.
  10875. *
  10876. * @name Highcharts.BBoxObject#y
  10877. * @type {number}
  10878. */
  10879. /**
  10880. * An object of key-value pairs for SVG attributes. Attributes in Highcharts
  10881. * elements for the most parts correspond to SVG, but some are specific to
  10882. * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
  10883. * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
  10884. * attributes containing a hyphen are _not_ camel-cased, they should be
  10885. * quoted to preserve the hyphen.
  10886. *
  10887. * @example
  10888. * {
  10889. * 'stroke': '#ff0000', // basic
  10890. * 'stroke-width': 2, // hyphenated
  10891. * 'rotation': 45 // custom
  10892. * 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
  10893. * }
  10894. *
  10895. * @interface Highcharts.SVGAttributes
  10896. */ /**
  10897. * @name Highcharts.SVGAttributes#[key:string]
  10898. * @type {*}
  10899. */ /**
  10900. * @name Highcharts.SVGAttributes#d
  10901. * @type {string|Highcharts.SVGPathArray|undefined}
  10902. */ /**
  10903. * @name Highcharts.SVGAttributes#dx
  10904. * @type {number|undefined}
  10905. */ /**
  10906. * @name Highcharts.SVGAttributes#dy
  10907. * @type {number|undefined}
  10908. */ /**
  10909. * @name Highcharts.SVGAttributes#fill
  10910. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  10911. */ /**
  10912. * @name Highcharts.SVGAttributes#inverted
  10913. * @type {boolean|undefined}
  10914. */ /**
  10915. * @name Highcharts.SVGAttributes#matrix
  10916. * @type {Array<number>|undefined}
  10917. */ /**
  10918. * @name Highcharts.SVGAttributes#rotation
  10919. * @type {number|undefined}
  10920. */ /**
  10921. * @name Highcharts.SVGAttributes#rotationOriginX
  10922. * @type {number|undefined}
  10923. */ /**
  10924. * @name Highcharts.SVGAttributes#rotationOriginY
  10925. * @type {number|undefined}
  10926. */ /**
  10927. * @name Highcharts.SVGAttributes#scaleX
  10928. * @type {number|undefined}
  10929. */ /**
  10930. * @name Highcharts.SVGAttributes#scaleY
  10931. * @type {number|undefined}
  10932. */ /**
  10933. * @name Highcharts.SVGAttributes#stroke
  10934. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  10935. */ /**
  10936. * @name Highcharts.SVGAttributes#style
  10937. * @type {string|Highcharts.CSSObject|undefined}
  10938. */ /**
  10939. * @name Highcharts.SVGAttributes#translateX
  10940. * @type {number|undefined}
  10941. */ /**
  10942. * @name Highcharts.SVGAttributes#translateY
  10943. * @type {number|undefined}
  10944. */ /**
  10945. * @name Highcharts.SVGAttributes#zIndex
  10946. * @type {number|undefined}
  10947. */
  10948. /**
  10949. * An SVG DOM element. The type is a reference to the regular SVGElement in the
  10950. * global scope.
  10951. *
  10952. * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
  10953. *
  10954. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  10955. */
  10956. /**
  10957. * The vertical alignment of an element.
  10958. *
  10959. * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignValue
  10960. */
  10961. ''; // keeps doclets above in JS file
  10962. return SVGElement;
  10963. });
  10964. _registerModule(_modules, 'Core/Renderer/RendererRegistry.js', [_modules['Core/Globals.js']], function (H) {
  10965. /* *
  10966. *
  10967. * (c) 2010-2021 Torstein Honsi
  10968. *
  10969. * License: www.highcharts.com/license
  10970. *
  10971. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10972. *
  10973. * */
  10974. /* *
  10975. *
  10976. * Namespace
  10977. *
  10978. * */
  10979. var RendererRegistry;
  10980. (function (RendererRegistry) {
  10981. /* *
  10982. *
  10983. * Constants
  10984. *
  10985. * */
  10986. RendererRegistry.rendererTypes = {};
  10987. /* *
  10988. *
  10989. * Variables
  10990. *
  10991. * */
  10992. let defaultRenderer;
  10993. /* *
  10994. *
  10995. * Functions
  10996. *
  10997. * */
  10998. /**
  10999. * Gets a registered renderer class. If no renderer type is provided or the
  11000. * requested renderer was not founded, the default renderer is returned.
  11001. *
  11002. * @param {string} [rendererType]
  11003. * Renderer type or the default renderer.
  11004. *
  11005. * @return {Highcharts.Class<Highcharts.SVGRenderer>}
  11006. * Returns the requested renderer class or the default renderer class.
  11007. */
  11008. function getRendererType(rendererType = defaultRenderer) {
  11009. return (RendererRegistry.rendererTypes[rendererType] || RendererRegistry.rendererTypes[defaultRenderer]);
  11010. }
  11011. RendererRegistry.getRendererType = getRendererType;
  11012. /**
  11013. * Register a renderer class.
  11014. *
  11015. * @param {string} rendererType
  11016. * Renderer type to register.
  11017. *
  11018. * @param {Highcharts.Class<Highcharts.SVGRenderer>} rendererClass
  11019. * Returns the requested renderer class or the default renderer class.
  11020. *
  11021. * @param {boolean} setAsDefault
  11022. * Sets the renderer class as the default renderer.
  11023. */
  11024. function registerRendererType(rendererType, rendererClass, setAsDefault) {
  11025. RendererRegistry.rendererTypes[rendererType] = rendererClass;
  11026. if (!defaultRenderer || setAsDefault) {
  11027. defaultRenderer = rendererType;
  11028. H.Renderer = rendererClass; // compatibility
  11029. }
  11030. }
  11031. RendererRegistry.registerRendererType = registerRendererType;
  11032. })(RendererRegistry || (RendererRegistry = {}));
  11033. /* *
  11034. *
  11035. * Default Export
  11036. *
  11037. * */
  11038. return RendererRegistry;
  11039. });
  11040. _registerModule(_modules, 'Core/Renderer/SVG/SVGLabel.js', [_modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (SVGElement, U) {
  11041. /* *
  11042. *
  11043. * (c) 2010-2021 Torstein Honsi
  11044. *
  11045. * License: www.highcharts.com/license
  11046. *
  11047. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11048. *
  11049. * */
  11050. const { defined, extend, isNumber, merge, pick, removeEvent } = U;
  11051. /* *
  11052. *
  11053. * Class
  11054. *
  11055. * */
  11056. /**
  11057. * SVG label to render text.
  11058. * @private
  11059. * @class
  11060. * @name Highcharts.SVGLabel
  11061. * @augments Highcharts.SVGElement
  11062. */
  11063. class SVGLabel extends SVGElement {
  11064. /* *
  11065. *
  11066. * Constructor
  11067. *
  11068. * */
  11069. constructor(renderer, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  11070. super();
  11071. this.paddingLeftSetter = this.paddingSetter;
  11072. this.paddingRightSetter = this.paddingSetter;
  11073. this.init(renderer, 'g');
  11074. this.textStr = str;
  11075. this.x = x;
  11076. this.y = y;
  11077. this.anchorX = anchorX;
  11078. this.anchorY = anchorY;
  11079. this.baseline = baseline;
  11080. this.className = className;
  11081. this.addClass(className === 'button' ?
  11082. 'highcharts-no-tooltip' :
  11083. 'highcharts-label');
  11084. if (className) {
  11085. this.addClass('highcharts-' + className);
  11086. }
  11087. // Create the text element. An undefined text content prevents redundant
  11088. // box calculation (#16121)
  11089. this.text = renderer.text(void 0, 0, 0, useHTML).attr({ zIndex: 1 });
  11090. // Validate the shape argument
  11091. let hasBGImage;
  11092. if (typeof shape === 'string') {
  11093. hasBGImage = /^url\((.*?)\)$/.test(shape);
  11094. if (hasBGImage || this.renderer.symbols[shape]) {
  11095. this.symbolKey = shape;
  11096. }
  11097. }
  11098. this.bBox = SVGLabel.emptyBBox;
  11099. this.padding = 3;
  11100. this.baselineOffset = 0;
  11101. this.needsBox = renderer.styledMode || hasBGImage;
  11102. this.deferredAttr = {};
  11103. this.alignFactor = 0;
  11104. }
  11105. /* *
  11106. *
  11107. * Functions
  11108. *
  11109. * */
  11110. alignSetter(value) {
  11111. const alignFactor = ({
  11112. left: 0,
  11113. center: 0.5,
  11114. right: 1
  11115. })[value];
  11116. if (alignFactor !== this.alignFactor) {
  11117. this.alignFactor = alignFactor;
  11118. // Bounding box exists, means we're dynamically changing
  11119. if (this.bBox && isNumber(this.xSetting)) {
  11120. this.attr({ x: this.xSetting }); // #5134
  11121. }
  11122. }
  11123. }
  11124. anchorXSetter(value, key) {
  11125. this.anchorX = value;
  11126. this.boxAttr(key, Math.round(value) - this.getCrispAdjust() - this.xSetting);
  11127. }
  11128. anchorYSetter(value, key) {
  11129. this.anchorY = value;
  11130. this.boxAttr(key, value - this.ySetting);
  11131. }
  11132. /*
  11133. * Set a box attribute, or defer it if the box is not yet created
  11134. */
  11135. boxAttr(key, value) {
  11136. if (this.box) {
  11137. this.box.attr(key, value);
  11138. }
  11139. else {
  11140. this.deferredAttr[key] = value;
  11141. }
  11142. }
  11143. /*
  11144. * Pick up some properties and apply them to the text instead of the
  11145. * wrapper.
  11146. */
  11147. css(styles) {
  11148. if (styles) {
  11149. const textStyles = {};
  11150. // Create a copy to avoid altering the original object
  11151. // (#537)
  11152. styles = merge(styles);
  11153. SVGLabel.textProps.forEach((prop) => {
  11154. if (typeof styles[prop] !== 'undefined') {
  11155. textStyles[prop] = styles[prop];
  11156. delete styles[prop];
  11157. }
  11158. });
  11159. this.text.css(textStyles);
  11160. // Update existing text, box (#9400, #12163, #18212)
  11161. if ('fontSize' in textStyles || 'fontWeight' in textStyles) {
  11162. this.updateTextPadding();
  11163. }
  11164. else if ('width' in textStyles || 'textOverflow' in textStyles) {
  11165. this.updateBoxSize();
  11166. }
  11167. }
  11168. return SVGElement.prototype.css.call(this, styles);
  11169. }
  11170. /*
  11171. * Destroy and release memory.
  11172. */
  11173. destroy() {
  11174. // Added by button implementation
  11175. removeEvent(this.element, 'mouseenter');
  11176. removeEvent(this.element, 'mouseleave');
  11177. if (this.text) {
  11178. this.text.destroy();
  11179. }
  11180. if (this.box) {
  11181. this.box = this.box.destroy();
  11182. }
  11183. // Call base implementation to destroy the rest
  11184. SVGElement.prototype.destroy.call(this);
  11185. return void 0;
  11186. }
  11187. fillSetter(value, key) {
  11188. if (value) {
  11189. this.needsBox = true;
  11190. }
  11191. // for animation getter (#6776)
  11192. this.fill = value;
  11193. this.boxAttr(key, value);
  11194. }
  11195. /*
  11196. * Return the bounding box of the box, not the group.
  11197. */
  11198. getBBox() {
  11199. // If we have a text string and the DOM bBox was 0, it typically means
  11200. // that the label was first rendered hidden, so we need to update the
  11201. // bBox (#15246)
  11202. if (this.textStr && this.bBox.width === 0 && this.bBox.height === 0) {
  11203. this.updateBoxSize();
  11204. }
  11205. const padding = this.padding;
  11206. const paddingLeft = pick(this.paddingLeft, padding);
  11207. return {
  11208. width: this.width,
  11209. height: this.height,
  11210. x: this.bBox.x - paddingLeft,
  11211. y: this.bBox.y - padding
  11212. };
  11213. }
  11214. getCrispAdjust() {
  11215. return this.renderer.styledMode && this.box ?
  11216. this.box.strokeWidth() % 2 / 2 :
  11217. (this['stroke-width'] ? parseInt(this['stroke-width'], 10) : 0) % 2 / 2;
  11218. }
  11219. heightSetter(value) {
  11220. this.heightSetting = value;
  11221. }
  11222. /*
  11223. * After the text element is added, get the desired size of the border
  11224. * box and add it before the text in the DOM.
  11225. */
  11226. onAdd() {
  11227. this.text.add(this);
  11228. this.attr({
  11229. // Alignment is available now (#3295, 0 not rendered if given
  11230. // as a value)
  11231. text: pick(this.textStr, ''),
  11232. x: this.x || 0,
  11233. y: this.y || 0
  11234. });
  11235. if (this.box && defined(this.anchorX)) {
  11236. this.attr({
  11237. anchorX: this.anchorX,
  11238. anchorY: this.anchorY
  11239. });
  11240. }
  11241. }
  11242. paddingSetter(value, key) {
  11243. if (!isNumber(value)) {
  11244. this[key] = void 0;
  11245. }
  11246. else if (value !== this[key]) {
  11247. this[key] = value;
  11248. this.updateTextPadding();
  11249. }
  11250. }
  11251. rSetter(value, key) {
  11252. this.boxAttr(key, value);
  11253. }
  11254. strokeSetter(value, key) {
  11255. // for animation getter (#6776)
  11256. this.stroke = value;
  11257. this.boxAttr(key, value);
  11258. }
  11259. 'stroke-widthSetter'(value, key) {
  11260. if (value) {
  11261. this.needsBox = true;
  11262. }
  11263. this['stroke-width'] = value;
  11264. this.boxAttr(key, value);
  11265. }
  11266. 'text-alignSetter'(value) {
  11267. this.textAlign = value;
  11268. }
  11269. textSetter(text) {
  11270. if (typeof text !== 'undefined') {
  11271. // Must use .attr to ensure transforms are done (#10009)
  11272. this.text.attr({ text });
  11273. }
  11274. this.updateTextPadding();
  11275. }
  11276. /*
  11277. * This function runs after the label is added to the DOM (when the bounding
  11278. * box is available), and after the text of the label is updated to detect
  11279. * the new bounding box and reflect it in the border box.
  11280. */
  11281. updateBoxSize() {
  11282. const text = this.text, attribs = {}, padding = this.padding,
  11283. // #12165 error when width is null (auto)
  11284. // #12163 when fontweight: bold, recalculate bBox withot cache
  11285. // #3295 && 3514 box failure when string equals 0
  11286. bBox = this.bBox = (((!isNumber(this.widthSetting) ||
  11287. !isNumber(this.heightSetting) ||
  11288. this.textAlign) && defined(text.textStr)) ?
  11289. text.getBBox() :
  11290. SVGLabel.emptyBBox);
  11291. let crispAdjust;
  11292. this.width = this.getPaddedWidth();
  11293. this.height = (this.heightSetting || bBox.height || 0) + 2 * padding;
  11294. const metrics = this.renderer.fontMetrics(text);
  11295. // Update the label-scoped y offset. Math.min because of inline
  11296. // style (#9400)
  11297. this.baselineOffset = padding + Math.min(
  11298. // When applicable, use the font size of the first line (#15707)
  11299. (this.text.firstLineMetrics || metrics).b,
  11300. // When the height is 0, there is no bBox, so go with the font
  11301. // metrics. Highmaps CSS demos.
  11302. bBox.height || Infinity);
  11303. // #15491: Vertical centering
  11304. if (this.heightSetting) {
  11305. this.baselineOffset += (this.heightSetting - metrics.h) / 2;
  11306. }
  11307. if (this.needsBox && !text.textPath) {
  11308. // Create the border box if it is not already present
  11309. if (!this.box) {
  11310. // Symbol definition exists (#5324)
  11311. const box = this.box = this.symbolKey ?
  11312. this.renderer.symbol(this.symbolKey) :
  11313. this.renderer.rect();
  11314. box.addClass(// Don't use label className for buttons
  11315. (this.className === 'button' ?
  11316. '' : 'highcharts-label-box') +
  11317. (this.className ?
  11318. ' highcharts-' + this.className + '-box' : ''));
  11319. box.add(this);
  11320. }
  11321. crispAdjust = this.getCrispAdjust();
  11322. attribs.x = crispAdjust;
  11323. attribs.y = ((this.baseline ? -this.baselineOffset : 0) + crispAdjust);
  11324. // Apply the box attributes
  11325. attribs.width = Math.round(this.width);
  11326. attribs.height = Math.round(this.height);
  11327. this.box.attr(extend(attribs, this.deferredAttr));
  11328. this.deferredAttr = {};
  11329. }
  11330. }
  11331. /*
  11332. * This function runs after setting text or padding, but only if padding
  11333. * is changed.
  11334. */
  11335. updateTextPadding() {
  11336. const text = this.text;
  11337. if (!text.textPath) {
  11338. this.updateBoxSize();
  11339. // Determine y based on the baseline
  11340. const textY = this.baseline ? 0 : this.baselineOffset;
  11341. let textX = pick(this.paddingLeft, this.padding);
  11342. // compensate for alignment
  11343. if (defined(this.widthSetting) &&
  11344. this.bBox &&
  11345. (this.textAlign === 'center' || this.textAlign === 'right')) {
  11346. textX += { center: 0.5, right: 1 }[this.textAlign] * (this.widthSetting - this.bBox.width);
  11347. }
  11348. // update if anything changed
  11349. if (textX !== text.x || textY !== text.y) {
  11350. text.attr('x', textX);
  11351. // #8159 - prevent misplaced data labels in treemap
  11352. // (useHTML: true)
  11353. if (text.hasBoxWidthChanged) {
  11354. this.bBox = text.getBBox(true);
  11355. }
  11356. if (typeof textY !== 'undefined') {
  11357. text.attr('y', textY);
  11358. }
  11359. }
  11360. // record current values
  11361. text.x = textX;
  11362. text.y = textY;
  11363. }
  11364. }
  11365. widthSetter(value) {
  11366. // width:auto => null
  11367. this.widthSetting = isNumber(value) ? value : void 0;
  11368. }
  11369. getPaddedWidth() {
  11370. const padding = this.padding;
  11371. const paddingLeft = pick(this.paddingLeft, padding);
  11372. const paddingRight = pick(this.paddingRight, padding);
  11373. return ((this.widthSetting || this.bBox.width || 0) +
  11374. paddingLeft +
  11375. paddingRight);
  11376. }
  11377. xSetter(value) {
  11378. this.x = value; // for animation getter
  11379. if (this.alignFactor) {
  11380. value -= this.alignFactor * this.getPaddedWidth();
  11381. // Force animation even when setting to the same value (#7898)
  11382. this['forceAnimate:x'] = true;
  11383. }
  11384. this.xSetting = Math.round(value);
  11385. this.attr('translateX', this.xSetting);
  11386. }
  11387. ySetter(value) {
  11388. this.ySetting = this.y = Math.round(value);
  11389. this.attr('translateY', this.ySetting);
  11390. }
  11391. }
  11392. /* *
  11393. *
  11394. * Static Properties
  11395. *
  11396. * */
  11397. SVGLabel.emptyBBox = {
  11398. width: 0,
  11399. height: 0,
  11400. x: 0,
  11401. y: 0
  11402. };
  11403. /**
  11404. * For labels, these CSS properties are applied to the `text` node directly.
  11405. *
  11406. * @private
  11407. * @name Highcharts.SVGLabel#textProps
  11408. * @type {Array<string>}
  11409. */
  11410. SVGLabel.textProps = [
  11411. 'color', 'direction', 'fontFamily', 'fontSize', 'fontStyle',
  11412. 'fontWeight', 'lineHeight', 'textAlign', 'textDecoration',
  11413. 'textOutline', 'textOverflow', 'whiteSpace', 'width'
  11414. ];
  11415. /* *
  11416. *
  11417. * Default Export
  11418. *
  11419. * */
  11420. return SVGLabel;
  11421. });
  11422. _registerModule(_modules, 'Core/Renderer/SVG/Symbols.js', [_modules['Core/Utilities.js']], function (U) {
  11423. /* *
  11424. *
  11425. * (c) 2010-2021 Torstein Honsi
  11426. *
  11427. * License: www.highcharts.com/license
  11428. *
  11429. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11430. *
  11431. * */
  11432. const { defined, isNumber, pick } = U;
  11433. /* *
  11434. *
  11435. * Functions
  11436. *
  11437. * */
  11438. /* eslint-disable require-jsdoc, valid-jsdoc */
  11439. function arc(cx, cy, w, h, options) {
  11440. const arc = [];
  11441. if (options) {
  11442. const start = options.start || 0, rx = pick(options.r, w), ry = pick(options.r, h || w), proximity = 0.001, fullCircle = (Math.abs((options.end || 0) - start - 2 * Math.PI) <
  11443. proximity),
  11444. // Substract a small number to prevent cos and sin of start
  11445. // and end from becoming equal on 360 arcs (related: #1561)
  11446. end = (options.end || 0) - proximity, innerRadius = options.innerR, open = pick(options.open, fullCircle), cosStart = Math.cos(start), sinStart = Math.sin(start), cosEnd = Math.cos(end), sinEnd = Math.sin(end),
  11447. // Proximity takes care of rounding errors around PI (#6971)
  11448. longArc = pick(options.longArc, end - start - Math.PI < proximity ? 0 : 1);
  11449. let arcSegment = [
  11450. 'A',
  11451. rx,
  11452. ry,
  11453. 0,
  11454. longArc,
  11455. pick(options.clockwise, 1),
  11456. cx + rx * cosEnd,
  11457. cy + ry * sinEnd
  11458. ];
  11459. arcSegment.params = { start, end, cx, cy }; // Memo for border radius
  11460. arc.push([
  11461. 'M',
  11462. cx + rx * cosStart,
  11463. cy + ry * sinStart
  11464. ], arcSegment);
  11465. if (defined(innerRadius)) {
  11466. arcSegment = [
  11467. 'A',
  11468. innerRadius,
  11469. innerRadius,
  11470. 0,
  11471. longArc,
  11472. // Clockwise - opposite to the outer arc clockwise
  11473. defined(options.clockwise) ? 1 - options.clockwise : 0,
  11474. cx + innerRadius * cosStart,
  11475. cy + innerRadius * sinStart
  11476. ];
  11477. // Memo for border radius
  11478. arcSegment.params = {
  11479. start: end,
  11480. end: start,
  11481. cx,
  11482. cy
  11483. };
  11484. arc.push(open ?
  11485. [
  11486. 'M',
  11487. cx + innerRadius * cosEnd,
  11488. cy + innerRadius * sinEnd
  11489. ] : [
  11490. 'L',
  11491. cx + innerRadius * cosEnd,
  11492. cy + innerRadius * sinEnd
  11493. ], arcSegment);
  11494. }
  11495. if (!open) {
  11496. arc.push(['Z']);
  11497. }
  11498. }
  11499. return arc;
  11500. }
  11501. /**
  11502. * Callout shape used for default tooltips.
  11503. */
  11504. function callout(x, y, w, h, options) {
  11505. const arrowLength = 6, halfDistance = 6, r = Math.min((options && options.r) || 0, w, h), safeDistance = r + halfDistance, anchorX = options && options.anchorX, anchorY = options && options.anchorY || 0;
  11506. const path = roundedRect(x, y, w, h, { r });
  11507. if (!isNumber(anchorX)) {
  11508. return path;
  11509. }
  11510. // Anchor on right side
  11511. if (x + anchorX >= w) {
  11512. // Chevron
  11513. if (anchorY > y + safeDistance &&
  11514. anchorY < y + h - safeDistance) {
  11515. path.splice(3, 1, ['L', x + w, anchorY - halfDistance], ['L', x + w + arrowLength, anchorY], ['L', x + w, anchorY + halfDistance], ['L', x + w, y + h - r]);
  11516. // Simple connector
  11517. }
  11518. else {
  11519. path.splice(3, 1, ['L', x + w, h / 2], ['L', anchorX, anchorY], ['L', x + w, h / 2], ['L', x + w, y + h - r]);
  11520. }
  11521. // Anchor on left side
  11522. }
  11523. else if (x + anchorX <= 0) {
  11524. // Chevron
  11525. if (anchorY > y + safeDistance &&
  11526. anchorY < y + h - safeDistance) {
  11527. path.splice(7, 1, ['L', x, anchorY + halfDistance], ['L', x - arrowLength, anchorY], ['L', x, anchorY - halfDistance], ['L', x, y + r]);
  11528. // Simple connector
  11529. }
  11530. else {
  11531. path.splice(7, 1, ['L', x, h / 2], ['L', anchorX, anchorY], ['L', x, h / 2], ['L', x, y + r]);
  11532. }
  11533. }
  11534. else if ( // replace bottom
  11535. anchorY &&
  11536. anchorY > h &&
  11537. anchorX > x + safeDistance &&
  11538. anchorX < x + w - safeDistance) {
  11539. path.splice(5, 1, ['L', anchorX + halfDistance, y + h], ['L', anchorX, y + h + arrowLength], ['L', anchorX - halfDistance, y + h], ['L', x + r, y + h]);
  11540. }
  11541. else if ( // replace top
  11542. anchorY &&
  11543. anchorY < 0 &&
  11544. anchorX > x + safeDistance &&
  11545. anchorX < x + w - safeDistance) {
  11546. path.splice(1, 1, ['L', anchorX - halfDistance, y], ['L', anchorX, y - arrowLength], ['L', anchorX + halfDistance, y], ['L', w - r, y]);
  11547. }
  11548. return path;
  11549. }
  11550. function circle(x, y, w, h) {
  11551. // Return a full arc
  11552. return arc(x + w / 2, y + h / 2, w / 2, h / 2, {
  11553. start: Math.PI * 0.5,
  11554. end: Math.PI * 2.5,
  11555. open: false
  11556. });
  11557. }
  11558. function diamond(x, y, w, h) {
  11559. return [
  11560. ['M', x + w / 2, y],
  11561. ['L', x + w, y + h / 2],
  11562. ['L', x + w / 2, y + h],
  11563. ['L', x, y + h / 2],
  11564. ['Z']
  11565. ];
  11566. }
  11567. // #15291
  11568. function rect(x, y, w, h, options) {
  11569. if (options && options.r) {
  11570. return roundedRect(x, y, w, h, options);
  11571. }
  11572. return [
  11573. ['M', x, y],
  11574. ['L', x + w, y],
  11575. ['L', x + w, y + h],
  11576. ['L', x, y + h],
  11577. ['Z']
  11578. ];
  11579. }
  11580. function roundedRect(x, y, w, h, options) {
  11581. const r = (options === null || options === void 0 ? void 0 : options.r) || 0;
  11582. return [
  11583. ['M', x + r, y],
  11584. ['L', x + w - r, y],
  11585. ['A', r, r, 0, 0, 1, x + w, y + r],
  11586. ['L', x + w, y + h - r],
  11587. ['A', r, r, 0, 0, 1, x + w - r, y + h],
  11588. ['L', x + r, y + h],
  11589. ['A', r, r, 0, 0, 1, x, y + h - r],
  11590. ['L', x, y + r],
  11591. ['A', r, r, 0, 0, 1, x + r, y],
  11592. ['Z'] // top-left corner
  11593. ];
  11594. }
  11595. function triangle(x, y, w, h) {
  11596. return [
  11597. ['M', x + w / 2, y],
  11598. ['L', x + w, y + h],
  11599. ['L', x, y + h],
  11600. ['Z']
  11601. ];
  11602. }
  11603. function triangleDown(x, y, w, h) {
  11604. return [
  11605. ['M', x, y],
  11606. ['L', x + w, y],
  11607. ['L', x + w / 2, y + h],
  11608. ['Z']
  11609. ];
  11610. }
  11611. const Symbols = {
  11612. arc,
  11613. callout,
  11614. circle,
  11615. diamond,
  11616. rect,
  11617. roundedRect,
  11618. square: rect,
  11619. triangle,
  11620. 'triangle-down': triangleDown
  11621. };
  11622. /* *
  11623. *
  11624. * Default Export
  11625. *
  11626. * */
  11627. return Symbols;
  11628. });
  11629. _registerModule(_modules, 'Core/Renderer/SVG/TextBuilder.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (AST, H, U) {
  11630. /* *
  11631. *
  11632. * (c) 2010-2020 Torstein Honsi
  11633. *
  11634. * License: www.highcharts.com/license
  11635. *
  11636. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11637. *
  11638. * */
  11639. const { doc, SVG_NS, win } = H;
  11640. const { attr, extend, fireEvent, isString, objectEach, pick } = U;
  11641. /* *
  11642. *
  11643. * Class
  11644. *
  11645. * */
  11646. /**
  11647. * SVG Text Builder
  11648. * @private
  11649. * @class
  11650. * @name Highcharts.TextBuilder
  11651. */
  11652. class TextBuilder {
  11653. constructor(svgElement) {
  11654. const textStyles = svgElement.styles;
  11655. this.renderer = svgElement.renderer;
  11656. this.svgElement = svgElement;
  11657. this.width = svgElement.textWidth;
  11658. this.textLineHeight = textStyles && textStyles.lineHeight;
  11659. this.textOutline = textStyles && textStyles.textOutline;
  11660. this.ellipsis = Boolean(textStyles && textStyles.textOverflow === 'ellipsis');
  11661. this.noWrap = Boolean(textStyles && textStyles.whiteSpace === 'nowrap');
  11662. }
  11663. /**
  11664. * Build an SVG representation of the pseudo HTML given in the object's
  11665. * svgElement.
  11666. *
  11667. * @private
  11668. *
  11669. * @return {void}.
  11670. */
  11671. buildSVG() {
  11672. const wrapper = this.svgElement, textNode = wrapper.element, renderer = wrapper.renderer, textStr = pick(wrapper.textStr, '').toString(), hasMarkup = textStr.indexOf('<') !== -1, childNodes = textNode.childNodes, tempParent = !wrapper.added && renderer.box, regexMatchBreaks = /<br.*?>/g,
  11673. // The buildText code is quite heavy, so if we're not changing
  11674. // something that affects the text, skip it (#6113).
  11675. textCache = [
  11676. textStr,
  11677. this.ellipsis,
  11678. this.noWrap,
  11679. this.textLineHeight,
  11680. this.textOutline,
  11681. wrapper.getStyle('font-size'),
  11682. this.width
  11683. ].join(',');
  11684. if (textCache === wrapper.textCache) {
  11685. return;
  11686. }
  11687. wrapper.textCache = textCache;
  11688. delete wrapper.actualWidth;
  11689. // Remove old text
  11690. for (let i = childNodes.length; i--;) {
  11691. textNode.removeChild(childNodes[i]);
  11692. }
  11693. // Simple strings, add text directly and return
  11694. if (!hasMarkup &&
  11695. !this.ellipsis &&
  11696. !this.width &&
  11697. !wrapper.textPath &&
  11698. (textStr.indexOf(' ') === -1 ||
  11699. (this.noWrap && !regexMatchBreaks.test(textStr)))) {
  11700. textNode.appendChild(doc.createTextNode(this.unescapeEntities(textStr)));
  11701. // Complex strings, add more logic
  11702. }
  11703. else if (textStr !== '') {
  11704. if (tempParent) {
  11705. // attach it to the DOM to read offset width and font size
  11706. tempParent.appendChild(textNode);
  11707. }
  11708. // Step 1. Parse the markup safely and directly into a tree
  11709. // structure.
  11710. const ast = new AST(textStr);
  11711. // Step 2. Do as many as we can of the modifications to the tree
  11712. // structure before it is added to the DOM
  11713. this.modifyTree(ast.nodes);
  11714. ast.addToDOM(textNode);
  11715. // Step 3. Some modifications can't be done until the structure is
  11716. // in the DOM, because we need to read computed metrics.
  11717. this.modifyDOM();
  11718. // Add title if an ellipsis was added
  11719. if (this.ellipsis &&
  11720. (textNode.textContent || '').indexOf('\u2026') !== -1) {
  11721. wrapper.attr('title', this.unescapeEntities(wrapper.textStr || '', ['&lt;', '&gt;']) // #7179
  11722. );
  11723. }
  11724. if (tempParent) {
  11725. tempParent.removeChild(textNode);
  11726. }
  11727. }
  11728. // Apply the text outline
  11729. if (isString(this.textOutline) && wrapper.applyTextOutline) {
  11730. wrapper.applyTextOutline(this.textOutline);
  11731. }
  11732. }
  11733. /**
  11734. * Modify the DOM of the generated SVG structure. This function only does
  11735. * operations that cannot be done until the elements are attached to the
  11736. * DOM, like doing layout based on rendered metrics of the added elements.
  11737. *
  11738. * @private
  11739. *
  11740. */
  11741. modifyDOM() {
  11742. const wrapper = this.svgElement;
  11743. const x = attr(wrapper.element, 'x');
  11744. wrapper.firstLineMetrics = void 0;
  11745. // Remove empty tspans (including breaks) from the beginning because
  11746. // SVG's getBBox doesn't count empty lines. The use case is tooltip
  11747. // where the header is empty. By doing this in the DOM rather than in
  11748. // the AST, we can inspect the textContent directly and don't have to
  11749. // recurse down to look for valid content.
  11750. let firstChild;
  11751. while ((firstChild = wrapper.element.firstChild)) {
  11752. if (/^[\s\u200B]*$/.test(firstChild.textContent || ' ')) {
  11753. wrapper.element.removeChild(firstChild);
  11754. }
  11755. else {
  11756. break;
  11757. }
  11758. }
  11759. // Modify hard line breaks by applying the rendered line height
  11760. [].forEach.call(wrapper.element.querySelectorAll('tspan.highcharts-br'), (br, i) => {
  11761. if (br.nextSibling && br.previousSibling) { // #5261
  11762. if (i === 0 && br.previousSibling.nodeType === 1) {
  11763. wrapper.firstLineMetrics = wrapper.renderer
  11764. .fontMetrics(br.previousSibling);
  11765. }
  11766. attr(br, {
  11767. // Since the break is inserted in front of the next
  11768. // line, we need to use the next sibling for the line
  11769. // height
  11770. dy: this.getLineHeight(br.nextSibling),
  11771. x
  11772. });
  11773. }
  11774. });
  11775. // Constrain the line width, either by ellipsis or wrapping
  11776. const width = this.width || 0;
  11777. if (!width) {
  11778. return;
  11779. }
  11780. // Insert soft line breaks into each text node
  11781. const modifyTextNode = (textNode, parentElement) => {
  11782. const text = textNode.textContent || '';
  11783. const words = text
  11784. .replace(/([^\^])-/g, '$1- ') // Split on hyphens
  11785. // .trim()
  11786. .split(' '); // #1273
  11787. const hasWhiteSpace = !this.noWrap && (words.length > 1 || wrapper.element.childNodes.length > 1);
  11788. const dy = this.getLineHeight(parentElement);
  11789. let lineNo = 0;
  11790. let startAt = wrapper.actualWidth;
  11791. if (this.ellipsis) {
  11792. if (text) {
  11793. this.truncate(textNode, text, void 0, 0,
  11794. // Target width
  11795. Math.max(0,
  11796. // Substract the font face to make room for the
  11797. // ellipsis itself
  11798. width - 0.8 * dy),
  11799. // Build the text to test for
  11800. (text, currentIndex) => text.substring(0, currentIndex) + '\u2026');
  11801. }
  11802. }
  11803. else if (hasWhiteSpace) {
  11804. const lines = [];
  11805. // Remove preceding siblings in order to make the text length
  11806. // calculation correct in the truncate function
  11807. const precedingSiblings = [];
  11808. while (parentElement.firstChild &&
  11809. parentElement.firstChild !== textNode) {
  11810. precedingSiblings.push(parentElement.firstChild);
  11811. parentElement.removeChild(parentElement.firstChild);
  11812. }
  11813. while (words.length) {
  11814. // Apply the previous line
  11815. if (words.length && !this.noWrap && lineNo > 0) {
  11816. lines.push(textNode.textContent || '');
  11817. textNode.textContent = words.join(' ')
  11818. .replace(/- /g, '-');
  11819. }
  11820. // For each line, truncate the remaining
  11821. // words into the line length.
  11822. this.truncate(textNode, void 0, words, lineNo === 0 ? (startAt || 0) : 0, width,
  11823. // Build the text to test for
  11824. (t, currentIndex) => words
  11825. .slice(0, currentIndex)
  11826. .join(' ')
  11827. .replace(/- /g, '-'));
  11828. startAt = wrapper.actualWidth;
  11829. lineNo++;
  11830. }
  11831. // Reinsert the preceding child nodes
  11832. precedingSiblings.forEach((childNode) => {
  11833. parentElement.insertBefore(childNode, textNode);
  11834. });
  11835. // Insert the previous lines before the original text node
  11836. lines.forEach((line) => {
  11837. // Insert the line
  11838. parentElement.insertBefore(doc.createTextNode(line), textNode);
  11839. // Insert a break
  11840. const br = doc.createElementNS(SVG_NS, 'tspan');
  11841. br.textContent = '\u200B'; // zero-width space
  11842. attr(br, { dy, x });
  11843. parentElement.insertBefore(br, textNode);
  11844. });
  11845. }
  11846. };
  11847. // Recurse down the DOM tree and handle line breaks for each text node
  11848. const modifyChildren = ((node) => {
  11849. const childNodes = [].slice.call(node.childNodes);
  11850. childNodes.forEach((childNode) => {
  11851. if (childNode.nodeType === win.Node.TEXT_NODE) {
  11852. modifyTextNode(childNode, node);
  11853. }
  11854. else {
  11855. // Reset word-wrap width readings after hard breaks
  11856. if (childNode.className.baseVal
  11857. .indexOf('highcharts-br') !== -1) {
  11858. wrapper.actualWidth = 0;
  11859. }
  11860. // Recurse down to child node
  11861. modifyChildren(childNode);
  11862. }
  11863. });
  11864. });
  11865. modifyChildren(wrapper.element);
  11866. }
  11867. /**
  11868. * Get the rendered line height of a <text>, <tspan> or pure text node.
  11869. *
  11870. * @param {DOMElementType|Text} node The node to check for
  11871. *
  11872. * @return {number} The rendered line height
  11873. */
  11874. getLineHeight(node) {
  11875. // If the node is a text node, use its parent
  11876. const element = (node.nodeType === win.Node.TEXT_NODE) ?
  11877. node.parentElement :
  11878. node;
  11879. return this.textLineHeight ?
  11880. parseInt(this.textLineHeight.toString(), 10) :
  11881. this.renderer.fontMetrics(element || this.svgElement.element).h;
  11882. }
  11883. /**
  11884. * Transform a pseudo HTML AST node tree into an SVG structure. We do as
  11885. * much heavy lifting as we can here, before doing the final processing in
  11886. * the modifyDOM function. The original data is mutated.
  11887. *
  11888. * @private
  11889. *
  11890. * @param {ASTNode[]} nodes The AST nodes
  11891. *
  11892. */
  11893. modifyTree(nodes) {
  11894. const modifyChild = (node, i) => {
  11895. const { attributes = {}, children, style = {}, tagName } = node, styledMode = this.renderer.styledMode;
  11896. // Apply styling to text tags
  11897. if (tagName === 'b' || tagName === 'strong') {
  11898. if (styledMode) {
  11899. // eslint-disable-next-line dot-notation
  11900. attributes['class'] = 'highcharts-strong';
  11901. }
  11902. else {
  11903. style.fontWeight = 'bold';
  11904. }
  11905. }
  11906. else if (tagName === 'i' || tagName === 'em') {
  11907. if (styledMode) {
  11908. // eslint-disable-next-line dot-notation
  11909. attributes['class'] = 'highcharts-emphasized';
  11910. }
  11911. else {
  11912. style.fontStyle = 'italic';
  11913. }
  11914. }
  11915. // Modify styling
  11916. if (style && style.color) {
  11917. style.fill = style.color;
  11918. }
  11919. // Handle breaks
  11920. if (tagName === 'br') {
  11921. attributes['class'] = 'highcharts-br'; // eslint-disable-line dot-notation
  11922. node.textContent = '\u200B'; // zero-width space
  11923. // Trim whitespace off the beginning of new lines
  11924. const nextNode = nodes[i + 1];
  11925. if (nextNode && nextNode.textContent) {
  11926. nextNode.textContent =
  11927. nextNode.textContent.replace(/^ +/gm, '');
  11928. }
  11929. // If an anchor has direct text node children, the text is unable to
  11930. // wrap because there is no `getSubStringLength` function on the
  11931. // element. Therefore we need to wrap the child text node or nodes
  11932. // in a tspan. #16173.
  11933. }
  11934. else if (tagName === 'a' &&
  11935. children &&
  11936. children.some((child) => child.tagName === '#text')) {
  11937. node.children = [{ children, tagName: 'tspan' }];
  11938. }
  11939. if (tagName !== '#text' && tagName !== 'a') {
  11940. node.tagName = 'tspan';
  11941. }
  11942. extend(node, { attributes, style });
  11943. // Recurse
  11944. if (children) {
  11945. children
  11946. .filter((c) => c.tagName !== '#text')
  11947. .forEach(modifyChild);
  11948. }
  11949. };
  11950. nodes.forEach(modifyChild);
  11951. fireEvent(this.svgElement, 'afterModifyTree', { nodes });
  11952. }
  11953. /*
  11954. * Truncate the text node contents to a given length. Used when the css
  11955. * width is set. If the `textOverflow` is `ellipsis`, the text is truncated
  11956. * character by character to the given length. If not, the text is
  11957. * word-wrapped line by line.
  11958. */
  11959. truncate(textNode, text, words, startAt, width, getString) {
  11960. const svgElement = this.svgElement;
  11961. const { renderer, rotation } = svgElement;
  11962. // Cache the lengths to avoid checking the same twice
  11963. const lengths = [];
  11964. // Word wrap cannot be truncated to shorter than one word, ellipsis
  11965. // text can be completely blank.
  11966. let minIndex = words ? 1 : 0;
  11967. let maxIndex = (text || words || '').length;
  11968. let currentIndex = maxIndex;
  11969. let str;
  11970. let actualWidth;
  11971. const getSubStringLength = function (charEnd, concatenatedEnd) {
  11972. // charEnd is used when finding the character-by-character
  11973. // break for ellipsis, concatenatedEnd is used for word-by-word
  11974. // break for word wrapping.
  11975. const end = concatenatedEnd || charEnd;
  11976. const parentNode = textNode.parentNode;
  11977. if (parentNode && typeof lengths[end] === 'undefined') {
  11978. // Modern browsers
  11979. if (parentNode.getSubStringLength) {
  11980. // Fails with DOM exception on unit-tests/legend/members
  11981. // of unknown reason. Desired width is 0, text content
  11982. // is "5" and end is 1.
  11983. try {
  11984. lengths[end] = startAt +
  11985. parentNode.getSubStringLength(0, words ? end + 1 : end);
  11986. }
  11987. catch (e) {
  11988. '';
  11989. }
  11990. }
  11991. }
  11992. return lengths[end];
  11993. };
  11994. svgElement.rotation = 0; // discard rotation when computing box
  11995. actualWidth = getSubStringLength(textNode.textContent.length);
  11996. if (startAt + actualWidth > width) {
  11997. // Do a binary search for the index where to truncate the text
  11998. while (minIndex <= maxIndex) {
  11999. currentIndex = Math.ceil((minIndex + maxIndex) / 2);
  12000. // When checking words for word-wrap, we need to build the
  12001. // string and measure the subStringLength at the concatenated
  12002. // word length.
  12003. if (words) {
  12004. str = getString(words, currentIndex);
  12005. }
  12006. actualWidth = getSubStringLength(currentIndex, str && str.length - 1);
  12007. if (minIndex === maxIndex) {
  12008. // Complete
  12009. minIndex = maxIndex + 1;
  12010. }
  12011. else if (actualWidth > width) {
  12012. // Too large. Set max index to current.
  12013. maxIndex = currentIndex - 1;
  12014. }
  12015. else {
  12016. // Within width. Set min index to current.
  12017. minIndex = currentIndex;
  12018. }
  12019. }
  12020. // If max index was 0 it means the shortest possible text was also
  12021. // too large. For ellipsis that means only the ellipsis, while for
  12022. // word wrap it means the whole first word.
  12023. if (maxIndex === 0) {
  12024. // Remove ellipsis
  12025. textNode.textContent = '';
  12026. // If the new text length is one less than the original, we don't
  12027. // need the ellipsis
  12028. }
  12029. else if (!(text && maxIndex === text.length - 1)) {
  12030. textNode.textContent = str || getString(text || words, currentIndex);
  12031. }
  12032. }
  12033. // When doing line wrapping, prepare for the next line by removing the
  12034. // items from this line.
  12035. if (words) {
  12036. words.splice(0, currentIndex);
  12037. }
  12038. svgElement.actualWidth = actualWidth;
  12039. svgElement.rotation = rotation; // Apply rotation again.
  12040. }
  12041. /*
  12042. * Un-escape HTML entities based on the public `renderer.escapes` list
  12043. *
  12044. * @private
  12045. *
  12046. * @param {string} inputStr The string to unescape
  12047. * @param {Array<string>} [except] Exceptions
  12048. *
  12049. * @return {string} The processed string
  12050. */
  12051. unescapeEntities(inputStr, except) {
  12052. objectEach(this.renderer.escapes, function (value, key) {
  12053. if (!except || except.indexOf(value) === -1) {
  12054. inputStr = inputStr.toString().replace(new RegExp(value, 'g'), key);
  12055. }
  12056. });
  12057. return inputStr;
  12058. }
  12059. }
  12060. return TextBuilder;
  12061. });
  12062. _registerModule(_modules, 'Core/Renderer/SVG/SVGRenderer.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGLabel.js'], _modules['Core/Renderer/SVG/Symbols.js'], _modules['Core/Renderer/SVG/TextBuilder.js'], _modules['Core/Utilities.js']], function (AST, Color, H, RendererRegistry, SVGElement, SVGLabel, Symbols, TextBuilder, U) {
  12063. /* *
  12064. *
  12065. * (c) 2010-2021 Torstein Honsi
  12066. *
  12067. * License: www.highcharts.com/license
  12068. *
  12069. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  12070. *
  12071. * */
  12072. const { charts, deg2rad, doc, isFirefox, isMS, isWebKit, noop, SVG_NS, symbolSizes, win } = H;
  12073. const { addEvent, attr, createElement, css, defined, destroyObjectProperties, extend, isArray, isNumber, isObject, isString, merge, pick, pInt, uniqueKey } = U;
  12074. /* *
  12075. *
  12076. * Variables
  12077. *
  12078. * */
  12079. let hasInternalReferenceBug;
  12080. /* *
  12081. *
  12082. * Class
  12083. *
  12084. * */
  12085. /* eslint-disable no-invalid-this, valid-jsdoc */
  12086. /**
  12087. * Allows direct access to the Highcharts rendering layer in order to draw
  12088. * primitive shapes like circles, rectangles, paths or text directly on a chart,
  12089. * or independent from any chart. The SVGRenderer represents a wrapper object
  12090. * for SVG in modern browsers.
  12091. *
  12092. * An existing chart's renderer can be accessed through {@link Chart.renderer}.
  12093. * The renderer can also be used completely decoupled from a chart.
  12094. *
  12095. * @sample highcharts/members/renderer-on-chart
  12096. * Annotating a chart programmatically.
  12097. * @sample highcharts/members/renderer-basic
  12098. * Independent SVG drawing.
  12099. *
  12100. * @example
  12101. * // Use directly without a chart object.
  12102. * let renderer = new Highcharts.Renderer(parentNode, 600, 400);
  12103. *
  12104. * @class
  12105. * @name Highcharts.SVGRenderer
  12106. *
  12107. * @param {Highcharts.HTMLDOMElement} container
  12108. * Where to put the SVG in the web page.
  12109. *
  12110. * @param {number} width
  12111. * The width of the SVG.
  12112. *
  12113. * @param {number} height
  12114. * The height of the SVG.
  12115. *
  12116. * @param {Highcharts.CSSObject} [style]
  12117. * The box style, if not in styleMode
  12118. *
  12119. * @param {boolean} [forExport=false]
  12120. * Whether the rendered content is intended for export.
  12121. *
  12122. * @param {boolean} [allowHTML=true]
  12123. * Whether the renderer is allowed to include HTML text, which will be
  12124. * projected on top of the SVG.
  12125. *
  12126. * @param {boolean} [styledMode=false]
  12127. * Whether the renderer belongs to a chart that is in styled mode.
  12128. * If it does, it will avoid setting presentational attributes in
  12129. * some cases, but not when set explicitly through `.attr` and `.css`
  12130. * etc.
  12131. */
  12132. class SVGRenderer {
  12133. /* *
  12134. *
  12135. * Constructors
  12136. *
  12137. * */
  12138. constructor(container, width, height, style, forExport, allowHTML, styledMode) {
  12139. /* *
  12140. *
  12141. * Properties
  12142. *
  12143. * */
  12144. this.alignedObjects = void 0;
  12145. /**
  12146. * The root `svg` node of the renderer.
  12147. *
  12148. * @name Highcharts.SVGRenderer#box
  12149. * @type {Highcharts.SVGDOMElement}
  12150. */
  12151. this.box = void 0;
  12152. /**
  12153. * The wrapper for the root `svg` node of the renderer.
  12154. *
  12155. * @name Highcharts.SVGRenderer#boxWrapper
  12156. * @type {Highcharts.SVGElement}
  12157. */
  12158. this.boxWrapper = void 0;
  12159. this.cache = void 0;
  12160. this.cacheKeys = void 0;
  12161. this.chartIndex = void 0;
  12162. /**
  12163. * A pointer to the `defs` node of the root SVG.
  12164. *
  12165. * @name Highcharts.SVGRenderer#defs
  12166. * @type {Highcharts.SVGElement}
  12167. */
  12168. this.defs = void 0;
  12169. this.globalAnimation = void 0;
  12170. this.gradients = void 0;
  12171. this.height = void 0;
  12172. this.imgCount = void 0;
  12173. this.style = void 0;
  12174. /**
  12175. * Page url used for internal references.
  12176. *
  12177. * @private
  12178. * @name Highcharts.SVGRenderer#url
  12179. * @type {string}
  12180. */
  12181. this.url = void 0;
  12182. this.width = void 0;
  12183. this.init(container, width, height, style, forExport, allowHTML, styledMode);
  12184. }
  12185. /* *
  12186. *
  12187. * Functions
  12188. *
  12189. * */
  12190. /**
  12191. * Initialize the SVGRenderer. Overridable initializer function that takes
  12192. * the same parameters as the constructor.
  12193. *
  12194. * @function Highcharts.SVGRenderer#init
  12195. *
  12196. * @param {Highcharts.HTMLDOMElement} container
  12197. * Where to put the SVG in the web page.
  12198. *
  12199. * @param {number} width
  12200. * The width of the SVG.
  12201. *
  12202. * @param {number} height
  12203. * The height of the SVG.
  12204. *
  12205. * @param {Highcharts.CSSObject} [style]
  12206. * The box style, if not in styleMode
  12207. *
  12208. * @param {boolean} [forExport=false]
  12209. * Whether the rendered content is intended for export.
  12210. *
  12211. * @param {boolean} [allowHTML=true]
  12212. * Whether the renderer is allowed to include HTML text, which will be
  12213. * projected on top of the SVG.
  12214. *
  12215. * @param {boolean} [styledMode=false]
  12216. * Whether the renderer belongs to a chart that is in styled mode. If it
  12217. * does, it will avoid setting presentational attributes in some cases, but
  12218. * not when set explicitly through `.attr` and `.css` etc.
  12219. */
  12220. init(container, width, height, style, forExport, allowHTML, styledMode) {
  12221. const renderer = this, boxWrapper = renderer
  12222. .createElement('svg')
  12223. .attr({
  12224. version: '1.1',
  12225. 'class': 'highcharts-root'
  12226. }), element = boxWrapper.element;
  12227. if (!styledMode) {
  12228. boxWrapper.css(this.getStyle(style));
  12229. }
  12230. container.appendChild(element);
  12231. // Always use ltr on the container, otherwise text-anchor will be
  12232. // flipped and text appear outside labels, buttons, tooltip etc (#3482)
  12233. attr(container, 'dir', 'ltr');
  12234. // For browsers other than IE, add the namespace attribute (#1978)
  12235. if (container.innerHTML.indexOf('xmlns') === -1) {
  12236. attr(element, 'xmlns', this.SVG_NS);
  12237. }
  12238. this.box = element;
  12239. this.boxWrapper = boxWrapper;
  12240. renderer.alignedObjects = [];
  12241. this.url = this.getReferenceURL();
  12242. // Add description
  12243. const desc = this.createElement('desc').add();
  12244. desc.element.appendChild(doc.createTextNode('Created with Highcharts 11.1.0'));
  12245. renderer.defs = this.createElement('defs').add();
  12246. renderer.allowHTML = allowHTML;
  12247. renderer.forExport = forExport;
  12248. renderer.styledMode = styledMode;
  12249. renderer.gradients = {}; // Object where gradient SvgElements are stored
  12250. renderer.cache = {}; // Cache for numerical bounding boxes
  12251. renderer.cacheKeys = [];
  12252. renderer.imgCount = 0;
  12253. renderer.rootFontSize = boxWrapper.getStyle('font-size');
  12254. renderer.setSize(width, height, false);
  12255. // Issue 110 workaround:
  12256. // In Firefox, if a div is positioned by percentage, its pixel position
  12257. // may land between pixels. The container itself doesn't display this,
  12258. // but an SVG element inside this container will be drawn at subpixel
  12259. // precision. In order to draw sharp lines, this must be compensated
  12260. // for. This doesn't seem to work inside iframes though (like in
  12261. // jsFiddle).
  12262. let subPixelFix, rect;
  12263. if (isFirefox && container.getBoundingClientRect) {
  12264. subPixelFix = function () {
  12265. css(container, { left: 0, top: 0 });
  12266. rect = container.getBoundingClientRect();
  12267. css(container, {
  12268. left: (Math.ceil(rect.left) - rect.left) + 'px',
  12269. top: (Math.ceil(rect.top) - rect.top) + 'px'
  12270. });
  12271. };
  12272. // run the fix now
  12273. subPixelFix();
  12274. // run it on resize
  12275. renderer.unSubPixelFix = addEvent(win, 'resize', subPixelFix);
  12276. }
  12277. }
  12278. /**
  12279. * General method for adding a definition to the SVG `defs` tag. Can be used
  12280. * for gradients, fills, filters etc. Styled mode only. A hook for adding
  12281. * general definitions to the SVG's defs tag. Definitions can be referenced
  12282. * from the CSS by its `id`. Read more in
  12283. * [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  12284. * Styled mode only.
  12285. *
  12286. * @function Highcharts.SVGRenderer#definition
  12287. *
  12288. * @param {Highcharts.ASTNode} def
  12289. * A serialized form of an SVG definition, including children.
  12290. *
  12291. * @return {Highcharts.SVGElement}
  12292. * The inserted node.
  12293. */
  12294. definition(def) {
  12295. const ast = new AST([def]);
  12296. return ast.addToDOM(this.defs.element);
  12297. }
  12298. /**
  12299. * Get the prefix needed for internal URL references to work in certain
  12300. * cases. Some older browser versions had a bug where internal url
  12301. * references in SVG attributes, on the form `url(#some-id)`, would fail if
  12302. * a base tag was present in the page. There were also issues with
  12303. * `history.pushState` related to this prefix.
  12304. *
  12305. * Related issues: #24, #672, #1070, #5244.
  12306. *
  12307. * The affected browsers are:
  12308. * - Chrome <= 53 (May 2018)
  12309. * - Firefox <= 51 (January 2017)
  12310. * - Safari/Mac <= 12.1 (2018 or 2019)
  12311. * - Safari/iOS <= 13
  12312. *
  12313. * @todo Remove this hack when time has passed. All the affected browsers
  12314. * are evergreens, so it is increasingly unlikely that users are affected by
  12315. * the bug.
  12316. *
  12317. * @return {string}
  12318. * The prefix to use. An empty string for modern browsers.
  12319. */
  12320. getReferenceURL() {
  12321. if ((isFirefox || isWebKit) &&
  12322. doc.getElementsByTagName('base').length) {
  12323. // Detect if a clip path is taking effect by performing a hit test
  12324. // outside the clipped area. If the hit element is the rectangle
  12325. // that was supposed to be clipped, the bug is present. This only
  12326. // has to be performed once per page load, so we store the result
  12327. // locally in the module.
  12328. if (!defined(hasInternalReferenceBug)) {
  12329. const id = uniqueKey();
  12330. const ast = new AST([{
  12331. tagName: 'svg',
  12332. attributes: {
  12333. width: 8,
  12334. height: 8
  12335. },
  12336. children: [{
  12337. tagName: 'defs',
  12338. children: [{
  12339. tagName: 'clipPath',
  12340. attributes: {
  12341. id
  12342. },
  12343. children: [{
  12344. tagName: 'rect',
  12345. attributes: {
  12346. width: 4,
  12347. height: 4
  12348. }
  12349. }]
  12350. }]
  12351. }, {
  12352. tagName: 'rect',
  12353. attributes: {
  12354. id: 'hitme',
  12355. width: 8,
  12356. height: 8,
  12357. 'clip-path': `url(#${id})`,
  12358. fill: 'rgba(0,0,0,0.001)'
  12359. }
  12360. }]
  12361. }]);
  12362. const svg = ast.addToDOM(doc.body);
  12363. css(svg, {
  12364. position: 'fixed',
  12365. top: 0,
  12366. left: 0,
  12367. zIndex: 9e5
  12368. });
  12369. const hitElement = doc.elementFromPoint(6, 6);
  12370. hasInternalReferenceBug = (hitElement && hitElement.id) === 'hitme';
  12371. doc.body.removeChild(svg);
  12372. }
  12373. if (hasInternalReferenceBug) {
  12374. return win.location.href
  12375. .split('#')[0] // remove the hash
  12376. .replace(/<[^>]*>/g, '') // wing cut HTML
  12377. // escape parantheses and quotes
  12378. .replace(/([\('\)])/g, '\\$1')
  12379. // replace spaces (needed for Safari only)
  12380. .replace(/ /g, '%20');
  12381. }
  12382. }
  12383. return '';
  12384. }
  12385. /**
  12386. * Get the global style setting for the renderer.
  12387. *
  12388. * @private
  12389. * @function Highcharts.SVGRenderer#getStyle
  12390. *
  12391. * @param {Highcharts.CSSObject} style
  12392. * Style settings.
  12393. *
  12394. * @return {Highcharts.CSSObject}
  12395. * The style settings mixed with defaults.
  12396. */
  12397. getStyle(style) {
  12398. this.style = extend({
  12399. fontFamily: 'Helvetica, Arial, sans-serif',
  12400. fontSize: '1rem'
  12401. }, style);
  12402. return this.style;
  12403. }
  12404. /**
  12405. * Apply the global style on the renderer, mixed with the default styles.
  12406. *
  12407. * @function Highcharts.SVGRenderer#setStyle
  12408. *
  12409. * @param {Highcharts.CSSObject} style
  12410. * CSS to apply.
  12411. */
  12412. setStyle(style) {
  12413. this.boxWrapper.css(this.getStyle(style));
  12414. }
  12415. /**
  12416. * Detect whether the renderer is hidden. This happens when one of the
  12417. * parent elements has `display: none`. Used internally to detect when we
  12418. * needto render preliminarily in another div to get the text bounding boxes
  12419. * right.
  12420. *
  12421. * @function Highcharts.SVGRenderer#isHidden
  12422. *
  12423. * @return {boolean}
  12424. * True if it is hidden.
  12425. */
  12426. isHidden() {
  12427. return !this.boxWrapper.getBBox().width;
  12428. }
  12429. /**
  12430. * Destroys the renderer and its allocated members.
  12431. *
  12432. * @function Highcharts.SVGRenderer#destroy
  12433. *
  12434. * @return {null}
  12435. * Pass through value.
  12436. */
  12437. destroy() {
  12438. const renderer = this, rendererDefs = renderer.defs;
  12439. renderer.box = null;
  12440. renderer.boxWrapper = renderer.boxWrapper.destroy();
  12441. // Call destroy on all gradient elements
  12442. destroyObjectProperties(renderer.gradients || {});
  12443. renderer.gradients = null;
  12444. renderer.defs = rendererDefs.destroy();
  12445. // Remove sub pixel fix handler (#982)
  12446. if (renderer.unSubPixelFix) {
  12447. renderer.unSubPixelFix();
  12448. }
  12449. renderer.alignedObjects = null;
  12450. return null;
  12451. }
  12452. /**
  12453. * Create a wrapper for an SVG element. Serves as a factory for
  12454. * {@link SVGElement}, but this function is itself mostly called from
  12455. * primitive factories like {@link SVGRenderer#path}, {@link
  12456. * SVGRenderer#rect} or {@link SVGRenderer#text}.
  12457. *
  12458. * @function Highcharts.SVGRenderer#createElement
  12459. *
  12460. * @param {string} nodeName
  12461. * The node name, for example `rect`, `g` etc.
  12462. *
  12463. * @return {Highcharts.SVGElement}
  12464. * The generated SVGElement.
  12465. */
  12466. createElement(nodeName) {
  12467. const wrapper = new this.Element();
  12468. wrapper.init(this, nodeName);
  12469. return wrapper;
  12470. }
  12471. /**
  12472. * Get converted radial gradient attributes according to the radial
  12473. * reference. Used internally from the {@link SVGElement#colorGradient}
  12474. * function.
  12475. *
  12476. * @private
  12477. * @function Highcharts.SVGRenderer#getRadialAttr
  12478. */
  12479. getRadialAttr(radialReference, gradAttr) {
  12480. return {
  12481. cx: (radialReference[0] - radialReference[2] / 2) +
  12482. (gradAttr.cx || 0) * radialReference[2],
  12483. cy: (radialReference[1] - radialReference[2] / 2) +
  12484. (gradAttr.cy || 0) * radialReference[2],
  12485. r: (gradAttr.r || 0) * radialReference[2]
  12486. };
  12487. }
  12488. /**
  12489. * Create a drop shadow definition and return its id
  12490. *
  12491. * @private
  12492. * @function Highcharts.SVGRenderer#shadowDefinition
  12493. *
  12494. * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions] The
  12495. * shadow options. If `true`, the default options are applied
  12496. */
  12497. shadowDefinition(shadowOptions) {
  12498. const id = [
  12499. `highcharts-drop-shadow-${this.chartIndex}`,
  12500. ...Object.keys(shadowOptions)
  12501. .map((key) => shadowOptions[key])
  12502. ].join('-').replace(/[^a-z0-9\-]/g, ''), options = merge({
  12503. color: '#000000',
  12504. offsetX: 1,
  12505. offsetY: 1,
  12506. opacity: 0.15,
  12507. width: 5
  12508. }, shadowOptions);
  12509. if (!this.defs.element.querySelector(`#${id}`)) {
  12510. this.definition({
  12511. tagName: 'filter',
  12512. attributes: {
  12513. id
  12514. },
  12515. children: [{
  12516. tagName: 'feDropShadow',
  12517. attributes: {
  12518. dx: options.offsetX,
  12519. dy: options.offsetY,
  12520. 'flood-color': options.color,
  12521. // Tuned and modified to keep a preserve compatibility
  12522. // with the old settings
  12523. 'flood-opacity': Math.min(options.opacity * 5, 1),
  12524. stdDeviation: options.width / 2
  12525. }
  12526. }]
  12527. });
  12528. }
  12529. return id;
  12530. }
  12531. /**
  12532. * Parse a simple HTML string into SVG tspans. Called internally when text
  12533. * is set on an SVGElement. The function supports a subset of HTML tags, CSS
  12534. * text features like `width`, `text-overflow`, `white-space`, and also
  12535. * attributes like `href` and `style`.
  12536. *
  12537. * @private
  12538. * @function Highcharts.SVGRenderer#buildText
  12539. *
  12540. * @param {Highcharts.SVGElement} wrapper
  12541. * The parent SVGElement.
  12542. */
  12543. buildText(wrapper) {
  12544. new TextBuilder(wrapper).buildSVG();
  12545. }
  12546. /**
  12547. * Returns white for dark colors and black for bright colors, based on W3C's
  12548. * definition of [Relative luminance](
  12549. * https://www.w3.org/WAI/GL/wiki/Relative_luminance).
  12550. *
  12551. * @function Highcharts.SVGRenderer#getContrast
  12552. *
  12553. * @param {Highcharts.ColorString} color
  12554. * The color to get the contrast for.
  12555. *
  12556. * @return {Highcharts.ColorString}
  12557. * The contrast color, either `#000000` or `#FFFFFF`.
  12558. */
  12559. getContrast(color) {
  12560. // #6216, #17273
  12561. const rgba = Color.parse(color).rgba
  12562. .map((b8) => {
  12563. const c = b8 / 255;
  12564. return c <= 0.03928 ?
  12565. c / 12.92 :
  12566. Math.pow((c + 0.055) / 1.055, 2.4);
  12567. });
  12568. // Relative luminance
  12569. const l = 0.2126 * rgba[0] + 0.7152 * rgba[1] + 0.0722 * rgba[2];
  12570. // Use white or black based on which provides more contrast
  12571. return 1.05 / (l + 0.05) > (l + 0.05) / 0.05 ? '#FFFFFF' : '#000000';
  12572. }
  12573. /**
  12574. * Create a button with preset states.
  12575. *
  12576. * @function Highcharts.SVGRenderer#button
  12577. *
  12578. * @param {string} text
  12579. * The text or HTML to draw.
  12580. *
  12581. * @param {number} x
  12582. * The x position of the button's left side.
  12583. *
  12584. * @param {number} y
  12585. * The y position of the button's top side.
  12586. *
  12587. * @param {Highcharts.EventCallbackFunction<Highcharts.SVGElement>} callback
  12588. * The function to execute on button click or touch.
  12589. *
  12590. * @param {Highcharts.SVGAttributes} [theme]
  12591. * SVG attributes for the normal state.
  12592. *
  12593. * @param {Highcharts.SVGAttributes} [hoverState]
  12594. * SVG attributes for the hover state.
  12595. *
  12596. * @param {Highcharts.SVGAttributes} [selectState]
  12597. * SVG attributes for the pressed state.
  12598. *
  12599. * @param {Highcharts.SVGAttributes} [disabledState]
  12600. * SVG attributes for the disabled state.
  12601. *
  12602. * @param {Highcharts.SymbolKeyValue} [shape=rect]
  12603. * The shape type.
  12604. *
  12605. * @param {boolean} [useHTML=false]
  12606. * Whether to use HTML to render the label.
  12607. *
  12608. * @return {Highcharts.SVGElement}
  12609. * The button element.
  12610. */
  12611. button(text, x, y, callback, theme = {}, hoverState, selectState, disabledState, shape, useHTML) {
  12612. const label = this.label(text, x, y, shape, void 0, void 0, useHTML, void 0, 'button'), styledMode = this.styledMode, states = theme.states || {};
  12613. let curState = 0;
  12614. theme = merge(theme);
  12615. delete theme.states;
  12616. const normalStyle = merge({
  12617. color: "#333333" /* Palette.neutralColor80 */,
  12618. cursor: 'pointer',
  12619. fontSize: '0.8em',
  12620. fontWeight: 'normal'
  12621. }, theme.style);
  12622. delete theme.style;
  12623. // Remove stylable attributes. Pass in the ButtonThemeObject and get the
  12624. // SVGAttributes subset back.
  12625. let normalState = AST.filterUserAttributes(theme);
  12626. // Default, non-stylable attributes
  12627. label.attr(merge({ padding: 8, r: 2 }, normalState));
  12628. // Presentational. The string type is a mistake, it is just for
  12629. // compliance with SVGAttribute and is not used in button theme.
  12630. let hoverStyle, selectStyle, disabledStyle;
  12631. if (!styledMode) {
  12632. // Normal state - prepare the attributes
  12633. normalState = merge({
  12634. fill: "#f7f7f7" /* Palette.neutralColor3 */,
  12635. stroke: "#cccccc" /* Palette.neutralColor20 */,
  12636. 'stroke-width': 1
  12637. }, normalState);
  12638. // Hover state
  12639. hoverState = merge(normalState, {
  12640. fill: "#e6e6e6" /* Palette.neutralColor10 */
  12641. }, AST.filterUserAttributes(hoverState || states.hover || {}));
  12642. hoverStyle = hoverState.style;
  12643. delete hoverState.style;
  12644. // Pressed state
  12645. selectState = merge(normalState, {
  12646. fill: "#e6e9ff" /* Palette.highlightColor10 */,
  12647. style: {
  12648. color: "#000000" /* Palette.neutralColor100 */,
  12649. fontWeight: 'bold'
  12650. }
  12651. }, AST.filterUserAttributes(selectState || states.select || {}));
  12652. selectStyle = selectState.style;
  12653. delete selectState.style;
  12654. // Disabled state
  12655. disabledState = merge(normalState, {
  12656. style: {
  12657. color: "#cccccc" /* Palette.neutralColor20 */
  12658. }
  12659. }, AST.filterUserAttributes(disabledState || states.disabled || {}));
  12660. disabledStyle = disabledState.style;
  12661. delete disabledState.style;
  12662. }
  12663. // Add the events. IE9 and IE10 need mouseover and mouseout to function
  12664. // (#667).
  12665. addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
  12666. if (curState !== 3) {
  12667. label.setState(1);
  12668. }
  12669. });
  12670. addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
  12671. if (curState !== 3) {
  12672. label.setState(curState);
  12673. }
  12674. });
  12675. label.setState = function (state) {
  12676. // Hover state is temporary, don't record it
  12677. if (state !== 1) {
  12678. label.state = curState = state;
  12679. }
  12680. // Update visuals
  12681. label
  12682. .removeClass(/highcharts-button-(normal|hover|pressed|disabled)/)
  12683. .addClass('highcharts-button-' +
  12684. ['normal', 'hover', 'pressed', 'disabled'][state || 0]);
  12685. if (!styledMode) {
  12686. label
  12687. .attr([
  12688. normalState,
  12689. hoverState,
  12690. selectState,
  12691. disabledState
  12692. ][state || 0]);
  12693. const css = [
  12694. normalStyle,
  12695. hoverStyle,
  12696. selectStyle,
  12697. disabledStyle
  12698. ][state || 0];
  12699. if (isObject(css)) {
  12700. label.css(css);
  12701. }
  12702. }
  12703. };
  12704. // Presentational attributes
  12705. if (!styledMode) {
  12706. label
  12707. .attr(normalState)
  12708. .css(extend({ cursor: 'default' }, normalStyle));
  12709. // HTML labels don't need to handle pointer events because click and
  12710. // mouseenter/mouseleave is bound to the underlying <g> element.
  12711. // Should this be reconsidered, we need more complex logic to share
  12712. // events between the <g> and its <div> counterpart, and avoid
  12713. // triggering mouseenter/mouseleave when hovering from one to the
  12714. // other (#17440).
  12715. if (useHTML) {
  12716. label.text.css({ pointerEvents: 'none' });
  12717. }
  12718. }
  12719. return label
  12720. .on('touchstart', (e) => e.stopPropagation())
  12721. .on('click', function (e) {
  12722. if (curState !== 3) {
  12723. callback.call(label, e);
  12724. }
  12725. });
  12726. }
  12727. /**
  12728. * Make a straight line crisper by not spilling out to neighbour pixels.
  12729. *
  12730. * @function Highcharts.SVGRenderer#crispLine
  12731. *
  12732. * @param {Highcharts.SVGPathArray} points
  12733. * The original points on the format `[['M', 0, 0], ['L', 100, 0]]`.
  12734. *
  12735. * @param {number} width
  12736. * The width of the line.
  12737. *
  12738. * @param {string} [roundingFunction=round]
  12739. * The rounding function name on the `Math` object, can be one of
  12740. * `round`, `floor` or `ceil`.
  12741. *
  12742. * @return {Highcharts.SVGPathArray}
  12743. * The original points array, but modified to render crisply.
  12744. */
  12745. crispLine(points, width, roundingFunction = 'round') {
  12746. const start = points[0];
  12747. const end = points[1];
  12748. // Normalize to a crisp line
  12749. if (defined(start[1]) && start[1] === end[1]) {
  12750. // Substract due to #1129. Now bottom and left axis gridlines behave
  12751. // the same.
  12752. start[1] = end[1] =
  12753. Math[roundingFunction](start[1]) - (width % 2 / 2);
  12754. }
  12755. if (defined(start[2]) && start[2] === end[2]) {
  12756. start[2] = end[2] =
  12757. Math[roundingFunction](start[2]) + (width % 2 / 2);
  12758. }
  12759. return points;
  12760. }
  12761. /**
  12762. * Draw a path, wraps the SVG `path` element.
  12763. *
  12764. * @sample highcharts/members/renderer-path-on-chart/
  12765. * Draw a path in a chart
  12766. * @sample highcharts/members/renderer-path/
  12767. * Draw a path independent from a chart
  12768. *
  12769. * @example
  12770. * let path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
  12771. * .attr({ stroke: '#ff00ff' })
  12772. * .add();
  12773. *
  12774. * @function Highcharts.SVGRenderer#path
  12775. *
  12776. * @param {Highcharts.SVGPathArray} [path]
  12777. * An SVG path definition in array form.
  12778. *
  12779. * @return {Highcharts.SVGElement}
  12780. * The generated wrapper element.
  12781. *
  12782. */ /**
  12783. * Draw a path, wraps the SVG `path` element.
  12784. *
  12785. * @function Highcharts.SVGRenderer#path
  12786. *
  12787. * @param {Highcharts.SVGAttributes} [attribs]
  12788. * The initial attributes.
  12789. *
  12790. * @return {Highcharts.SVGElement}
  12791. * The generated wrapper element.
  12792. */
  12793. path(path) {
  12794. const attribs = (this.styledMode ? {} : {
  12795. fill: 'none'
  12796. });
  12797. if (isArray(path)) {
  12798. attribs.d = path;
  12799. }
  12800. else if (isObject(path)) { // attributes
  12801. extend(attribs, path);
  12802. }
  12803. return this.createElement('path').attr(attribs);
  12804. }
  12805. /**
  12806. * Draw a circle, wraps the SVG `circle` element.
  12807. *
  12808. * @sample highcharts/members/renderer-circle/
  12809. * Drawing a circle
  12810. *
  12811. * @function Highcharts.SVGRenderer#circle
  12812. *
  12813. * @param {number} [x]
  12814. * The center x position.
  12815. *
  12816. * @param {number} [y]
  12817. * The center y position.
  12818. *
  12819. * @param {number} [r]
  12820. * The radius.
  12821. *
  12822. * @return {Highcharts.SVGElement}
  12823. * The generated wrapper element.
  12824. */ /**
  12825. * Draw a circle, wraps the SVG `circle` element.
  12826. *
  12827. * @function Highcharts.SVGRenderer#circle
  12828. *
  12829. * @param {Highcharts.SVGAttributes} [attribs]
  12830. * The initial attributes.
  12831. *
  12832. * @return {Highcharts.SVGElement}
  12833. * The generated wrapper element.
  12834. */
  12835. circle(x, y, r) {
  12836. const attribs = (isObject(x) ?
  12837. x :
  12838. typeof x === 'undefined' ? {} : { x: x, y: y, r: r }), wrapper = this.createElement('circle');
  12839. // Setting x or y translates to cx and cy
  12840. wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
  12841. element.setAttribute('c' + key, value);
  12842. };
  12843. return wrapper.attr(attribs);
  12844. }
  12845. /**
  12846. * Draw and return an arc.
  12847. *
  12848. * @sample highcharts/members/renderer-arc/
  12849. * Drawing an arc
  12850. *
  12851. * @function Highcharts.SVGRenderer#arc
  12852. *
  12853. * @param {number} [x=0]
  12854. * Center X position.
  12855. *
  12856. * @param {number} [y=0]
  12857. * Center Y position.
  12858. *
  12859. * @param {number} [r=0]
  12860. * The outer radius' of the arc.
  12861. *
  12862. * @param {number} [innerR=0]
  12863. * Inner radius like used in donut charts.
  12864. *
  12865. * @param {number} [start=0]
  12866. * The starting angle of the arc in radians, where 0 is to the right and
  12867. * `-Math.PI/2` is up.
  12868. *
  12869. * @param {number} [end=0]
  12870. * The ending angle of the arc in radians, where 0 is to the right and
  12871. * `-Math.PI/2` is up.
  12872. *
  12873. * @return {Highcharts.SVGElement}
  12874. * The generated wrapper element.
  12875. */ /**
  12876. * Draw and return an arc. Overloaded function that takes arguments object.
  12877. *
  12878. * @function Highcharts.SVGRenderer#arc
  12879. *
  12880. * @param {Highcharts.SVGAttributes} attribs
  12881. * Initial SVG attributes.
  12882. *
  12883. * @return {Highcharts.SVGElement}
  12884. * The generated wrapper element.
  12885. */
  12886. arc(x, y, r, innerR, start, end) {
  12887. let options;
  12888. if (isObject(x)) {
  12889. options = x;
  12890. y = options.y;
  12891. r = options.r;
  12892. innerR = options.innerR;
  12893. start = options.start;
  12894. end = options.end;
  12895. x = options.x;
  12896. }
  12897. else {
  12898. options = { innerR, start, end };
  12899. }
  12900. // Arcs are defined as symbols for the ability to set
  12901. // attributes in attr and animate
  12902. const arc = this.symbol('arc', x, y, r, r, options);
  12903. arc.r = r; // #959
  12904. return arc;
  12905. }
  12906. /**
  12907. * Draw and return a rectangle.
  12908. *
  12909. * @function Highcharts.SVGRenderer#rect
  12910. *
  12911. * @param {number} [x]
  12912. * Left position.
  12913. *
  12914. * @param {number} [y]
  12915. * Top position.
  12916. *
  12917. * @param {number} [width]
  12918. * Width of the rectangle.
  12919. *
  12920. * @param {number} [height]
  12921. * Height of the rectangle.
  12922. *
  12923. * @param {number} [r]
  12924. * Border corner radius.
  12925. *
  12926. * @param {number} [strokeWidth]
  12927. * A stroke width can be supplied to allow crisp drawing.
  12928. *
  12929. * @return {Highcharts.SVGElement}
  12930. * The generated wrapper element.
  12931. */ /**
  12932. * Draw and return a rectangle.
  12933. *
  12934. * @sample highcharts/members/renderer-rect-on-chart/
  12935. * Draw a rectangle in a chart
  12936. * @sample highcharts/members/renderer-rect/
  12937. * Draw a rectangle independent from a chart
  12938. *
  12939. * @function Highcharts.SVGRenderer#rect
  12940. *
  12941. * @param {Highcharts.SVGAttributes} [attributes]
  12942. * General SVG attributes for the rectangle.
  12943. *
  12944. * @return {Highcharts.SVGElement}
  12945. * The generated wrapper element.
  12946. */
  12947. rect(x, y, width, height, r, strokeWidth) {
  12948. const attribs = (isObject(x) ?
  12949. x :
  12950. typeof x === 'undefined' ?
  12951. {} :
  12952. {
  12953. x,
  12954. y,
  12955. r,
  12956. width: Math.max(width || 0, 0),
  12957. height: Math.max(height || 0, 0)
  12958. }), wrapper = this.createElement('rect');
  12959. if (!this.styledMode) {
  12960. if (typeof strokeWidth !== 'undefined') {
  12961. attribs['stroke-width'] = strokeWidth;
  12962. extend(attribs, wrapper.crisp(attribs));
  12963. }
  12964. attribs.fill = 'none';
  12965. }
  12966. wrapper.rSetter = function (value, _key, element) {
  12967. wrapper.r = value;
  12968. attr(element, {
  12969. rx: value,
  12970. ry: value
  12971. });
  12972. };
  12973. wrapper.rGetter = function () {
  12974. return wrapper.r || 0;
  12975. };
  12976. return wrapper.attr(attribs);
  12977. }
  12978. /**
  12979. * Draw and return a rectangle with advanced corner rounding options.
  12980. *
  12981. * @function Highcharts.SVGRenderer#roundedRect
  12982. *
  12983. * @param {Highcharts.SVGAttributes} attribs
  12984. * Attributes
  12985. * @return {Highcharts.SVGElement}
  12986. * The generated wrapper element.
  12987. */
  12988. roundedRect(attribs) {
  12989. return this.symbol('roundedRect').attr(attribs);
  12990. }
  12991. /**
  12992. * Resize the {@link SVGRenderer#box} and re-align all aligned child
  12993. * elements.
  12994. *
  12995. * @sample highcharts/members/renderer-g/
  12996. * Show and hide grouped objects
  12997. *
  12998. * @function Highcharts.SVGRenderer#setSize
  12999. *
  13000. * @param {number} width
  13001. * The new pixel width.
  13002. *
  13003. * @param {number} height
  13004. * The new pixel height.
  13005. *
  13006. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animate=true]
  13007. * Whether and how to animate.
  13008. */
  13009. setSize(width, height, animate) {
  13010. const renderer = this;
  13011. renderer.width = width;
  13012. renderer.height = height;
  13013. renderer.boxWrapper.animate({
  13014. width: width,
  13015. height: height
  13016. }, {
  13017. step: function () {
  13018. this.attr({
  13019. viewBox: '0 0 ' + this.attr('width') + ' ' +
  13020. this.attr('height')
  13021. });
  13022. },
  13023. duration: pick(animate, true) ? void 0 : 0
  13024. });
  13025. renderer.alignElements();
  13026. }
  13027. /**
  13028. * Create and return an svg group element. Child
  13029. * {@link Highcharts.SVGElement} objects are added to the group by using the
  13030. * group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
  13031. *
  13032. * @function Highcharts.SVGRenderer#g
  13033. *
  13034. * @param {string} [name]
  13035. * The group will be given a class name of `highcharts-{name}`. This
  13036. * can be used for styling and scripting.
  13037. *
  13038. * @return {Highcharts.SVGElement}
  13039. * The generated wrapper element.
  13040. */
  13041. g(name) {
  13042. const elem = this.createElement('g');
  13043. return name ?
  13044. elem.attr({ 'class': 'highcharts-' + name }) :
  13045. elem;
  13046. }
  13047. /**
  13048. * Display an image.
  13049. *
  13050. * @sample highcharts/members/renderer-image-on-chart/
  13051. * Add an image in a chart
  13052. * @sample highcharts/members/renderer-image/
  13053. * Add an image independent of a chart
  13054. *
  13055. * @function Highcharts.SVGRenderer#image
  13056. *
  13057. * @param {string} href
  13058. * The image source.
  13059. *
  13060. * @param {number} [x]
  13061. * The X position.
  13062. *
  13063. * @param {number} [y]
  13064. * The Y position.
  13065. *
  13066. * @param {number} [width]
  13067. * The image width. If omitted, it defaults to the image file width.
  13068. *
  13069. * @param {number} [height]
  13070. * The image height. If omitted it defaults to the image file
  13071. * height.
  13072. *
  13073. * @param {Function} [onload]
  13074. * Event handler for image load.
  13075. *
  13076. * @return {Highcharts.SVGElement}
  13077. * The generated wrapper element.
  13078. */
  13079. image(href, x, y, width, height, onload) {
  13080. const attribs = { preserveAspectRatio: 'none' };
  13081. // Optional properties (#11756)
  13082. if (isNumber(x)) {
  13083. attribs.x = x;
  13084. }
  13085. if (isNumber(y)) {
  13086. attribs.y = y;
  13087. }
  13088. if (isNumber(width)) {
  13089. attribs.width = width;
  13090. }
  13091. if (isNumber(height)) {
  13092. attribs.height = height;
  13093. }
  13094. const elemWrapper = this.createElement('image').attr(attribs), onDummyLoad = function (e) {
  13095. elemWrapper.attr({ href });
  13096. onload.call(elemWrapper, e);
  13097. };
  13098. // Add load event if supplied
  13099. if (onload) {
  13100. // We have to use a dummy HTML image since IE support for SVG image
  13101. // load events is very buggy. First set a transparent src, wait for
  13102. // dummy to load, and then add the real src to the SVG image.
  13103. elemWrapper.attr({
  13104. /* eslint-disable-next-line max-len */
  13105. href: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
  13106. });
  13107. const dummy = new win.Image();
  13108. addEvent(dummy, 'load', onDummyLoad);
  13109. dummy.src = href;
  13110. if (dummy.complete) {
  13111. onDummyLoad({});
  13112. }
  13113. }
  13114. else {
  13115. elemWrapper.attr({ href });
  13116. }
  13117. return elemWrapper;
  13118. }
  13119. /**
  13120. * Draw a symbol out of pre-defined shape paths from
  13121. * {@link SVGRenderer#symbols}.
  13122. * It is used in Highcharts for point makers, which cake a `symbol` option,
  13123. * and label and button backgrounds like in the tooltip and stock flags.
  13124. *
  13125. * @function Highcharts.SVGRenderer#symbol
  13126. *
  13127. * @param {string} symbol
  13128. * The symbol name.
  13129. *
  13130. * @param {number} [x]
  13131. * The X coordinate for the top left position.
  13132. *
  13133. * @param {number} [y]
  13134. * The Y coordinate for the top left position.
  13135. *
  13136. * @param {number} [width]
  13137. * The pixel width.
  13138. *
  13139. * @param {number} [height]
  13140. * The pixel height.
  13141. *
  13142. * @param {Highcharts.SymbolOptionsObject} [options]
  13143. * Additional options, depending on the actual symbol drawn.
  13144. *
  13145. * @return {Highcharts.SVGElement}
  13146. * SVG symbol.
  13147. */
  13148. symbol(symbol, x, y, width, height, options) {
  13149. const ren = this, imageRegex = /^url\((.*?)\)$/, isImage = imageRegex.test(symbol), sym = (!isImage && (this.symbols[symbol] ? symbol : 'circle')),
  13150. // get the symbol definition function
  13151. symbolFn = (sym && this.symbols[sym]);
  13152. let obj, path, imageSrc, centerImage;
  13153. if (symbolFn) {
  13154. // Check if there's a path defined for this symbol
  13155. if (typeof x === 'number') {
  13156. path = symbolFn.call(this.symbols, Math.round(x || 0), Math.round(y || 0), width || 0, height || 0, options);
  13157. }
  13158. obj = this.path(path);
  13159. if (!ren.styledMode) {
  13160. obj.attr('fill', 'none');
  13161. }
  13162. // expando properties for use in animate and attr
  13163. extend(obj, {
  13164. symbolName: (sym || void 0),
  13165. x: x,
  13166. y: y,
  13167. width: width,
  13168. height: height
  13169. });
  13170. if (options) {
  13171. extend(obj, options);
  13172. }
  13173. // Image symbols
  13174. }
  13175. else if (isImage) {
  13176. imageSrc = symbol.match(imageRegex)[1];
  13177. // Create the image synchronously, add attribs async
  13178. const img = obj = this.image(imageSrc);
  13179. // The image width is not always the same as the symbol width. The
  13180. // image may be centered within the symbol, as is the case when
  13181. // image shapes are used as label backgrounds, for example in flags.
  13182. img.imgwidth = pick(options && options.width, symbolSizes[imageSrc] && symbolSizes[imageSrc].width);
  13183. img.imgheight = pick(options && options.height, symbolSizes[imageSrc] && symbolSizes[imageSrc].height);
  13184. /**
  13185. * Set the size and position
  13186. */
  13187. centerImage = (obj) => obj.attr({
  13188. width: obj.width,
  13189. height: obj.height
  13190. });
  13191. /**
  13192. * Width and height setters that take both the image's physical size
  13193. * and the label size into consideration, and translates the image
  13194. * to center within the label.
  13195. */
  13196. ['width', 'height'].forEach(function (key) {
  13197. img[key + 'Setter'] = function (value, key) {
  13198. this[key] = value;
  13199. const { alignByTranslate, element, width, height, imgwidth, imgheight } = this;
  13200. let imgSize = this['img' + key];
  13201. if (defined(imgSize)) {
  13202. let scale = 1;
  13203. // Scale and center the image within its container.
  13204. // The name `backgroundSize` is taken from the CSS spec,
  13205. // but the value `within` is made up. Other possible
  13206. // values in the spec, `cover` and `contain`, can be
  13207. // implemented if needed.
  13208. if (options &&
  13209. options.backgroundSize === 'within' &&
  13210. width &&
  13211. height) {
  13212. scale = Math.min(width / imgwidth, height / imgheight);
  13213. imgSize = Math.round(imgSize * scale);
  13214. // Update both width and height to keep the ratio
  13215. // correct (#17315)
  13216. attr(element, {
  13217. width: Math.round(imgwidth * scale),
  13218. height: Math.round(imgheight * scale)
  13219. });
  13220. }
  13221. else if (element) {
  13222. element.setAttribute(key, imgSize);
  13223. }
  13224. if (!alignByTranslate) {
  13225. this.translate(((width || 0) - (imgwidth * scale)) / 2, ((height || 0) - (imgheight * scale)) / 2);
  13226. }
  13227. }
  13228. };
  13229. });
  13230. if (defined(x)) {
  13231. img.attr({
  13232. x: x,
  13233. y: y
  13234. });
  13235. }
  13236. img.isImg = true;
  13237. if (defined(img.imgwidth) && defined(img.imgheight)) {
  13238. centerImage(img);
  13239. }
  13240. else {
  13241. // Initialize image to be 0 size so export will still function
  13242. // if there's no cached sizes.
  13243. img.attr({ width: 0, height: 0 });
  13244. // Create a dummy JavaScript image to get the width and height.
  13245. createElement('img', {
  13246. onload: function () {
  13247. const chart = charts[ren.chartIndex];
  13248. // Special case for SVGs on IE11, the width is not
  13249. // accessible until the image is part of the DOM
  13250. // (#2854).
  13251. if (this.width === 0) {
  13252. css(this, {
  13253. position: 'absolute',
  13254. top: '-999em'
  13255. });
  13256. doc.body.appendChild(this);
  13257. }
  13258. // Center the image
  13259. symbolSizes[imageSrc] = {
  13260. width: this.width,
  13261. height: this.height
  13262. };
  13263. img.imgwidth = this.width;
  13264. img.imgheight = this.height;
  13265. if (img.element) {
  13266. centerImage(img);
  13267. }
  13268. // Clean up after #2854 workaround.
  13269. if (this.parentNode) {
  13270. this.parentNode.removeChild(this);
  13271. }
  13272. // Fire the load event when all external images are
  13273. // loaded
  13274. ren.imgCount--;
  13275. if (!ren.imgCount && chart && !chart.hasLoaded) {
  13276. chart.onload();
  13277. }
  13278. },
  13279. src: imageSrc
  13280. });
  13281. this.imgCount++;
  13282. }
  13283. }
  13284. return obj;
  13285. }
  13286. /**
  13287. * Define a clipping rectangle. The clipping rectangle is later applied
  13288. * to {@link SVGElement} objects through the {@link SVGElement#clip}
  13289. * function.
  13290. *
  13291. * @example
  13292. * let circle = renderer.circle(100, 100, 100)
  13293. * .attr({ fill: 'red' })
  13294. * .add();
  13295. * let clipRect = renderer.clipRect(100, 100, 100, 100);
  13296. *
  13297. * // Leave only the lower right quarter visible
  13298. * circle.clip(clipRect);
  13299. *
  13300. * @function Highcharts.SVGRenderer#clipRect
  13301. *
  13302. * @param {number} [x]
  13303. *
  13304. * @param {number} [y]
  13305. *
  13306. * @param {number} [width]
  13307. *
  13308. * @param {number} [height]
  13309. *
  13310. * @return {Highcharts.ClipRectElement}
  13311. * A clipping rectangle.
  13312. */
  13313. clipRect(x, y, width, height) {
  13314. const
  13315. // Add a hyphen at the end to avoid confusion in testing indexes
  13316. // -1 and -10, -11 etc (#6550)
  13317. id = uniqueKey() + '-', clipPath = this.createElement('clipPath').attr({
  13318. id: id
  13319. }).add(this.defs), wrapper = this.rect(x, y, width, height, 0).add(clipPath);
  13320. wrapper.id = id;
  13321. wrapper.clipPath = clipPath;
  13322. wrapper.count = 0;
  13323. return wrapper;
  13324. }
  13325. /**
  13326. * Draw text. The text can contain a subset of HTML, like spans and anchors
  13327. * and some basic text styling of these. For more advanced features like
  13328. * border and background, use {@link Highcharts.SVGRenderer#label} instead.
  13329. * To update the text after render, run `text.attr({ text: 'New text' })`.
  13330. *
  13331. * @sample highcharts/members/renderer-text-on-chart/
  13332. * Annotate the chart freely
  13333. * @sample highcharts/members/renderer-on-chart/
  13334. * Annotate with a border and in response to the data
  13335. * @sample highcharts/members/renderer-text/
  13336. * Formatted text
  13337. *
  13338. * @function Highcharts.SVGRenderer#text
  13339. *
  13340. * @param {string} [str]
  13341. * The text of (subset) HTML to draw.
  13342. *
  13343. * @param {number} [x]
  13344. * The x position of the text's lower left corner.
  13345. *
  13346. * @param {number} [y]
  13347. * The y position of the text's lower left corner.
  13348. *
  13349. * @param {boolean} [useHTML=false]
  13350. * Use HTML to render the text.
  13351. *
  13352. * @return {Highcharts.SVGElement}
  13353. * The text object.
  13354. */
  13355. text(str, x, y, useHTML) {
  13356. const renderer = this, attribs = {};
  13357. if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
  13358. return renderer.html(str, x, y);
  13359. }
  13360. attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
  13361. if (y) {
  13362. attribs.y = Math.round(y);
  13363. }
  13364. if (defined(str)) {
  13365. attribs.text = str;
  13366. }
  13367. const wrapper = renderer.createElement('text').attr(attribs);
  13368. if (!useHTML || (renderer.forExport && !renderer.allowHTML)) {
  13369. wrapper.xSetter = function (value, key, element) {
  13370. const tspans = element.getElementsByTagName('tspan'), parentVal = element.getAttribute(key);
  13371. for (let i = 0, tspan; i < tspans.length; i++) {
  13372. tspan = tspans[i];
  13373. // If the x values are equal, the tspan represents a line
  13374. // break
  13375. if (tspan.getAttribute(key) === parentVal) {
  13376. tspan.setAttribute(key, value);
  13377. }
  13378. }
  13379. element.setAttribute(key, value);
  13380. };
  13381. }
  13382. return wrapper;
  13383. }
  13384. /**
  13385. * Utility to return the baseline offset and total line height from the font
  13386. * size.
  13387. *
  13388. * @function Highcharts.SVGRenderer#fontMetrics
  13389. *
  13390. * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement|number} [element]
  13391. * The element to inspect for a current font size. If a number is
  13392. * given, it's used as a fall back for direct font size in pixels.
  13393. *
  13394. * @return {Highcharts.FontMetricsObject}
  13395. * The font metrics.
  13396. */
  13397. fontMetrics(element) {
  13398. const f = pInt(SVGElement.prototype.getStyle.call(element, 'font-size') || 0);
  13399. // Empirical values found by comparing font size and bounding box
  13400. // height. Applies to the default font family.
  13401. // https://jsfiddle.net/highcharts/7xvn7/
  13402. const h = f < 24 ? f + 3 : Math.round(f * 1.2), b = Math.round(h * 0.8);
  13403. return {
  13404. // Line height
  13405. h,
  13406. // Baseline
  13407. b,
  13408. // Font size
  13409. f
  13410. };
  13411. }
  13412. /**
  13413. * Correct X and Y positioning of a label for rotation (#1764).
  13414. *
  13415. * @private
  13416. * @function Highcharts.SVGRenderer#rotCorr
  13417. */
  13418. rotCorr(baseline, rotation, alterY) {
  13419. let y = baseline;
  13420. if (rotation && alterY) {
  13421. y = Math.max(y * Math.cos(rotation * deg2rad), 4);
  13422. }
  13423. return {
  13424. x: (-baseline / 3) * Math.sin(rotation * deg2rad),
  13425. y: y
  13426. };
  13427. }
  13428. /**
  13429. * Compatibility function to convert the legacy one-dimensional path array
  13430. * into an array of segments.
  13431. *
  13432. * It is used in maps to parse the `path` option, and in SVGRenderer.dSetter
  13433. * to support legacy paths from demos.
  13434. *
  13435. * @private
  13436. * @function Highcharts.SVGRenderer#pathToSegments
  13437. */
  13438. pathToSegments(path) {
  13439. const ret = [];
  13440. const segment = [];
  13441. const commandLength = {
  13442. A: 8,
  13443. C: 7,
  13444. H: 2,
  13445. L: 3,
  13446. M: 3,
  13447. Q: 5,
  13448. S: 5,
  13449. T: 3,
  13450. V: 2
  13451. };
  13452. // Short, non-typesafe parsing of the one-dimensional array. It splits
  13453. // the path on any string. This is not type checked against the tuple
  13454. // types, but is shorter, and doesn't require specific checks for any
  13455. // command type in SVG.
  13456. for (let i = 0; i < path.length; i++) {
  13457. // Command skipped, repeat previous or insert L/l for M/m
  13458. if (isString(segment[0]) &&
  13459. isNumber(path[i]) &&
  13460. segment.length === commandLength[(segment[0].toUpperCase())]) {
  13461. path.splice(i, 0, segment[0].replace('M', 'L').replace('m', 'l'));
  13462. }
  13463. // Split on string
  13464. if (typeof path[i] === 'string') {
  13465. if (segment.length) {
  13466. ret.push(segment.slice(0));
  13467. }
  13468. segment.length = 0;
  13469. }
  13470. segment.push(path[i]);
  13471. }
  13472. ret.push(segment.slice(0));
  13473. return ret;
  13474. /*
  13475. // Fully type-safe version where each tuple type is checked. The
  13476. // downside is filesize and a lack of flexibility for unsupported
  13477. // commands
  13478. const ret: SVGPath = [],
  13479. commands = {
  13480. A: 7,
  13481. C: 6,
  13482. H: 1,
  13483. L: 2,
  13484. M: 2,
  13485. Q: 4,
  13486. S: 4,
  13487. T: 2,
  13488. V: 1,
  13489. Z: 0
  13490. };
  13491. let i = 0,
  13492. lastI = 0,
  13493. lastCommand;
  13494. while (i < path.length) {
  13495. const item = path[i];
  13496. let command;
  13497. if (typeof item === 'string') {
  13498. command = item;
  13499. i += 1;
  13500. } else {
  13501. command = lastCommand || 'M';
  13502. }
  13503. // Upper case
  13504. const commandUC = command.toUpperCase();
  13505. if (commandUC in commands) {
  13506. // No numeric parameters
  13507. if (command === 'Z' || command === 'z') {
  13508. ret.push([command]);
  13509. // One numeric parameter
  13510. } else {
  13511. const val0 = path[i];
  13512. if (typeof val0 === 'number') {
  13513. // Horizontal line to
  13514. if (command === 'H' || command === 'h') {
  13515. ret.push([command, val0]);
  13516. i += 1;
  13517. // Vertical line to
  13518. } else if (command === 'V' || command === 'v') {
  13519. ret.push([command, val0]);
  13520. i += 1;
  13521. // Two numeric parameters
  13522. } else {
  13523. const val1 = path[i + 1];
  13524. if (typeof val1 === 'number') {
  13525. // lineTo
  13526. if (command === 'L' || command === 'l') {
  13527. ret.push([command, val0, val1]);
  13528. i += 2;
  13529. // moveTo
  13530. } else if (command === 'M' || command === 'm') {
  13531. ret.push([command, val0, val1]);
  13532. i += 2;
  13533. // Smooth quadratic bezier
  13534. } else if (command === 'T' || command === 't') {
  13535. ret.push([command, val0, val1]);
  13536. i += 2;
  13537. // Four numeric parameters
  13538. } else {
  13539. const val2 = path[i + 2],
  13540. val3 = path[i + 3];
  13541. if (
  13542. typeof val2 === 'number' &&
  13543. typeof val3 === 'number'
  13544. ) {
  13545. // Quadratic bezier to
  13546. if (
  13547. command === 'Q' ||
  13548. command === 'q'
  13549. ) {
  13550. ret.push([
  13551. command,
  13552. val0,
  13553. val1,
  13554. val2,
  13555. val3
  13556. ]);
  13557. i += 4;
  13558. // Smooth cubic bezier to
  13559. } else if (
  13560. command === 'S' ||
  13561. command === 's'
  13562. ) {
  13563. ret.push([
  13564. command,
  13565. val0,
  13566. val1,
  13567. val2,
  13568. val3
  13569. ]);
  13570. i += 4;
  13571. // Six numeric parameters
  13572. } else {
  13573. const val4 = path[i + 4],
  13574. val5 = path[i + 5];
  13575. if (
  13576. typeof val4 === 'number' &&
  13577. typeof val5 === 'number'
  13578. ) {
  13579. // Curve to
  13580. if (
  13581. command === 'C' ||
  13582. command === 'c'
  13583. ) {
  13584. ret.push([
  13585. command,
  13586. val0,
  13587. val1,
  13588. val2,
  13589. val3,
  13590. val4,
  13591. val5
  13592. ]);
  13593. i += 6;
  13594. // Seven numeric parameters
  13595. } else {
  13596. const val6 = path[i + 6];
  13597. // Arc to
  13598. if (
  13599. typeof val6 ===
  13600. 'number' &&
  13601. (
  13602. command === 'A' ||
  13603. command === 'a'
  13604. )
  13605. ) {
  13606. ret.push([
  13607. command,
  13608. val0,
  13609. val1,
  13610. val2,
  13611. val3,
  13612. val4,
  13613. val5,
  13614. val6
  13615. ]);
  13616. i += 7;
  13617. }
  13618. }
  13619. }
  13620. }
  13621. }
  13622. }
  13623. }
  13624. }
  13625. }
  13626. }
  13627. }
  13628. // An unmarked command following a moveTo is a lineTo
  13629. lastCommand = command === 'M' ? 'L' : command;
  13630. if (i === lastI) {
  13631. break;
  13632. }
  13633. lastI = i;
  13634. }
  13635. return ret;
  13636. */
  13637. }
  13638. /**
  13639. * Draw a label, which is an extended text element with support for border
  13640. * and background. Highcharts creates a `g` element with a text and a `path`
  13641. * or `rect` inside, to make it behave somewhat like a HTML div. Border and
  13642. * background are set through `stroke`, `stroke-width` and `fill` attributes
  13643. * using the {@link Highcharts.SVGElement#attr|attr} method. To update the
  13644. * text after render, run `label.attr({ text: 'New text' })`.
  13645. *
  13646. * @sample highcharts/members/renderer-label-on-chart/
  13647. * A label on the chart
  13648. *
  13649. * @function Highcharts.SVGRenderer#label
  13650. *
  13651. * @param {string} str
  13652. * The initial text string or (subset) HTML to render.
  13653. *
  13654. * @param {number} x
  13655. * The x position of the label's left side.
  13656. *
  13657. * @param {number} [y]
  13658. * The y position of the label's top side or baseline, depending on
  13659. * the `baseline` parameter.
  13660. *
  13661. * @param {string} [shape='rect']
  13662. * The shape of the label's border/background, if any. Defaults to
  13663. * `rect`. Other possible values are `callout` or other shapes
  13664. * defined in {@link Highcharts.SVGRenderer#symbols}.
  13665. *
  13666. * @param {number} [anchorX]
  13667. * In case the `shape` has a pointer, like a flag, this is the
  13668. * coordinates it should be pinned to.
  13669. *
  13670. * @param {number} [anchorY]
  13671. * In case the `shape` has a pointer, like a flag, this is the
  13672. * coordinates it should be pinned to.
  13673. *
  13674. * @param {boolean} [useHTML=false]
  13675. * Whether to use HTML to render the label.
  13676. *
  13677. * @param {boolean} [baseline=false]
  13678. * Whether to position the label relative to the text baseline,
  13679. * like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
  13680. * upper border of the rectangle.
  13681. *
  13682. * @param {string} [className]
  13683. * Class name for the group.
  13684. *
  13685. * @return {Highcharts.SVGElement}
  13686. * The generated label.
  13687. */
  13688. label(str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  13689. return new SVGLabel(this, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className);
  13690. }
  13691. /**
  13692. * Re-align all aligned elements.
  13693. *
  13694. * @private
  13695. * @function Highcharts.SVGRenderer#alignElements
  13696. */
  13697. alignElements() {
  13698. this.alignedObjects.forEach((el) => el.align());
  13699. }
  13700. }
  13701. extend(SVGRenderer.prototype, {
  13702. /**
  13703. * A pointer to the renderer's associated Element class.
  13704. *
  13705. * @name Highcharts.SVGRenderer#Element
  13706. * @type {Highcharts.SVGElement}
  13707. */
  13708. Element: SVGElement,
  13709. SVG_NS,
  13710. /**
  13711. * A collection of characters mapped to HTML entities. When `useHTML` on an
  13712. * element is true, these entities will be rendered correctly by HTML. In
  13713. * the SVG pseudo-HTML, they need to be unescaped back to simple characters,
  13714. * so for example `&lt;` will render as `<`.
  13715. *
  13716. * @example
  13717. * // Add support for unescaping quotes
  13718. * Highcharts.SVGRenderer.prototype.escapes['"'] = '&quot;';
  13719. *
  13720. * @name Highcharts.SVGRenderer#escapes
  13721. * @type {Highcharts.Dictionary<string>}
  13722. */
  13723. escapes: {
  13724. '&': '&amp;',
  13725. '<': '&lt;',
  13726. '>': '&gt;',
  13727. "'": '&#39;',
  13728. '"': '&quot;'
  13729. },
  13730. /**
  13731. * An extendable collection of functions for defining symbol paths.
  13732. *
  13733. * @name Highcharts.SVGRenderer#symbols
  13734. * @type {Highcharts.SymbolDictionary}
  13735. */
  13736. symbols: Symbols,
  13737. /**
  13738. * Dummy function for plugins, called every time the renderer is updated.
  13739. * Prior to Highcharts 5, this was used for the canvg renderer.
  13740. *
  13741. * @deprecated
  13742. * @function Highcharts.SVGRenderer#draw
  13743. */
  13744. draw: noop
  13745. });
  13746. /* *
  13747. *
  13748. * Registry
  13749. *
  13750. * */
  13751. RendererRegistry.registerRendererType('svg', SVGRenderer, true);
  13752. /* *
  13753. *
  13754. * Export Default
  13755. *
  13756. * */
  13757. /* *
  13758. *
  13759. * API Declarations
  13760. *
  13761. * */
  13762. /**
  13763. * A clipping rectangle that can be applied to one or more {@link SVGElement}
  13764. * instances. It is instanciated with the {@link SVGRenderer#clipRect} function
  13765. * and applied with the {@link SVGElement#clip} function.
  13766. *
  13767. * @example
  13768. * let circle = renderer.circle(100, 100, 100)
  13769. * .attr({ fill: 'red' })
  13770. * .add();
  13771. * let clipRect = renderer.clipRect(100, 100, 100, 100);
  13772. *
  13773. * // Leave only the lower right quarter visible
  13774. * circle.clip(clipRect);
  13775. *
  13776. * @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
  13777. */
  13778. /**
  13779. * The font metrics.
  13780. *
  13781. * @interface Highcharts.FontMetricsObject
  13782. */ /**
  13783. * The baseline relative to the top of the box.
  13784. *
  13785. * @name Highcharts.FontMetricsObject#b
  13786. * @type {number}
  13787. */ /**
  13788. * The font size.
  13789. *
  13790. * @name Highcharts.FontMetricsObject#f
  13791. * @type {number}
  13792. */ /**
  13793. * The line height.
  13794. *
  13795. * @name Highcharts.FontMetricsObject#h
  13796. * @type {number}
  13797. */
  13798. /**
  13799. * An object containing `x` and `y` properties for the position of an element.
  13800. *
  13801. * @interface Highcharts.PositionObject
  13802. */ /**
  13803. * X position of the element.
  13804. * @name Highcharts.PositionObject#x
  13805. * @type {number}
  13806. */ /**
  13807. * Y position of the element.
  13808. * @name Highcharts.PositionObject#y
  13809. * @type {number}
  13810. */
  13811. /**
  13812. * A rectangle.
  13813. *
  13814. * @interface Highcharts.RectangleObject
  13815. */ /**
  13816. * Height of the rectangle.
  13817. * @name Highcharts.RectangleObject#height
  13818. * @type {number}
  13819. */ /**
  13820. * Width of the rectangle.
  13821. * @name Highcharts.RectangleObject#width
  13822. * @type {number}
  13823. */ /**
  13824. * Horizontal position of the rectangle.
  13825. * @name Highcharts.RectangleObject#x
  13826. * @type {number}
  13827. */ /**
  13828. * Vertical position of the rectangle.
  13829. * @name Highcharts.RectangleObject#y
  13830. * @type {number}
  13831. */
  13832. /**
  13833. * The shadow options.
  13834. *
  13835. * @interface Highcharts.ShadowOptionsObject
  13836. */ /**
  13837. * The shadow color.
  13838. * @name Highcharts.ShadowOptionsObject#color
  13839. * @type {Highcharts.ColorString|undefined}
  13840. * @default #000000
  13841. */ /**
  13842. * The horizontal offset from the element.
  13843. *
  13844. * @name Highcharts.ShadowOptionsObject#offsetX
  13845. * @type {number|undefined}
  13846. * @default 1
  13847. */ /**
  13848. * The vertical offset from the element.
  13849. * @name Highcharts.ShadowOptionsObject#offsetY
  13850. * @type {number|undefined}
  13851. * @default 1
  13852. */ /**
  13853. * The shadow opacity.
  13854. *
  13855. * @name Highcharts.ShadowOptionsObject#opacity
  13856. * @type {number|undefined}
  13857. * @default 0.15
  13858. */ /**
  13859. * The shadow width or distance from the element.
  13860. * @name Highcharts.ShadowOptionsObject#width
  13861. * @type {number|undefined}
  13862. * @default 3
  13863. */
  13864. /**
  13865. * @interface Highcharts.SizeObject
  13866. */ /**
  13867. * @name Highcharts.SizeObject#height
  13868. * @type {number}
  13869. */ /**
  13870. * @name Highcharts.SizeObject#width
  13871. * @type {number}
  13872. */
  13873. /**
  13874. * Array of path commands, that will go into the `d` attribute of an SVG
  13875. * element.
  13876. *
  13877. * @typedef {Array<(Array<Highcharts.SVGPathCommand>|Array<Highcharts.SVGPathCommand,number>|Array<Highcharts.SVGPathCommand,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number,number,number>|Array<Highcharts.SVGPathCommand,number,number,number,number,number,number,number>)>} Highcharts.SVGPathArray
  13878. */
  13879. /**
  13880. * Possible path commands in an SVG path array. Valid values are `A`, `C`, `H`,
  13881. * `L`, `M`, `Q`, `S`, `T`, `V`, `Z`.
  13882. *
  13883. * @typedef {string} Highcharts.SVGPathCommand
  13884. * @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
  13885. */
  13886. /**
  13887. * An extendable collection of functions for defining symbol paths. Symbols are
  13888. * used internally for point markers, button and label borders and backgrounds,
  13889. * or custom shapes. Extendable by adding to {@link SVGRenderer#symbols}.
  13890. *
  13891. * @interface Highcharts.SymbolDictionary
  13892. */ /**
  13893. * @name Highcharts.SymbolDictionary#[key:string]
  13894. * @type {Function|undefined}
  13895. */ /**
  13896. * @name Highcharts.SymbolDictionary#arc
  13897. * @type {Function|undefined}
  13898. */ /**
  13899. * @name Highcharts.SymbolDictionary#callout
  13900. * @type {Function|undefined}
  13901. */ /**
  13902. * @name Highcharts.SymbolDictionary#circle
  13903. * @type {Function|undefined}
  13904. */ /**
  13905. * @name Highcharts.SymbolDictionary#diamond
  13906. * @type {Function|undefined}
  13907. */ /**
  13908. * @name Highcharts.SymbolDictionary#square
  13909. * @type {Function|undefined}
  13910. */ /**
  13911. * @name Highcharts.SymbolDictionary#triangle
  13912. * @type {Function|undefined}
  13913. */
  13914. /**
  13915. * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`, `triangle`,
  13916. * and `triangle-down`. Symbols are used internally for point markers, button
  13917. * and label borders and backgrounds, or custom shapes. Extendable by adding to
  13918. * {@link SVGRenderer#symbols}.
  13919. *
  13920. * @typedef {"arc"|"callout"|"circle"|"diamond"|"square"|"triangle"|"triangle-down"} Highcharts.SymbolKeyValue
  13921. */
  13922. /**
  13923. * Additional options, depending on the actual symbol drawn.
  13924. *
  13925. * @interface Highcharts.SymbolOptionsObject
  13926. */ /**
  13927. * The anchor X position for the `callout` symbol. This is where the chevron
  13928. * points to.
  13929. *
  13930. * @name Highcharts.SymbolOptionsObject#anchorX
  13931. * @type {number|undefined}
  13932. */ /**
  13933. * The anchor Y position for the `callout` symbol. This is where the chevron
  13934. * points to.
  13935. *
  13936. * @name Highcharts.SymbolOptionsObject#anchorY
  13937. * @type {number|undefined}
  13938. */ /**
  13939. * The end angle of an `arc` symbol.
  13940. *
  13941. * @name Highcharts.SymbolOptionsObject#end
  13942. * @type {number|undefined}
  13943. */ /**
  13944. * Whether to draw `arc` symbol open or closed.
  13945. *
  13946. * @name Highcharts.SymbolOptionsObject#open
  13947. * @type {boolean|undefined}
  13948. */ /**
  13949. * The radius of an `arc` symbol, or the border radius for the `callout` symbol.
  13950. *
  13951. * @name Highcharts.SymbolOptionsObject#r
  13952. * @type {number|undefined}
  13953. */ /**
  13954. * The start angle of an `arc` symbol.
  13955. *
  13956. * @name Highcharts.SymbolOptionsObject#start
  13957. * @type {number|undefined}
  13958. */
  13959. (''); // keeps doclets above in transpiled file
  13960. return SVGRenderer;
  13961. });
  13962. _registerModule(_modules, 'Core/Renderer/HTML/HTMLElement.js', [_modules['Core/Globals.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (H, SVGElement, U) {
  13963. /* *
  13964. *
  13965. * (c) 2010-2021 Torstein Honsi
  13966. *
  13967. * License: www.highcharts.com/license
  13968. *
  13969. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  13970. *
  13971. * */
  13972. const { isFirefox, isMS, isWebKit, win } = H;
  13973. const { css, defined, extend, pick, pInt } = U;
  13974. /* *
  13975. *
  13976. * Constants
  13977. *
  13978. * */
  13979. const composedMembers = [];
  13980. /* *
  13981. *
  13982. * Class
  13983. *
  13984. * */
  13985. /* eslint-disable valid-jsdoc */
  13986. class HTMLElement extends SVGElement {
  13987. /* *
  13988. *
  13989. * Static Functions
  13990. *
  13991. * */
  13992. /**
  13993. * Modifies SVGElement to support HTML elements.
  13994. * @private
  13995. */
  13996. static compose(SVGElementClass) {
  13997. if (U.pushUnique(composedMembers, SVGElementClass)) {
  13998. const htmlElementProto = HTMLElement.prototype, svgElementProto = SVGElementClass.prototype;
  13999. svgElementProto.getSpanCorrection = htmlElementProto
  14000. .getSpanCorrection;
  14001. svgElementProto.htmlCss = htmlElementProto.htmlCss;
  14002. svgElementProto.htmlGetBBox = htmlElementProto.htmlGetBBox;
  14003. svgElementProto.htmlUpdateTransform = htmlElementProto
  14004. .htmlUpdateTransform;
  14005. svgElementProto.setSpanRotation = htmlElementProto.setSpanRotation;
  14006. }
  14007. return SVGElementClass;
  14008. }
  14009. /* *
  14010. *
  14011. * Functions
  14012. *
  14013. * */
  14014. /**
  14015. * Get the correction in X and Y positioning as the element is rotated.
  14016. * @private
  14017. */
  14018. getSpanCorrection(width, baseline, alignCorrection) {
  14019. this.xCorr = -width * alignCorrection;
  14020. this.yCorr = -baseline;
  14021. }
  14022. /**
  14023. * Apply CSS to HTML elements. This is used in text within SVG rendering.
  14024. * @private
  14025. */
  14026. htmlCss(styles) {
  14027. const wrapper = this, element = wrapper.element,
  14028. // When setting or unsetting the width style, we need to update
  14029. // transform (#8809)
  14030. isSettingWidth = (element.tagName === 'SPAN' &&
  14031. styles &&
  14032. 'width' in styles), textWidth = pick(isSettingWidth && styles.width, void 0);
  14033. let doTransform;
  14034. if (isSettingWidth) {
  14035. delete styles.width;
  14036. wrapper.textWidth = textWidth;
  14037. doTransform = true;
  14038. }
  14039. if (styles && styles.textOverflow === 'ellipsis') {
  14040. styles.whiteSpace = 'nowrap';
  14041. styles.overflow = 'hidden';
  14042. }
  14043. wrapper.styles = extend(wrapper.styles, styles);
  14044. css(wrapper.element, styles);
  14045. // Now that all styles are applied, to the transform
  14046. if (doTransform) {
  14047. wrapper.htmlUpdateTransform();
  14048. }
  14049. return wrapper;
  14050. }
  14051. /**
  14052. * useHTML method for calculating the bounding box based on offsets.
  14053. */
  14054. htmlGetBBox() {
  14055. const wrapper = this, element = wrapper.element;
  14056. return {
  14057. x: element.offsetLeft,
  14058. y: element.offsetTop,
  14059. width: element.offsetWidth,
  14060. height: element.offsetHeight
  14061. };
  14062. }
  14063. /**
  14064. * @private
  14065. */
  14066. htmlUpdateTransform() {
  14067. // aligning non added elements is expensive
  14068. if (!this.added) {
  14069. this.alignOnAdd = true;
  14070. return;
  14071. }
  14072. const wrapper = this, renderer = wrapper.renderer, elem = wrapper.element, translateX = wrapper.translateX || 0, translateY = wrapper.translateY || 0, x = wrapper.x || 0, y = wrapper.y || 0, align = wrapper.textAlign || 'left', alignCorrection = {
  14073. left: 0, center: 0.5, right: 1
  14074. }[align], styles = wrapper.styles, whiteSpace = styles && styles.whiteSpace;
  14075. /** @private */
  14076. function getTextPxLength() {
  14077. if (wrapper.textPxLength) {
  14078. return wrapper.textPxLength;
  14079. }
  14080. // Reset multiline/ellipsis in order to read width (#4928,
  14081. // #5417)
  14082. css(elem, {
  14083. width: '',
  14084. whiteSpace: whiteSpace || 'nowrap'
  14085. });
  14086. return elem.offsetWidth;
  14087. }
  14088. // apply translate
  14089. css(elem, {
  14090. marginLeft: translateX,
  14091. marginTop: translateY
  14092. });
  14093. if (elem.tagName === 'SPAN') {
  14094. const rotation = wrapper.rotation, textWidth = wrapper.textWidth && pInt(wrapper.textWidth), currentTextTransform = [
  14095. rotation,
  14096. align,
  14097. elem.innerHTML,
  14098. wrapper.textWidth,
  14099. wrapper.textAlign
  14100. ].join(',');
  14101. let baseline, hasBoxWidthChanged = false;
  14102. // Update textWidth. Use the memoized textPxLength if possible, to
  14103. // avoid the getTextPxLength function using elem.offsetWidth.
  14104. // Calling offsetWidth affects rendering time as it forces layout
  14105. // (#7656).
  14106. if (textWidth !== wrapper.oldTextWidth) { // #983, #1254
  14107. const textPxLength = getTextPxLength();
  14108. if (((textWidth > wrapper.oldTextWidth) ||
  14109. textPxLength > textWidth) && (
  14110. // Only set the width if the text is able to word-wrap,
  14111. // or text-overflow is ellipsis (#9537)
  14112. /[ \-]/.test(elem.textContent || elem.innerText) ||
  14113. elem.style.textOverflow === 'ellipsis')) {
  14114. css(elem, {
  14115. width: (textPxLength > textWidth) || rotation ?
  14116. textWidth + 'px' :
  14117. 'auto',
  14118. display: 'block',
  14119. whiteSpace: whiteSpace || 'normal' // #3331
  14120. });
  14121. wrapper.oldTextWidth = textWidth;
  14122. hasBoxWidthChanged = true; // #8159
  14123. }
  14124. }
  14125. wrapper.hasBoxWidthChanged = hasBoxWidthChanged; // #8159
  14126. // Do the calculations and DOM access only if properties changed
  14127. if (currentTextTransform !== wrapper.cTT) {
  14128. baseline = renderer.fontMetrics(elem).b;
  14129. // Renderer specific handling of span rotation, but only if we
  14130. // have something to update.
  14131. if (defined(rotation) &&
  14132. ((rotation !== (wrapper.oldRotation || 0)) ||
  14133. (align !== wrapper.oldAlign))) {
  14134. wrapper.setSpanRotation(rotation, alignCorrection, baseline);
  14135. }
  14136. wrapper.getSpanCorrection(
  14137. // Avoid elem.offsetWidth if we can, it affects rendering
  14138. // time heavily (#7656)
  14139. ((!defined(rotation) && wrapper.textPxLength) || // #7920
  14140. elem.offsetWidth), baseline, alignCorrection, rotation, align);
  14141. }
  14142. // apply position with correction
  14143. css(elem, {
  14144. left: (x + (wrapper.xCorr || 0)) + 'px',
  14145. top: (y + (wrapper.yCorr || 0)) + 'px'
  14146. });
  14147. // record current text transform
  14148. wrapper.cTT = currentTextTransform;
  14149. wrapper.oldRotation = rotation;
  14150. wrapper.oldAlign = align;
  14151. }
  14152. }
  14153. /**
  14154. * Set the rotation of an individual HTML span.
  14155. * @private
  14156. */
  14157. setSpanRotation(rotation, alignCorrection, baseline) {
  14158. const getTransformKey = () => (isMS &&
  14159. !/Edge/.test(win.navigator.userAgent) ?
  14160. '-ms-transform' :
  14161. isWebKit ?
  14162. '-webkit-transform' :
  14163. isFirefox ?
  14164. 'MozTransform' :
  14165. win.opera ?
  14166. '-o-transform' :
  14167. void 0);
  14168. const rotationStyle = {}, cssTransformKey = getTransformKey();
  14169. if (cssTransformKey) {
  14170. rotationStyle[cssTransformKey] = rotationStyle.transform =
  14171. 'rotate(' + rotation + 'deg)';
  14172. rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] = rotationStyle.transformOrigin =
  14173. (alignCorrection * 100) + '% ' + baseline + 'px';
  14174. css(this.element, rotationStyle);
  14175. }
  14176. }
  14177. }
  14178. /* *
  14179. *
  14180. * Default Export
  14181. *
  14182. * */
  14183. return HTMLElement;
  14184. });
  14185. _registerModule(_modules, 'Core/Renderer/HTML/HTMLRenderer.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (AST, SVGElement, SVGRenderer, U) {
  14186. /* *
  14187. *
  14188. * (c) 2010-2021 Torstein Honsi
  14189. *
  14190. * License: www.highcharts.com/license
  14191. *
  14192. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  14193. *
  14194. * */
  14195. const { attr, createElement, extend, pick } = U;
  14196. /* *
  14197. *
  14198. * Constants
  14199. *
  14200. * */
  14201. const composedMembers = [];
  14202. /* *
  14203. *
  14204. * Class
  14205. *
  14206. * */
  14207. /* eslint-disable valid-jsdoc */
  14208. // Extend SvgRenderer for useHTML option.
  14209. class HTMLRenderer extends SVGRenderer {
  14210. /* *
  14211. *
  14212. * Static Functions
  14213. *
  14214. * */
  14215. /** @private */
  14216. static compose(SVGRendererClass) {
  14217. if (U.pushUnique(composedMembers, SVGRendererClass)) {
  14218. const htmlRendererProto = HTMLRenderer.prototype, svgRendererProto = SVGRendererClass.prototype;
  14219. svgRendererProto.html = htmlRendererProto.html;
  14220. }
  14221. return SVGRendererClass;
  14222. }
  14223. /* *
  14224. *
  14225. * Functions
  14226. *
  14227. * */
  14228. /**
  14229. * Create HTML text node. This is used by the SVG renderer through the
  14230. * useHTML option.
  14231. *
  14232. * @private
  14233. * @function Highcharts.SVGRenderer#html
  14234. *
  14235. * @param {string} str
  14236. * The text of (subset) HTML to draw.
  14237. *
  14238. * @param {number} x
  14239. * The x position of the text's lower left corner.
  14240. *
  14241. * @param {number} y
  14242. * The y position of the text's lower left corner.
  14243. *
  14244. * @return {Highcharts.HTMLDOMElement}
  14245. * HTML element.
  14246. */
  14247. html(str, x, y) {
  14248. const wrapper = this.createElement('span'), element = wrapper.element, renderer = wrapper.renderer, addSetters = function (gWrapper, style) {
  14249. // These properties are set as attributes on the SVG group, and
  14250. // as identical CSS properties on the div. (#3542)
  14251. ['opacity', 'visibility'].forEach(function (prop) {
  14252. gWrapper[prop + 'Setter'] = function (value, key, elem) {
  14253. const styleObject = gWrapper.div ?
  14254. gWrapper.div.style :
  14255. style;
  14256. SVGElement.prototype[prop + 'Setter']
  14257. .call(this, value, key, elem);
  14258. if (styleObject) {
  14259. styleObject[key] = value;
  14260. }
  14261. };
  14262. });
  14263. gWrapper.addedSetters = true;
  14264. };
  14265. // Text setter
  14266. wrapper.textSetter = function (value) {
  14267. if (value !== this.textStr) {
  14268. delete this.bBox;
  14269. delete this.oldTextWidth;
  14270. AST.setElementHTML(this.element, pick(value, ''));
  14271. this.textStr = value;
  14272. wrapper.doTransform = true;
  14273. }
  14274. };
  14275. addSetters(wrapper, wrapper.element.style);
  14276. // Various setters which rely on update transform
  14277. wrapper.xSetter =
  14278. wrapper.ySetter =
  14279. wrapper.alignSetter =
  14280. wrapper.rotationSetter =
  14281. function (value, key) {
  14282. if (key === 'align') {
  14283. // Do not overwrite the SVGElement.align method.
  14284. wrapper.alignValue = wrapper.textAlign = value;
  14285. }
  14286. else {
  14287. wrapper[key] = value;
  14288. }
  14289. wrapper.doTransform = true;
  14290. };
  14291. // Runs at the end of .attr()
  14292. wrapper.afterSetters = function () {
  14293. // Update transform. Do this outside the loop to prevent redundant
  14294. // updating for batch setting of attributes.
  14295. if (this.doTransform) {
  14296. this.htmlUpdateTransform();
  14297. this.doTransform = false;
  14298. }
  14299. };
  14300. // Set the default attributes
  14301. wrapper
  14302. .attr({
  14303. text: str,
  14304. x: Math.round(x),
  14305. y: Math.round(y)
  14306. })
  14307. .css({
  14308. position: 'absolute'
  14309. });
  14310. if (!renderer.styledMode) {
  14311. wrapper.css({
  14312. fontFamily: this.style.fontFamily,
  14313. fontSize: this.style.fontSize
  14314. });
  14315. }
  14316. // Keep the whiteSpace style outside the wrapper.styles collection
  14317. element.style.whiteSpace = 'nowrap';
  14318. // Use the HTML specific .css method
  14319. wrapper.css = wrapper.htmlCss;
  14320. wrapper.add = function (svgGroupWrapper) {
  14321. const container = renderer.box.parentNode, parents = [];
  14322. let htmlGroup, parentGroup;
  14323. this.parentGroup = svgGroupWrapper;
  14324. // Create a mock group to hold the HTML elements
  14325. if (svgGroupWrapper) {
  14326. htmlGroup = svgGroupWrapper.div;
  14327. if (!htmlGroup) {
  14328. // Read the parent chain into an array and read from top
  14329. // down
  14330. parentGroup = svgGroupWrapper;
  14331. while (parentGroup) {
  14332. parents.push(parentGroup);
  14333. // Move up to the next parent group
  14334. parentGroup = parentGroup.parentGroup;
  14335. }
  14336. // Ensure dynamically updating position when any parent
  14337. // is translated
  14338. parents.reverse().forEach(function (parentGroup) {
  14339. const cls = attr(parentGroup.element, 'class');
  14340. /**
  14341. * Common translate setter for X and Y on the HTML
  14342. * group. Reverted the fix for #6957 du to
  14343. * positioning problems and offline export (#7254,
  14344. * #7280, #7529)
  14345. * @private
  14346. * @param {*} value
  14347. * @param {string} key
  14348. */
  14349. function translateSetter(value, key) {
  14350. parentGroup[key] = value;
  14351. if (key === 'translateX') {
  14352. htmlGroupStyle.left = value + 'px';
  14353. }
  14354. else {
  14355. htmlGroupStyle.top = value + 'px';
  14356. }
  14357. parentGroup.doTransform = true;
  14358. }
  14359. // Create a HTML div and append it to the parent div
  14360. // to emulate the SVG group structure
  14361. const parentGroupStyles = parentGroup.styles || {};
  14362. htmlGroup =
  14363. parentGroup.div =
  14364. parentGroup.div || createElement('div', cls ? { className: cls } : void 0, {
  14365. position: 'absolute',
  14366. left: (parentGroup.translateX || 0) + 'px',
  14367. top: (parentGroup.translateY || 0) + 'px',
  14368. display: parentGroup.display,
  14369. opacity: parentGroup.opacity,
  14370. visibility: parentGroup.visibility
  14371. // the top group is appended to container
  14372. }, htmlGroup || container);
  14373. // Shortcut
  14374. const htmlGroupStyle = htmlGroup.style;
  14375. // Set listeners to update the HTML div's position
  14376. // whenever the SVG group position is changed.
  14377. extend(parentGroup, {
  14378. // (#7287) Pass htmlGroup to use
  14379. // the related group
  14380. classSetter: (function (htmlGroup) {
  14381. return function (value) {
  14382. this.element.setAttribute('class', value);
  14383. htmlGroup.className = value;
  14384. };
  14385. }(htmlGroup)),
  14386. // Extend the parent group's css function by
  14387. // updating the shadow div counterpart with the same
  14388. // style.
  14389. css: function (styles) {
  14390. wrapper.css.call(parentGroup, styles);
  14391. [
  14392. // #6794
  14393. 'cursor',
  14394. // #5595, #18821
  14395. 'pointerEvents'
  14396. ].forEach((prop) => {
  14397. if (styles[prop]) {
  14398. htmlGroupStyle[prop] = styles[prop];
  14399. }
  14400. });
  14401. return parentGroup;
  14402. },
  14403. on: function () {
  14404. if (parents[0].div) { // #6418
  14405. wrapper.on.apply({
  14406. element: parents[0].div,
  14407. onEvents: parentGroup.onEvents
  14408. }, arguments);
  14409. }
  14410. return parentGroup;
  14411. },
  14412. translateXSetter: translateSetter,
  14413. translateYSetter: translateSetter
  14414. });
  14415. if (!parentGroup.addedSetters) {
  14416. addSetters(parentGroup);
  14417. }
  14418. // Apply pre-existing style
  14419. parentGroup.css(parentGroupStyles);
  14420. });
  14421. }
  14422. }
  14423. else {
  14424. htmlGroup = container;
  14425. }
  14426. htmlGroup.appendChild(element);
  14427. wrapper.added = true;
  14428. if (wrapper.alignOnAdd) {
  14429. wrapper.htmlUpdateTransform();
  14430. }
  14431. return wrapper;
  14432. };
  14433. return wrapper;
  14434. }
  14435. }
  14436. /* *
  14437. *
  14438. * Default Export
  14439. *
  14440. * */
  14441. return HTMLRenderer;
  14442. });
  14443. _registerModule(_modules, 'Core/Axis/AxisDefaults.js', [], function () {
  14444. /* *
  14445. *
  14446. * (c) 2010-2021 Torstein Honsi
  14447. *
  14448. * License: www.highcharts.com/license
  14449. *
  14450. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  14451. *
  14452. * */
  14453. /* *
  14454. *
  14455. * Namespace
  14456. *
  14457. * */
  14458. var AxisDefaults;
  14459. (function (AxisDefaults) {
  14460. /* *
  14461. *
  14462. * Constants
  14463. *
  14464. * */
  14465. /**
  14466. * The X axis or category axis. Normally this is the horizontal axis,
  14467. * though if the chart is inverted this is the vertical axis. In case of
  14468. * multiple axes, the xAxis node is an array of configuration objects.
  14469. *
  14470. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  14471. * access to the axis.
  14472. *
  14473. * @productdesc {highmaps}
  14474. * In Highmaps, the axis is hidden, but it is used behind the scenes to
  14475. * control features like zooming and panning. Zooming is in effect the same
  14476. * as setting the extremes of one of the exes.
  14477. *
  14478. * @type {*|Array<*>}
  14479. * @optionparent xAxis
  14480. */
  14481. AxisDefaults.defaultXAxisOptions = {
  14482. /**
  14483. * When using multiple axis, the ticks of two or more opposite axes
  14484. * will automatically be aligned by adding ticks to the axis or axes
  14485. * with the least ticks, as if `tickAmount` were specified.
  14486. *
  14487. * This can be prevented by setting `alignTicks` to false. If the grid
  14488. * lines look messy, it's a good idea to hide them for the secondary
  14489. * axis by setting `gridLineWidth` to 0.
  14490. *
  14491. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  14492. * then the `alignTicks ` will be disabled for the Axis.
  14493. *
  14494. * Disabled for logarithmic axes.
  14495. *
  14496. * @product highcharts highstock gantt
  14497. */
  14498. alignTicks: true,
  14499. /**
  14500. * Whether to allow decimals in this axis' ticks. When counting
  14501. * integers, like persons or hits on a web page, decimals should
  14502. * be avoided in the labels. By default, decimals are allowed on small
  14503. * scale axes.
  14504. *
  14505. * @see [minTickInterval](#xAxis.minTickInterval)
  14506. *
  14507. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
  14508. * True by default
  14509. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
  14510. * False
  14511. *
  14512. * @type {boolean|undefined}
  14513. * @default undefined
  14514. * @since 2.0
  14515. */
  14516. allowDecimals: void 0,
  14517. /**
  14518. * When using an alternate grid color, a band is painted across the
  14519. * plot area between every other grid line.
  14520. *
  14521. * @sample {highcharts} highcharts/yaxis/alternategridcolor/
  14522. * Alternate grid color on the Y axis
  14523. * @sample {highstock} stock/xaxis/alternategridcolor/
  14524. * Alternate grid color on the Y axis
  14525. *
  14526. * @type {Highcharts.ColorType}
  14527. * @apioption xAxis.alternateGridColor
  14528. */
  14529. /**
  14530. * An array defining breaks in the axis, the sections defined will be
  14531. * left out and all the points shifted closer to each other.
  14532. *
  14533. * @productdesc {highcharts}
  14534. * Requires that the broken-axis.js module is loaded.
  14535. *
  14536. * @sample {highcharts} highcharts/axisbreak/break-simple/
  14537. * Simple break
  14538. * @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
  14539. * Advanced with callback
  14540. * @sample {highstock} stock/demo/intraday-breaks/
  14541. * Break on nights and weekends
  14542. *
  14543. * @type {Array<*>}
  14544. * @since 4.1.0
  14545. * @product highcharts highstock gantt
  14546. * @apioption xAxis.breaks
  14547. */
  14548. /**
  14549. * A number indicating how much space should be left between the start
  14550. * and the end of the break. The break size is given in axis units,
  14551. * so for instance on a `datetime` axis, a break size of 3600000 would
  14552. * indicate the equivalent of an hour.
  14553. *
  14554. * @type {number}
  14555. * @default 0
  14556. * @since 4.1.0
  14557. * @product highcharts highstock gantt
  14558. * @apioption xAxis.breaks.breakSize
  14559. */
  14560. /**
  14561. * The point where the break starts.
  14562. *
  14563. * @type {number}
  14564. * @since 4.1.0
  14565. * @product highcharts highstock gantt
  14566. * @apioption xAxis.breaks.from
  14567. */
  14568. /**
  14569. * Defines an interval after which the break appears again. By default
  14570. * the breaks do not repeat.
  14571. *
  14572. * @type {number}
  14573. * @default 0
  14574. * @since 4.1.0
  14575. * @product highcharts highstock gantt
  14576. * @apioption xAxis.breaks.repeat
  14577. */
  14578. /**
  14579. * The point where the break ends.
  14580. *
  14581. * @type {number}
  14582. * @since 4.1.0
  14583. * @product highcharts highstock gantt
  14584. * @apioption xAxis.breaks.to
  14585. */
  14586. /**
  14587. * If categories are present for the xAxis, names are used instead of
  14588. * numbers for that axis.
  14589. *
  14590. * Since Highcharts 3.0, categories can also
  14591. * be extracted by giving each point a [name](#series.data) and setting
  14592. * axis [type](#xAxis.type) to `category`. However, if you have multiple
  14593. * series, best practice remains defining the `categories` array.
  14594. *
  14595. * Example: `categories: ['Apples', 'Bananas', 'Oranges']`
  14596. *
  14597. * @sample {highcharts} highcharts/demo/line-labels/
  14598. * With
  14599. * @sample {highcharts} highcharts/xaxis/categories/
  14600. * Without
  14601. *
  14602. * @type {Array<string>}
  14603. * @product highcharts gantt
  14604. * @apioption xAxis.categories
  14605. */
  14606. /**
  14607. * The highest allowed value for automatically computed axis extremes.
  14608. *
  14609. * @see [floor](#xAxis.floor)
  14610. *
  14611. * @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
  14612. * Floor and ceiling
  14613. *
  14614. * @type {number}
  14615. * @since 4.0
  14616. * @product highcharts highstock gantt
  14617. * @apioption xAxis.ceiling
  14618. */
  14619. /**
  14620. * A class name that opens for styling the axis by CSS, especially in
  14621. * Highcharts styled mode. The class name is applied to group elements
  14622. * for the grid, axis elements and labels.
  14623. *
  14624. * @sample {highcharts|highstock|highmaps} highcharts/css/axis/
  14625. * Multiple axes with separate styling
  14626. *
  14627. * @type {string}
  14628. * @since 5.0.0
  14629. * @apioption xAxis.className
  14630. */
  14631. /**
  14632. * Configure a crosshair that follows either the mouse pointer or the
  14633. * hovered point.
  14634. *
  14635. * In styled mode, the crosshairs are styled in the
  14636. * `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
  14637. * `.highcharts-xaxis-category` classes.
  14638. *
  14639. * @productdesc {highstock}
  14640. * In Highcharts stock, by default, the crosshair is enabled on the
  14641. * X axis and disabled on the Y axis.
  14642. *
  14643. * @sample {highcharts} highcharts/xaxis/crosshair-both/
  14644. * Crosshair on both axes
  14645. * @sample {highstock} stock/xaxis/crosshairs-xy/
  14646. * Crosshair on both axes, with y axis label
  14647. * @sample {highmaps} highcharts/xaxis/crosshair-both/
  14648. * Crosshair on both axes
  14649. *
  14650. * @declare Highcharts.AxisCrosshairOptions
  14651. * @type {boolean|*}
  14652. * @default false
  14653. * @since 4.1
  14654. * @apioption xAxis.crosshair
  14655. */
  14656. /**
  14657. * The value on a perpendicular axis where this axis should cross. This
  14658. * is typically used on mathematical plots where the axes cross at 0.
  14659. * When `crossing` is set, space will not be reserved at the sides of
  14660. * the chart for axis labels and title, so those may be clipped. In this
  14661. * case it is better to place the axes without the `crossing` option.
  14662. *
  14663. * @type {number}
  14664. * @sample highcharts/xaxis/crossing
  14665. * Function plot with axes crossing at 0
  14666. * @since 11.0.1
  14667. * @apioption xAxis.crossing
  14668. */
  14669. /**
  14670. * A class name for the crosshair, especially as a hook for styling.
  14671. *
  14672. * @type {string}
  14673. * @since 5.0.0
  14674. * @apioption xAxis.crosshair.className
  14675. */
  14676. /**
  14677. * The color of the crosshair. Defaults to `#cccccc` for numeric and
  14678. * datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
  14679. * the crosshair by default highlights the whole category.
  14680. *
  14681. * @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
  14682. * Customized crosshairs
  14683. *
  14684. * @type {Highcharts.ColorType}
  14685. * @default #cccccc
  14686. * @since 4.1
  14687. * @apioption xAxis.crosshair.color
  14688. */
  14689. /**
  14690. * The dash style for the crosshair. See
  14691. * [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  14692. * for possible values.
  14693. *
  14694. * @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
  14695. * Dotted crosshair
  14696. * @sample {highstock} stock/xaxis/crosshair-dashed/
  14697. * Dashed X axis crosshair
  14698. *
  14699. * @type {Highcharts.DashStyleValue}
  14700. * @default Solid
  14701. * @since 4.1
  14702. * @apioption xAxis.crosshair.dashStyle
  14703. */
  14704. /**
  14705. * A label on the axis next to the crosshair.
  14706. *
  14707. * In styled mode, the label is styled with the
  14708. * `.highcharts-crosshair-label` class.
  14709. *
  14710. * @sample {highstock} stock/xaxis/crosshair-label/
  14711. * Crosshair labels
  14712. * @sample {highstock} highcharts/css/crosshair-label/
  14713. * Style mode
  14714. *
  14715. * @declare Highcharts.AxisCrosshairLabelOptions
  14716. * @since 2.1
  14717. * @product highstock
  14718. * @apioption xAxis.crosshair.label
  14719. */
  14720. /**
  14721. * Alignment of the label compared to the axis. Defaults to `"left"` for
  14722. * right-side axes, `"right"` for left-side axes and `"center"` for
  14723. * horizontal axes.
  14724. *
  14725. * @type {Highcharts.AlignValue}
  14726. * @since 2.1
  14727. * @product highstock
  14728. * @apioption xAxis.crosshair.label.align
  14729. */
  14730. /**
  14731. * The background color for the label. Defaults to the related series
  14732. * color, or `#666666` if that is not available.
  14733. *
  14734. * @type {Highcharts.ColorType}
  14735. * @since 2.1
  14736. * @product highstock
  14737. * @apioption xAxis.crosshair.label.backgroundColor
  14738. */
  14739. /**
  14740. * The border color for the crosshair label
  14741. *
  14742. * @type {Highcharts.ColorType}
  14743. * @since 2.1
  14744. * @product highstock
  14745. * @apioption xAxis.crosshair.label.borderColor
  14746. */
  14747. /**
  14748. * The border corner radius of the crosshair label.
  14749. *
  14750. * @type {number}
  14751. * @default 3
  14752. * @since 2.1.10
  14753. * @product highstock
  14754. * @apioption xAxis.crosshair.label.borderRadius
  14755. */
  14756. /**
  14757. * The border width for the crosshair label.
  14758. *
  14759. * @type {number}
  14760. * @default 0
  14761. * @since 2.1
  14762. * @product highstock
  14763. * @apioption xAxis.crosshair.label.borderWidth
  14764. */
  14765. /**
  14766. * Flag to enable crosshair's label.
  14767. *
  14768. * @sample {highstock} stock/xaxis/crosshairs-xy/
  14769. * Enabled label for yAxis' crosshair
  14770. *
  14771. * @type {boolean}
  14772. * @default false
  14773. * @since 2.1
  14774. * @product highstock
  14775. * @apioption xAxis.crosshair.label.enabled
  14776. */
  14777. /**
  14778. * A format string for the crosshair label. Defaults to `{value}` for
  14779. * numeric axes and `{value:%b %d, %Y}` for datetime axes.
  14780. *
  14781. * @type {string}
  14782. * @since 2.1
  14783. * @product highstock
  14784. * @apioption xAxis.crosshair.label.format
  14785. */
  14786. /**
  14787. * Formatter function for the label text.
  14788. *
  14789. * @type {Highcharts.XAxisCrosshairLabelFormatterCallbackFunction}
  14790. * @since 2.1
  14791. * @product highstock
  14792. * @apioption xAxis.crosshair.label.formatter
  14793. */
  14794. /**
  14795. * Padding inside the crosshair label.
  14796. *
  14797. * @type {number}
  14798. * @default 8
  14799. * @since 2.1
  14800. * @product highstock
  14801. * @apioption xAxis.crosshair.label.padding
  14802. */
  14803. /**
  14804. * The shape to use for the label box.
  14805. *
  14806. * @type {string}
  14807. * @default callout
  14808. * @since 2.1
  14809. * @product highstock
  14810. * @apioption xAxis.crosshair.label.shape
  14811. */
  14812. /**
  14813. * Text styles for the crosshair label.
  14814. *
  14815. * @type {Highcharts.CSSObject}
  14816. * @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
  14817. * @since 2.1
  14818. * @product highstock
  14819. * @apioption xAxis.crosshair.label.style
  14820. */
  14821. /**
  14822. * Whether the crosshair should snap to the point or follow the pointer
  14823. * independent of points.
  14824. *
  14825. * @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
  14826. * True by default
  14827. * @sample {highmaps} maps/demo/latlon-advanced/
  14828. * Snap is false
  14829. *
  14830. * @type {boolean}
  14831. * @default true
  14832. * @since 4.1
  14833. * @apioption xAxis.crosshair.snap
  14834. */
  14835. /**
  14836. * The pixel width of the crosshair. Defaults to 1 for numeric or
  14837. * datetime axes, and for one category width for category axes.
  14838. *
  14839. * @sample {highcharts} highcharts/xaxis/crosshair-customized/
  14840. * Customized crosshairs
  14841. * @sample {highstock} highcharts/xaxis/crosshair-customized/
  14842. * Customized crosshairs
  14843. * @sample {highmaps} highcharts/xaxis/crosshair-customized/
  14844. * Customized crosshairs
  14845. *
  14846. * @type {number}
  14847. * @default 1
  14848. * @since 4.1
  14849. * @apioption xAxis.crosshair.width
  14850. */
  14851. /**
  14852. * The Z index of the crosshair. Higher Z indices allow drawing the
  14853. * crosshair on top of the series or behind the grid lines.
  14854. *
  14855. * @type {number}
  14856. * @default 2
  14857. * @since 4.1
  14858. * @apioption xAxis.crosshair.zIndex
  14859. */
  14860. /**
  14861. * Whether to pan axis. If `chart.panning` is enabled, the option
  14862. * allows to disable panning on an individual axis.
  14863. */
  14864. panningEnabled: true,
  14865. /**
  14866. * The Z index for the axis group.
  14867. */
  14868. zIndex: 2,
  14869. /**
  14870. * Whether to zoom axis. If `chart.zoomType` is set, the option allows
  14871. * to disable zooming on an individual axis.
  14872. *
  14873. * @sample {highcharts} highcharts/xaxis/zoomenabled/
  14874. * Zoom enabled is false
  14875. */
  14876. zoomEnabled: true,
  14877. /**
  14878. * For a datetime axis, the scale will automatically adjust to the
  14879. * appropriate unit. This member gives the default string
  14880. * representations used for each unit. For intermediate values,
  14881. * different units may be used, for example the `day` unit can be used
  14882. * on midnight and `hour` unit be used for intermediate values on the
  14883. * same axis.
  14884. *
  14885. * For an overview of the replacement codes, see
  14886. * [dateFormat](/class-reference/Highcharts.Time#dateFormat).
  14887. *
  14888. * Defaults to:
  14889. * ```js
  14890. * {
  14891. * millisecond: '%H:%M:%S.%L',
  14892. * second: '%H:%M:%S',
  14893. * minute: '%H:%M',
  14894. * hour: '%H:%M',
  14895. * day: '%e. %b',
  14896. * week: '%e. %b',
  14897. * month: '%b \'%y',
  14898. * year: '%Y'
  14899. * }
  14900. * ```
  14901. *
  14902. * @sample {highcharts} highcharts/xaxis/datetimelabelformats/
  14903. * Different day format on X axis
  14904. * @sample {highstock} stock/xaxis/datetimelabelformats/
  14905. * More information in x axis labels
  14906. *
  14907. * @declare Highcharts.AxisDateTimeLabelFormatsOptions
  14908. * @product highcharts highstock gantt
  14909. */
  14910. dateTimeLabelFormats: {
  14911. /**
  14912. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14913. * @type {string|*}
  14914. */
  14915. millisecond: {
  14916. main: '%H:%M:%S.%L',
  14917. range: false
  14918. },
  14919. /**
  14920. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14921. * @type {string|*}
  14922. */
  14923. second: {
  14924. main: '%H:%M:%S',
  14925. range: false
  14926. },
  14927. /**
  14928. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14929. * @type {string|*}
  14930. */
  14931. minute: {
  14932. main: '%H:%M',
  14933. range: false
  14934. },
  14935. /**
  14936. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14937. * @type {string|*}
  14938. */
  14939. hour: {
  14940. main: '%H:%M',
  14941. range: false
  14942. },
  14943. /**
  14944. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14945. * @type {string|*}
  14946. */
  14947. day: {
  14948. main: '%e %b'
  14949. },
  14950. /**
  14951. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14952. * @type {string|*}
  14953. */
  14954. week: {
  14955. main: '%e %b'
  14956. },
  14957. /**
  14958. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14959. * @type {string|*}
  14960. */
  14961. month: {
  14962. main: '%b \'%y'
  14963. },
  14964. /**
  14965. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  14966. * @type {string|*}
  14967. */
  14968. year: {
  14969. main: '%Y'
  14970. }
  14971. },
  14972. /**
  14973. * Whether to force the axis to end on a tick. Use this option with
  14974. * the `maxPadding` option to control the axis end.
  14975. *
  14976. * @productdesc {highstock}
  14977. * In Highcharts Stock, `endOnTick` is always `false` when the navigator
  14978. * is enabled, to prevent jumpy scrolling.
  14979. *
  14980. * @sample {highcharts} highcharts/yaxis/endontick/
  14981. * True by default
  14982. * @sample {highcharts} highcharts/yaxis/endontick-false/
  14983. * False
  14984. * @sample {highstock} stock/demo/basic-line/
  14985. * True by default
  14986. * @sample {highstock} stock/xaxis/endontick/
  14987. * False
  14988. *
  14989. * @since 1.2.0
  14990. */
  14991. endOnTick: false,
  14992. /**
  14993. * Event handlers for the axis.
  14994. *
  14995. * @type {*}
  14996. * @apioption xAxis.events
  14997. */
  14998. /**
  14999. * An event fired after the breaks have rendered.
  15000. *
  15001. * @see [breaks](#xAxis.breaks)
  15002. *
  15003. * @sample {highcharts} highcharts/axisbreak/break-event/
  15004. * AfterBreak Event
  15005. *
  15006. * @type {Highcharts.AxisEventCallbackFunction}
  15007. * @since 4.1.0
  15008. * @product highcharts gantt
  15009. * @apioption xAxis.events.afterBreaks
  15010. */
  15011. /**
  15012. * As opposed to the `setExtremes` event, this event fires after the
  15013. * final min and max values are computed and corrected for `minRange`.
  15014. *
  15015. * Fires when the minimum and maximum is set for the axis, either by
  15016. * calling the `.setExtremes()` method or by selecting an area in the
  15017. * chart. One parameter, `event`, is passed to the function, containing
  15018. * common event information.
  15019. *
  15020. * The new user set minimum and maximum values can be found by
  15021. * `event.min` and `event.max`. These reflect the axis minimum and
  15022. * maximum in axis values. The actual data extremes are found in
  15023. * `event.dataMin` and `event.dataMax`.
  15024. *
  15025. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  15026. * @since 2.3
  15027. * @context Highcharts.Axis
  15028. * @apioption xAxis.events.afterSetExtremes
  15029. */
  15030. /**
  15031. * An event fired when a break from this axis occurs on a point.
  15032. *
  15033. * @see [breaks](#xAxis.breaks)
  15034. *
  15035. * @sample {highcharts} highcharts/axisbreak/break-visualized/
  15036. * Visualization of a Break
  15037. *
  15038. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  15039. * @since 4.1.0
  15040. * @product highcharts gantt
  15041. * @context Highcharts.Axis
  15042. * @apioption xAxis.events.pointBreak
  15043. */
  15044. /**
  15045. * An event fired when a point falls inside a break from this axis.
  15046. *
  15047. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  15048. * @product highcharts highstock gantt
  15049. * @context Highcharts.Axis
  15050. * @apioption xAxis.events.pointInBreak
  15051. */
  15052. /**
  15053. * Fires when the minimum and maximum is set for the axis, either by
  15054. * calling the `.setExtremes()` method or by selecting an area in the
  15055. * chart. One parameter, `event`, is passed to the function,
  15056. * containing common event information.
  15057. *
  15058. * The new user set minimum and maximum values can be found by
  15059. * `event.min` and `event.max`. These reflect the axis minimum and
  15060. * maximum in data values. When an axis is zoomed all the way out from
  15061. * the "Reset zoom" button, `event.min` and `event.max` are null, and
  15062. * the new extremes are set based on `this.dataMin` and `this.dataMax`.
  15063. *
  15064. * @sample {highstock} stock/xaxis/events-setextremes/
  15065. * Log new extremes on x axis
  15066. *
  15067. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  15068. * @since 1.2.0
  15069. * @context Highcharts.Axis
  15070. * @apioption xAxis.events.setExtremes
  15071. */
  15072. /**
  15073. * The lowest allowed value for automatically computed axis extremes.
  15074. *
  15075. * @see [ceiling](#yAxis.ceiling)
  15076. *
  15077. * @sample {highcharts} highcharts/yaxis/floor-ceiling/
  15078. * Floor and ceiling
  15079. * @sample {highstock} stock/demo/lazy-loading/
  15080. * Prevent negative stock price on Y axis
  15081. *
  15082. * @type {number}
  15083. * @since 4.0
  15084. * @product highcharts highstock gantt
  15085. * @apioption xAxis.floor
  15086. */
  15087. /**
  15088. * The dash or dot style of the grid lines. For possible values, see
  15089. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  15090. *
  15091. * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
  15092. * Long dashes
  15093. * @sample {highstock} stock/xaxis/gridlinedashstyle/
  15094. * Long dashes
  15095. *
  15096. * @type {Highcharts.DashStyleValue}
  15097. * @since 1.2
  15098. */
  15099. gridLineDashStyle: 'Solid',
  15100. /**
  15101. * The Z index of the grid lines.
  15102. *
  15103. * @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
  15104. * A Z index of 4 renders the grid above the graph
  15105. *
  15106. * @product highcharts highstock gantt
  15107. */
  15108. gridZIndex: 1,
  15109. /**
  15110. * An id for the axis. This can be used after render time to get
  15111. * a pointer to the axis object through `chart.get()`.
  15112. *
  15113. * @sample {highcharts} highcharts/xaxis/id/
  15114. * Get the object
  15115. * @sample {highstock} stock/xaxis/id/
  15116. * Get the object
  15117. *
  15118. * @type {string}
  15119. * @since 1.2.0
  15120. * @apioption xAxis.id
  15121. */
  15122. /**
  15123. * The axis labels show the number or category for each tick.
  15124. *
  15125. * Since v8.0.0: Labels are animated in categorized x-axis with
  15126. * updating data if `tickInterval` and `step` is set to 1.
  15127. *
  15128. * @productdesc {highmaps}
  15129. * X and Y axis labels are by default disabled in Highmaps, but the
  15130. * functionality is inherited from Highcharts and used on `colorAxis`,
  15131. * and can be enabled on X and Y axes too.
  15132. */
  15133. labels: {
  15134. /**
  15135. * What part of the string the given position is anchored to.
  15136. * If `left`, the left side of the string is at the axis position.
  15137. * Can be one of `"left"`, `"center"` or `"right"`. Defaults to
  15138. * an intelligent guess based on which side of the chart the axis
  15139. * is on and the rotation of the label.
  15140. *
  15141. * @see [reserveSpace](#xAxis.labels.reserveSpace)
  15142. *
  15143. * @sample {highcharts} highcharts/xaxis/labels-align-left/
  15144. * Left
  15145. * @sample {highcharts} highcharts/xaxis/labels-align-right/
  15146. * Right
  15147. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  15148. * Left-aligned labels on a vertical category axis
  15149. *
  15150. * @type {Highcharts.AlignValue}
  15151. * @apioption xAxis.labels.align
  15152. */
  15153. /**
  15154. * Whether to allow the axis labels to overlap. When false,
  15155. * overlapping labels are hidden.
  15156. *
  15157. * @sample {highcharts} highcharts/xaxis/labels-allowoverlap-true/
  15158. * X axis labels overlap enabled
  15159. *
  15160. * @type {boolean}
  15161. * @default false
  15162. * @apioption xAxis.labels.allowOverlap
  15163. */
  15164. /**
  15165. * For horizontal axes, the allowed degrees of label rotation
  15166. * to prevent overlapping labels. If there is enough space,
  15167. * labels are not rotated. As the chart gets narrower, it
  15168. * will start rotating the labels -45 degrees, then remove
  15169. * every second label and try again with rotations 0 and -45 etc.
  15170. * Set it to `undefined` to disable rotation, which will
  15171. * cause the labels to word-wrap if possible. Defaults to `[-45]``
  15172. * on bottom and top axes, `undefined` on left and right axes.
  15173. *
  15174. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
  15175. * Default auto rotation of 0 or -45
  15176. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
  15177. * Custom graded auto rotation
  15178. *
  15179. * @type {Array<number>}
  15180. * @default undefined
  15181. * @since 4.1.0
  15182. * @product highcharts highstock gantt
  15183. * @apioption xAxis.labels.autoRotation
  15184. */
  15185. autoRotation: void 0,
  15186. /**
  15187. * When each category width is more than this many pixels, we don't
  15188. * apply auto rotation. Instead, we lay out the axis label with word
  15189. * wrap. A lower limit makes sense when the label contains multiple
  15190. * short words that don't extend the available horizontal space for
  15191. * each label.
  15192. *
  15193. * @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
  15194. * Lower limit
  15195. *
  15196. * @since 4.1.5
  15197. * @product highcharts gantt
  15198. */
  15199. autoRotationLimit: 80,
  15200. /**
  15201. * The label's pixel distance from the perimeter of the plot area.
  15202. * On cartesian charts, this is overridden if the `labels.y` setting
  15203. * is set.
  15204. *
  15205. * @sample {highcharts} highcharts/yaxis/labels-distance/
  15206. * Polar chart, labels centered under the arc
  15207. *
  15208. * @type {number}
  15209. * @product highcharts gantt
  15210. */
  15211. distance: 15,
  15212. /**
  15213. * Enable or disable the axis labels.
  15214. *
  15215. * @sample {highcharts} highcharts/xaxis/labels-enabled/
  15216. * X axis labels disabled
  15217. * @sample {highstock} stock/xaxis/labels-enabled/
  15218. * X axis labels disabled
  15219. *
  15220. */
  15221. enabled: true,
  15222. /**
  15223. * A format string for the axis label. The context is available as
  15224. * format string variables. For example, you can use `{text}` to
  15225. * insert the default formatted text. The recommended way of adding
  15226. * units for the label is using `text`, for example `{text} km`.
  15227. *
  15228. * To add custom numeric or datetime formatting, use `{value}` with
  15229. * formatting, for example `{value:.1f}` or `{value:%Y-%m-%d}`.
  15230. *
  15231. * See
  15232. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  15233. * for more examples of formatting.
  15234. *
  15235. * The default value is not specified due to the dynamic
  15236. * nature of the default implementation.
  15237. *
  15238. * @sample {highcharts|highstock} highcharts/yaxis/labels-format/
  15239. * Add units to Y axis label
  15240. * @sample {highcharts} highcharts/xaxis/labels-format-linked/
  15241. * Linked category names
  15242. * @sample {highcharts} highcharts/xaxis/labels-format-custom/
  15243. * Custom number format
  15244. *
  15245. * @type {string}
  15246. * @since 3.0
  15247. * @apioption xAxis.labels.format
  15248. */
  15249. /**
  15250. * Callback JavaScript function to format the label. The value
  15251. * is given by `this.value`. Additional properties for `this` are
  15252. * `axis`, `chart`, `isFirst`, `isLast` and `text` which holds the
  15253. * value of the default formatter.
  15254. *
  15255. * Defaults to a built in function returning a formatted string
  15256. * depending on whether the axis is `category`, `datetime`,
  15257. * `numeric` or other.
  15258. *
  15259. * @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
  15260. * Linked category names
  15261. * @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
  15262. * Modified numeric labels
  15263. * @sample {highstock} stock/xaxis/labels-formatter/
  15264. * Added units on Y axis
  15265. *
  15266. * @type {Highcharts.AxisLabelsFormatterCallbackFunction}
  15267. * @apioption xAxis.labels.formatter
  15268. */
  15269. /**
  15270. * The number of pixels to indent the labels per level in a treegrid
  15271. * axis.
  15272. *
  15273. * @sample gantt/treegrid-axis/demo
  15274. * Indentation 10px by default.
  15275. * @sample gantt/treegrid-axis/indentation-0px
  15276. * Indentation set to 0px.
  15277. *
  15278. * @product gantt
  15279. */
  15280. indentation: 10,
  15281. /**
  15282. * Horizontal axis only. When `staggerLines` is not set,
  15283. * `maxStaggerLines` defines how many lines the axis is allowed to
  15284. * add to automatically avoid overlapping X labels. Set to `1` to
  15285. * disable overlap detection.
  15286. *
  15287. * @deprecated
  15288. * @type {number}
  15289. * @default 5
  15290. * @since 1.3.3
  15291. * @apioption xAxis.labels.maxStaggerLines
  15292. */
  15293. /**
  15294. * How to handle overflowing labels on horizontal axis. If set to
  15295. * `"allow"`, it will not be aligned at all. By default it
  15296. * `"justify"` labels inside the chart area. If there is room to
  15297. * move it, it will be aligned to the edge, else it will be removed.
  15298. *
  15299. * @since 2.2.5
  15300. * @validvalue ["allow", "justify"]
  15301. */
  15302. overflow: 'justify',
  15303. /**
  15304. * The pixel padding for axis labels, to ensure white space between
  15305. * them.
  15306. *
  15307. * @product highcharts gantt
  15308. */
  15309. padding: 5,
  15310. /**
  15311. * Whether to reserve space for the labels. By default, space is
  15312. * reserved for the labels in these cases:
  15313. *
  15314. * * On all horizontal axes.
  15315. * * On vertical axes if `label.align` is `right` on a left-side
  15316. * axis or `left` on a right-side axis.
  15317. * * On vertical axes if `label.align` is `center`.
  15318. *
  15319. * This can be turned off when for example the labels are rendered
  15320. * inside the plot area instead of outside.
  15321. *
  15322. * @see [labels.align](#xAxis.labels.align)
  15323. *
  15324. * @sample {highcharts} highcharts/xaxis/labels-reservespace/
  15325. * No reserved space, labels inside plot
  15326. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  15327. * Left-aligned labels on a vertical category axis
  15328. *
  15329. * @type {boolean}
  15330. * @since 4.1.10
  15331. * @product highcharts highstock gantt
  15332. * @apioption xAxis.labels.reserveSpace
  15333. */
  15334. reserveSpace: void 0,
  15335. /**
  15336. * Rotation of the labels in degrees. When `undefined`, the
  15337. * `autoRotation` option takes precedence.
  15338. *
  15339. * @sample {highcharts} highcharts/xaxis/labels-rotation/
  15340. * X axis labels rotated 90°
  15341. *
  15342. * @type {number}
  15343. * @default 0
  15344. * @apioption xAxis.labels.rotation
  15345. */
  15346. rotation: void 0,
  15347. /**
  15348. * Horizontal axes only. The number of lines to spread the labels
  15349. * over to make room or tighter labels. 0 disables staggering.
  15350. *
  15351. * @sample {highcharts} highcharts/xaxis/labels-staggerlines/
  15352. * Show labels over two lines
  15353. * @sample {highstock} stock/xaxis/labels-staggerlines/
  15354. * Show labels over two lines
  15355. *
  15356. * @since 2.1
  15357. */
  15358. staggerLines: 0,
  15359. /**
  15360. * To show only every _n_'th label on the axis, set the step to _n_.
  15361. * Setting the step to 2 shows every other label.
  15362. *
  15363. * By default, when 0, the step is calculated automatically to avoid
  15364. * overlap. To prevent this, set it to 1\. This usually only
  15365. * happens on a category axis, and is often a sign that you have
  15366. * chosen the wrong axis type.
  15367. *
  15368. * Read more at
  15369. * [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
  15370. * => What axis should I use?
  15371. *
  15372. * @sample {highcharts} highcharts/xaxis/labels-step/
  15373. * Showing only every other axis label on a categorized
  15374. * x-axis
  15375. * @sample {highcharts} highcharts/xaxis/labels-step-auto/
  15376. * Auto steps on a category axis
  15377. *
  15378. * @since 2.1
  15379. */
  15380. step: 0,
  15381. /**
  15382. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  15383. * to render the labels.
  15384. */
  15385. useHTML: false,
  15386. /**
  15387. * The x position offset of all labels relative to the tick
  15388. * positions on the axis. Overrides the `labels.distance` option.
  15389. *
  15390. * @type {number}
  15391. * @apioption xAxis.labels.x
  15392. */
  15393. /**
  15394. * The y position offset of all labels relative to the tick
  15395. * positions on the axis. Overrides the `labels.distance` option.
  15396. *
  15397. * @sample {highcharts} highcharts/xaxis/labels-x/
  15398. * X axis labels placed on grid lines
  15399. *
  15400. * @type {number}
  15401. * @apioption xAxis.labels.y
  15402. */
  15403. /**
  15404. * The Z index for the axis labels.
  15405. */
  15406. zIndex: 7,
  15407. /**
  15408. * CSS styles for the label. Use `whiteSpace: 'nowrap'` to prevent
  15409. * wrapping of category labels. Use `textOverflow: 'none'` to
  15410. * prevent ellipsis (dots).
  15411. *
  15412. * In styled mode, the labels are styled with the
  15413. * `.highcharts-axis-labels` class.
  15414. *
  15415. * @sample {highcharts} highcharts/xaxis/labels-style/
  15416. * Red X axis labels
  15417. *
  15418. * @type {Highcharts.CSSObject}
  15419. */
  15420. style: {
  15421. /** @internal */
  15422. color: "#333333" /* Palette.neutralColor80 */,
  15423. /** @internal */
  15424. cursor: 'default',
  15425. /** @internal */
  15426. fontSize: '0.8em'
  15427. }
  15428. },
  15429. /**
  15430. * The left position as the horizontal axis. If it's a number, it is
  15431. * interpreted as pixel position relative to the chart.
  15432. *
  15433. * Since Highcharts v5.0.13: If it's a percentage string, it is
  15434. * interpreted as percentages of the plot width, offset from plot area
  15435. * left.
  15436. *
  15437. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  15438. * Different axis position properties
  15439. *
  15440. * @type {number|string}
  15441. * @product highcharts highstock
  15442. * @apioption xAxis.left
  15443. */
  15444. /**
  15445. * The top position as the vertical axis. If it's a number, it is
  15446. * interpreted as pixel position relative to the chart.
  15447. *
  15448. * Since Highcharts 2: If it's a percentage string, it is interpreted
  15449. * as percentages of the plot height, offset from plot area top.
  15450. *
  15451. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  15452. * Different axis position properties
  15453. *
  15454. * @type {number|string}
  15455. * @product highcharts highstock
  15456. * @apioption xAxis.top
  15457. */
  15458. /**
  15459. * Index of another axis that this axis is linked to. When an axis is
  15460. * linked to a master axis, it will take the same extremes as
  15461. * the master, but as assigned by min or max or by setExtremes.
  15462. * It can be used to show additional info, or to ease reading the
  15463. * chart by duplicating the scales.
  15464. *
  15465. * @sample {highcharts} highcharts/xaxis/linkedto/
  15466. * Different string formats of the same date
  15467. * @sample {highcharts} highcharts/yaxis/linkedto/
  15468. * Y values on both sides
  15469. *
  15470. * @type {number}
  15471. * @since 2.0.2
  15472. * @product highcharts highstock gantt
  15473. * @apioption xAxis.linkedTo
  15474. */
  15475. /**
  15476. * The maximum value of the axis. If `null`, the max value is
  15477. * automatically calculated.
  15478. *
  15479. * If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
  15480. * might be rounded up.
  15481. *
  15482. * If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
  15483. * beyond the set max in order to reach the given number of ticks. The
  15484. * same may happen in a chart with multiple axes, determined by [chart.
  15485. * alignTicks](#chart), where a `tickAmount` is applied internally.
  15486. *
  15487. * @sample {highcharts} highcharts/yaxis/max-200/
  15488. * Y axis max of 200
  15489. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  15490. * Y axis max on logarithmic axis
  15491. * @sample {highstock} stock/xaxis/min-max/
  15492. * Fixed min and max on X axis
  15493. *
  15494. * @type {number|null}
  15495. * @apioption xAxis.max
  15496. */
  15497. /**
  15498. * Padding of the max value relative to the length of the axis. A
  15499. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15500. * when you don't want the highest data value to appear on the edge
  15501. * of the plot area. When the axis' `max` option is set or a max extreme
  15502. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  15503. *
  15504. * @productdesc {highstock}
  15505. * For an [ordinal](#xAxis.ordinal) axis, `minPadding` and `maxPadding`
  15506. * are ignored. Use [overscroll](#xAxis.overscroll) instead.
  15507. *
  15508. * @sample {highcharts} highcharts/yaxis/maxpadding/
  15509. * Max padding of 0.25 on y axis
  15510. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15511. * Greater min- and maxPadding
  15512. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  15513. * Add some padding
  15514. *
  15515. * @default {highcharts} 0.01
  15516. * @default {highstock|highmaps} 0
  15517. * @since 1.2.0
  15518. */
  15519. maxPadding: 0.01,
  15520. /**
  15521. * Deprecated. Use `minRange` instead.
  15522. *
  15523. * @deprecated
  15524. * @type {number}
  15525. * @product highcharts highstock
  15526. * @apioption xAxis.maxZoom
  15527. */
  15528. /**
  15529. * The minimum value of the axis. If `null` the min value is
  15530. * automatically calculated.
  15531. *
  15532. * If the [startOnTick](#yAxis.startOnTick) option is true (default),
  15533. * the `min` value might be rounded down.
  15534. *
  15535. * The automatically calculated minimum value is also affected by
  15536. * [floor](#yAxis.floor), [softMin](#yAxis.softMin),
  15537. * [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
  15538. * as well as [series.threshold](#plotOptions.series.threshold)
  15539. * and [series.softThreshold](#plotOptions.series.softThreshold).
  15540. *
  15541. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  15542. * -50 with startOnTick to false
  15543. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  15544. * -50 with startOnTick true by default
  15545. * @sample {highstock} stock/xaxis/min-max/
  15546. * Set min and max on X axis
  15547. *
  15548. * @type {number|null}
  15549. * @apioption xAxis.min
  15550. */
  15551. /**
  15552. * The dash or dot style of the minor grid lines. For possible values,
  15553. * see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  15554. *
  15555. * @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
  15556. * Long dashes on minor grid lines
  15557. * @sample {highstock} stock/xaxis/minorgridlinedashstyle/
  15558. * Long dashes on minor grid lines
  15559. *
  15560. * @type {Highcharts.DashStyleValue}
  15561. * @since 1.2
  15562. */
  15563. minorGridLineDashStyle: 'Solid',
  15564. /**
  15565. * Specific tick interval in axis units for the minor ticks. On a linear
  15566. * axis, if `"auto"`, the minor tick interval is calculated as a fifth
  15567. * of the tickInterval. If `null` or `undefined`, minor ticks are not
  15568. * shown.
  15569. *
  15570. * On logarithmic axes, the unit is the power of the value. For example,
  15571. * setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
  15572. * 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
  15573. * between 1 and 10, 10 and 100 etc.
  15574. *
  15575. * If user settings dictate minor ticks to become too dense, they don't
  15576. * make sense, and will be ignored to prevent performance problems.
  15577. *
  15578. * @sample {highcharts} highcharts/yaxis/minortickinterval-null/
  15579. * Null by default
  15580. * @sample {highcharts} highcharts/yaxis/minortickinterval-5/
  15581. * 5 units
  15582. * @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
  15583. * "auto"
  15584. * @sample {highcharts} highcharts/yaxis/minortickinterval-log/
  15585. * 0.1
  15586. * @sample {highstock} stock/demo/basic-line/
  15587. * Null by default
  15588. * @sample {highstock} stock/xaxis/minortickinterval-auto/
  15589. * "auto"
  15590. *
  15591. * @type {number|string|null}
  15592. * @apioption xAxis.minorTickInterval
  15593. */
  15594. /**
  15595. * The pixel length of the minor tick marks.
  15596. *
  15597. * @sample {highcharts} highcharts/yaxis/minorticklength/
  15598. * 10px on Y axis
  15599. * @sample {highstock} stock/xaxis/minorticks/
  15600. * 10px on Y axis
  15601. */
  15602. minorTickLength: 2,
  15603. /**
  15604. * The position of the minor tick marks relative to the axis line.
  15605. * Can be one of `inside` and `outside`.
  15606. *
  15607. * @sample {highcharts} highcharts/yaxis/minortickposition-outside/
  15608. * Outside by default
  15609. * @sample {highcharts} highcharts/yaxis/minortickposition-inside/
  15610. * Inside
  15611. * @sample {highstock} stock/xaxis/minorticks/
  15612. * Inside
  15613. *
  15614. * @validvalue ["inside", "outside"]
  15615. */
  15616. minorTickPosition: 'outside',
  15617. /**
  15618. * Enable or disable minor ticks. The interval between the minor ticks
  15619. * can be controlled either by the
  15620. * [minorTicksPerMajor](#xAxis.minorTicksPerMajor) setting, or as an
  15621. * absolute [minorTickInterval](#xAxis.minorTickInterval) value.
  15622. *
  15623. * On a logarithmic axis, minor ticks are laid out based on a best
  15624. * guess, attempting to enter an approximate number of minor ticks
  15625. * between each major tick based on
  15626. * [minorTicksPerMajor](#xAxis.minorTicksPerMajor).
  15627. *
  15628. * Prior to v6.0.0, ticks were enabled in auto layout by setting
  15629. * `minorTickInterval` to `"auto"`.
  15630. *
  15631. * @productdesc {highcharts} On axes using
  15632. * [categories](#xAxis.categories), minor ticks are not supported.
  15633. *
  15634. * @sample {highcharts} highcharts/yaxis/minorticks-true/ Enabled on
  15635. * linear Y axis
  15636. *
  15637. * @type {boolean}
  15638. * @default false
  15639. * @since 6.0.0
  15640. * @apioption xAxis.minorTicks
  15641. */
  15642. /**
  15643. * The number of minor ticks per major tick. Works for `linear`,
  15644. * `logarithmic` and `datetime` axes.
  15645. *
  15646. * @sample {highcharts} highcharts/yaxis/minortickspermajor/
  15647. * 2 minor ticks per major tick on Y axis
  15648. *
  15649. * @type {number}
  15650. */
  15651. minorTicksPerMajor: 5,
  15652. /**
  15653. * The pixel width of the minor tick mark.
  15654. *
  15655. * @sample {highcharts} highcharts/yaxis/minortickwidth/
  15656. * 3px width
  15657. * @sample {highstock} stock/xaxis/minorticks/
  15658. * 1px width
  15659. *
  15660. * @type {number}
  15661. * @default 0
  15662. * @apioption xAxis.minorTickWidth
  15663. */
  15664. /**
  15665. * Padding of the min value relative to the length of the axis. A
  15666. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  15667. * when you don't want the lowest data value to appear on the edge
  15668. * of the plot area. When the axis' `min` option is set or a min extreme
  15669. * is set using `axis.setExtremes()`, the minPadding will be ignored.
  15670. *
  15671. * @productdesc {highstock}
  15672. * For an [ordinal](#xAxis.ordinal) axis, `minPadding` and `maxPadding`
  15673. * are ignored. Use [overscroll](#xAxis.overscroll) instead.
  15674. *
  15675. * @sample {highcharts} highcharts/yaxis/minpadding/
  15676. * Min padding of 0.2
  15677. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  15678. * Greater min- and maxPadding
  15679. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  15680. * Add some padding
  15681. *
  15682. * @default {highcharts} 0.01
  15683. * @default {highstock|highmaps} 0
  15684. * @since 1.2.0
  15685. * @product highcharts highstock gantt
  15686. */
  15687. minPadding: 0.01,
  15688. /**
  15689. * The minimum range to display on this axis. The entire axis will not
  15690. * be allowed to span over a smaller interval than this. For example,
  15691. * for a datetime axis the main unit is milliseconds. If minRange is
  15692. * set to 3600000, you can't zoom in more than to one hour.
  15693. *
  15694. * The default minRange for the x axis is five times the smallest
  15695. * interval between any of the data points.
  15696. *
  15697. * On a logarithmic axis, the unit for the minimum range is the power.
  15698. * So a minRange of 1 means that the axis can be zoomed to 10-100,
  15699. * 100-1000, 1000-10000 etc.
  15700. *
  15701. * **Note**: The `minPadding`, `maxPadding`, `startOnTick` and
  15702. * `endOnTick` settings also affect how the extremes of the axis
  15703. * are computed.
  15704. *
  15705. * @sample {highcharts} highcharts/xaxis/minrange/
  15706. * Minimum range of 5
  15707. * @sample {highstock} stock/xaxis/minrange/
  15708. * Max zoom of 6 months overrides user selections
  15709. *
  15710. * @type {number}
  15711. * @apioption xAxis.minRange
  15712. */
  15713. /**
  15714. * The minimum tick interval allowed in axis values. For example on
  15715. * zooming in on an axis with daily data, this can be used to prevent
  15716. * the axis from showing hours. Defaults to the closest distance between
  15717. * two points on the axis.
  15718. *
  15719. * @type {number}
  15720. * @since 2.3.0
  15721. * @apioption xAxis.minTickInterval
  15722. */
  15723. /**
  15724. * The distance in pixels from the plot area to the axis line.
  15725. * A positive offset moves the axis with it's line, labels and ticks
  15726. * away from the plot area. This is typically used when two or more
  15727. * axes are displayed on the same side of the plot. With multiple
  15728. * axes the offset is dynamically adjusted to avoid collision, this
  15729. * can be overridden by setting offset explicitly.
  15730. *
  15731. * @sample {highcharts} highcharts/yaxis/offset/
  15732. * Y axis offset of 70
  15733. * @sample {highcharts} highcharts/yaxis/offset-centered/
  15734. * Axes positioned in the center of the plot
  15735. * @sample {highstock} stock/xaxis/offset/
  15736. * Y axis offset by 70 px
  15737. *
  15738. * @type {number}
  15739. */
  15740. offset: void 0,
  15741. /**
  15742. * Whether to display the axis on the opposite side of the normal. The
  15743. * normal is on the left side for vertical axes and bottom for
  15744. * horizontal, so the opposite sides will be right and top respectively.
  15745. * This is typically used with dual or multiple axes.
  15746. *
  15747. * @sample {highcharts} highcharts/yaxis/opposite/
  15748. * Secondary Y axis opposite
  15749. * @sample {highstock} stock/xaxis/opposite/
  15750. * Y axis on left side
  15751. *
  15752. * @default {highcharts|highstock|highmaps} false
  15753. * @default {gantt} true
  15754. */
  15755. opposite: false,
  15756. /**
  15757. * In an ordinal axis, the points are equally spaced in the chart
  15758. * regardless of the actual time or x distance between them. This means
  15759. * that missing data periods (e.g. nights or weekends for a stock chart)
  15760. * will not take up space in the chart.
  15761. * Having `ordinal: false` will show any gaps created by the `gapSize`
  15762. * setting proportionate to their duration.
  15763. *
  15764. * In stock charts the X axis is ordinal by default, unless
  15765. * the boost module is used and at least one of the series' data length
  15766. * exceeds the [boostThreshold](#series.line.boostThreshold).
  15767. *
  15768. * For an ordinal axis, `minPadding` and `maxPadding` are ignored. Use
  15769. * [overscroll](#xAxis.overscroll) instead.
  15770. *
  15771. * @sample {highstock} stock/xaxis/ordinal-true/
  15772. * True by default
  15773. * @sample {highstock} stock/xaxis/ordinal-false/
  15774. * False
  15775. *
  15776. * @see [overscroll](#xAxis.overscroll)
  15777. *
  15778. * @type {boolean}
  15779. * @default true
  15780. * @since 1.1
  15781. * @product highstock
  15782. * @apioption xAxis.ordinal
  15783. */
  15784. /**
  15785. * Additional range on the right side of the xAxis. Works similar to
  15786. * `xAxis.maxPadding`, but value is set in milliseconds. Can be set for
  15787. * both main `xAxis` and the navigator's `xAxis`.
  15788. *
  15789. * @sample {highstock} stock/xaxis/overscroll/
  15790. * One minute overscroll with live data
  15791. *
  15792. * @type {number}
  15793. * @default 0
  15794. * @since 6.0.0
  15795. * @product highstock
  15796. * @apioption xAxis.overscroll
  15797. */
  15798. /**
  15799. * Refers to the index in the [panes](#panes) array. Used for circular
  15800. * gauges and polar charts. When the option is not set then first pane
  15801. * will be used.
  15802. *
  15803. * @sample highcharts/demo/gauge-vu-meter
  15804. * Two gauges with different center
  15805. *
  15806. * @type {number}
  15807. * @product highcharts
  15808. * @apioption xAxis.pane
  15809. */
  15810. /**
  15811. * The zoomed range to display when only defining one or none of `min`
  15812. * or `max`. For example, to show the latest month, a range of one month
  15813. * can be set.
  15814. *
  15815. * @sample {highstock} stock/xaxis/range/
  15816. * Setting a zoomed range when the rangeSelector is disabled
  15817. *
  15818. * @type {number}
  15819. * @product highstock
  15820. * @apioption xAxis.range
  15821. */
  15822. /**
  15823. * Whether to reverse the axis so that the highest number is closest
  15824. * to the origin. If the chart is inverted, the x axis is reversed by
  15825. * default.
  15826. *
  15827. * @sample {highcharts} highcharts/yaxis/reversed/
  15828. * Reversed Y axis
  15829. * @sample {highstock} stock/xaxis/reversed/
  15830. * Reversed Y axis
  15831. *
  15832. * @type {boolean}
  15833. * @default undefined
  15834. * @apioption xAxis.reversed
  15835. */
  15836. reversed: void 0,
  15837. /**
  15838. * This option determines how stacks should be ordered within a group.
  15839. * For example reversed xAxis also reverses stacks, so first series
  15840. * comes last in a group. To keep order like for non-reversed xAxis
  15841. * enable this option.
  15842. *
  15843. * @sample {highcharts} highcharts/xaxis/reversedstacks/
  15844. * Reversed stacks comparison
  15845. * @sample {highstock} highcharts/xaxis/reversedstacks/
  15846. * Reversed stacks comparison
  15847. *
  15848. * @since 6.1.1
  15849. * @product highcharts highstock
  15850. */
  15851. reversedStacks: false,
  15852. /**
  15853. * An optional scrollbar to display on the X axis in response to
  15854. * limiting the minimum and maximum of the axis values.
  15855. *
  15856. * In styled mode, all the presentational options for the scrollbar are
  15857. * replaced by the classes `.highcharts-scrollbar-thumb`,
  15858. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  15859. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  15860. *
  15861. * @sample {highstock} stock/yaxis/heatmap-scrollbars/
  15862. * Heatmap with both scrollbars
  15863. *
  15864. * @extends scrollbar
  15865. * @since 4.2.6
  15866. * @product highstock
  15867. * @apioption xAxis.scrollbar
  15868. */
  15869. /**
  15870. * Whether to show the axis line and title when the axis has no data.
  15871. *
  15872. * @sample {highcharts} highcharts/yaxis/showempty/
  15873. * When clicking the legend to hide series, one axis preserves
  15874. * line and title, the other doesn't
  15875. * @sample {highstock} highcharts/yaxis/showempty/
  15876. * When clicking the legend to hide series, one axis preserves
  15877. * line and title, the other doesn't
  15878. *
  15879. * @since 1.1
  15880. */
  15881. showEmpty: true,
  15882. /**
  15883. * Whether to show the first tick label.
  15884. *
  15885. * @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
  15886. * Set to false on X axis
  15887. * @sample {highstock} stock/xaxis/showfirstlabel/
  15888. * Labels below plot lines on Y axis
  15889. */
  15890. showFirstLabel: true,
  15891. /**
  15892. * Whether to show the last tick label. Defaults to `true` on cartesian
  15893. * charts, and `false` on polar charts.
  15894. *
  15895. * @sample {highcharts} highcharts/xaxis/showlastlabel-true/
  15896. * Set to true on X axis
  15897. * @sample {highstock} stock/xaxis/showfirstlabel/
  15898. * Labels below plot lines on Y axis
  15899. *
  15900. * @type {boolean}
  15901. * @default undefined
  15902. * @product highcharts highstock gantt
  15903. */
  15904. showLastLabel: true,
  15905. /**
  15906. * A soft maximum for the axis. If the series data maximum is less than
  15907. * this, the axis will stay at this maximum, but if the series data
  15908. * maximum is higher, the axis will flex to show all data.
  15909. *
  15910. * @sample highcharts/yaxis/softmin-softmax/
  15911. * Soft min and max
  15912. *
  15913. * @type {number}
  15914. * @since 5.0.1
  15915. * @product highcharts highstock gantt
  15916. * @apioption xAxis.softMax
  15917. */
  15918. /**
  15919. * A soft minimum for the axis. If the series data minimum is greater
  15920. * than this, the axis will stay at this minimum, but if the series
  15921. * data minimum is lower, the axis will flex to show all data.
  15922. *
  15923. * @sample highcharts/yaxis/softmin-softmax/
  15924. * Soft min and max
  15925. *
  15926. * @type {number}
  15927. * @since 5.0.1
  15928. * @product highcharts highstock gantt
  15929. * @apioption xAxis.softMin
  15930. */
  15931. /**
  15932. * For datetime axes, this decides where to put the tick between weeks.
  15933. * 0 = Sunday, 1 = Monday.
  15934. *
  15935. * @sample {highcharts} highcharts/xaxis/startofweek-monday/
  15936. * Monday by default
  15937. * @sample {highcharts} highcharts/xaxis/startofweek-sunday/
  15938. * Sunday
  15939. * @sample {highstock} stock/xaxis/startofweek-1
  15940. * Monday by default
  15941. * @sample {highstock} stock/xaxis/startofweek-0
  15942. * Sunday
  15943. *
  15944. * @product highcharts highstock gantt
  15945. */
  15946. startOfWeek: 1,
  15947. /**
  15948. * Whether to force the axis to start on a tick. Use this option with
  15949. * the `minPadding` option to control the axis start.
  15950. *
  15951. * @productdesc {highstock}
  15952. * In Highcharts Stock, `startOnTick` is always `false` when
  15953. * the navigator is enabled, to prevent jumpy scrolling.
  15954. *
  15955. * @sample {highcharts} highcharts/xaxis/startontick-false/
  15956. * False by default
  15957. * @sample {highcharts} highcharts/xaxis/startontick-true/
  15958. * True
  15959. *
  15960. * @since 1.2.0
  15961. */
  15962. startOnTick: false,
  15963. /**
  15964. * The amount of ticks to draw on the axis. This opens up for aligning
  15965. * the ticks of multiple charts or panes within a chart. This option
  15966. * overrides the `tickPixelInterval` option.
  15967. *
  15968. * This option only has an effect on linear axes. Datetime, logarithmic
  15969. * or category axes are not affected.
  15970. *
  15971. * @sample {highcharts} highcharts/yaxis/tickamount/
  15972. * 8 ticks on Y axis
  15973. * @sample {highstock} highcharts/yaxis/tickamount/
  15974. * 8 ticks on Y axis
  15975. *
  15976. * @type {number}
  15977. * @since 4.1.0
  15978. * @product highcharts highstock gantt
  15979. * @apioption xAxis.tickAmount
  15980. */
  15981. /**
  15982. * The interval of the tick marks in axis units. When `undefined`, the
  15983. * tick interval is computed to approximately follow the
  15984. * [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
  15985. * axes. On categorized axes, a `undefined` tickInterval will default to
  15986. * 1, one category. Note that datetime axes are based on milliseconds,
  15987. * so for example an interval of one day is expressed as
  15988. * `24 * 3600 * 1000`.
  15989. *
  15990. * On logarithmic axes, the tickInterval is based on powers, so a
  15991. * tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
  15992. * tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
  15993. * of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
  15994. * 40 etc.
  15995. *
  15996. *
  15997. * If the tickInterval is too dense for labels to be drawn, Highcharts
  15998. * may remove ticks.
  15999. *
  16000. * If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
  16001. * option may interfere with the `tickInterval` setting.
  16002. *
  16003. * @see [tickPixelInterval](#xAxis.tickPixelInterval)
  16004. * @see [tickPositions](#xAxis.tickPositions)
  16005. * @see [tickPositioner](#xAxis.tickPositioner)
  16006. *
  16007. * @sample {highcharts} highcharts/xaxis/tickinterval-5/
  16008. * Tick interval of 5 on a linear axis
  16009. * @sample {highstock} stock/xaxis/tickinterval/
  16010. * Tick interval of 0.01 on Y axis
  16011. *
  16012. * @type {number}
  16013. * @apioption xAxis.tickInterval
  16014. */
  16015. /**
  16016. * The pixel length of the main tick marks.
  16017. *
  16018. * @sample {highcharts} highcharts/xaxis/ticklength/
  16019. * 20 px tick length on the X axis
  16020. * @sample {highstock} stock/xaxis/ticks/
  16021. * Formatted ticks on X axis
  16022. */
  16023. tickLength: 10,
  16024. /**
  16025. * If tickInterval is `null` this option sets the approximate pixel
  16026. * interval of the tick marks. Not applicable to categorized axis.
  16027. *
  16028. * The tick interval is also influenced by the [minTickInterval](
  16029. * #xAxis.minTickInterval) option, that, by default prevents ticks from
  16030. * being denser than the data points.
  16031. *
  16032. * @see [tickInterval](#xAxis.tickInterval)
  16033. * @see [tickPositioner](#xAxis.tickPositioner)
  16034. * @see [tickPositions](#xAxis.tickPositions)
  16035. *
  16036. * @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
  16037. * 50 px on X axis
  16038. * @sample {highstock} stock/xaxis/tickpixelinterval/
  16039. * 200 px on X axis
  16040. */
  16041. tickPixelInterval: 100,
  16042. /**
  16043. * For categorized axes only. If `on` the tick mark is placed in the
  16044. * center of the category, if `between` the tick mark is placed between
  16045. * categories. The default is `between` if the `tickInterval` is 1, else
  16046. * `on`.
  16047. *
  16048. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
  16049. * "between" by default
  16050. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
  16051. * "on"
  16052. *
  16053. * @product highcharts gantt
  16054. * @validvalue ["on", "between"]
  16055. */
  16056. tickmarkPlacement: 'between',
  16057. /**
  16058. * The position of the major tick marks relative to the axis line.
  16059. * Can be one of `inside` and `outside`.
  16060. *
  16061. * @sample {highcharts} highcharts/xaxis/tickposition-outside/
  16062. * "outside" by default
  16063. * @sample {highcharts} highcharts/xaxis/tickposition-inside/
  16064. * "inside"
  16065. * @sample {highstock} stock/xaxis/ticks/
  16066. * Formatted ticks on X axis
  16067. *
  16068. * @validvalue ["inside", "outside"]
  16069. */
  16070. tickPosition: 'outside',
  16071. /**
  16072. * A callback function returning array defining where the ticks are
  16073. * laid out on the axis. This overrides the default behaviour of
  16074. * [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
  16075. * #xAxis.tickInterval). The automatic tick positions are accessible
  16076. * through `this.tickPositions` and can be modified by the callback.
  16077. *
  16078. * @see [tickPositions](#xAxis.tickPositions)
  16079. *
  16080. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  16081. * Demo of tickPositions and tickPositioner
  16082. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  16083. * Demo of tickPositions and tickPositioner
  16084. *
  16085. * @type {Highcharts.AxisTickPositionerCallbackFunction}
  16086. * @apioption xAxis.tickPositioner
  16087. */
  16088. /**
  16089. * An array defining where the ticks are laid out on the axis. This
  16090. * overrides the default behaviour of [tickPixelInterval](
  16091. * #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
  16092. *
  16093. * @see [tickPositioner](#xAxis.tickPositioner)
  16094. *
  16095. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  16096. * Demo of tickPositions and tickPositioner
  16097. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  16098. * Demo of tickPositions and tickPositioner
  16099. *
  16100. * @type {Array<number>}
  16101. * @apioption xAxis.tickPositions
  16102. */
  16103. /**
  16104. * The pixel width of the major tick marks. Defaults to 0 on category
  16105. * axes, otherwise 1.
  16106. *
  16107. * In styled mode, the stroke width is given in the `.highcharts-tick`
  16108. * class, but in order for the element to be generated on category axes,
  16109. * the option must be explicitly set to 1.
  16110. *
  16111. * @sample {highcharts} highcharts/xaxis/tickwidth/
  16112. * 10 px width
  16113. * @sample {highcharts} highcharts/css/axis-grid/
  16114. * Styled mode
  16115. * @sample {highstock} stock/xaxis/ticks/
  16116. * Formatted ticks on X axis
  16117. * @sample {highstock} highcharts/css/axis-grid/
  16118. * Styled mode
  16119. *
  16120. * @type {undefined|number}
  16121. * @default {highstock} 1
  16122. * @default {highmaps} 0
  16123. * @apioption xAxis.tickWidth
  16124. */
  16125. /**
  16126. * The axis title, showing next to the axis line.
  16127. *
  16128. * @productdesc {highmaps}
  16129. * In Highmaps, the axis is hidden by default, but adding an axis title
  16130. * is still possible. X axis and Y axis titles will appear at the bottom
  16131. * and left by default.
  16132. */
  16133. title: {
  16134. /**
  16135. * Alignment of the title relative to the axis values. Possible
  16136. * values are "low", "middle" or "high".
  16137. *
  16138. * @sample {highcharts} highcharts/xaxis/title-align-low/
  16139. * "low"
  16140. * @sample {highcharts} highcharts/xaxis/title-align-center/
  16141. * "middle" by default
  16142. * @sample {highcharts} highcharts/xaxis/title-align-high/
  16143. * "high"
  16144. * @sample {highcharts} highcharts/yaxis/title-offset/
  16145. * Place the Y axis title on top of the axis
  16146. * @sample {highstock} stock/xaxis/title-align/
  16147. * Aligned to "high" value
  16148. *
  16149. * @type {Highcharts.AxisTitleAlignValue}
  16150. */
  16151. align: 'middle',
  16152. /**
  16153. * Deprecated. Set the `text` to `undefined` to disable the title.
  16154. *
  16155. * @deprecated
  16156. * @type {boolean}
  16157. * @product highcharts
  16158. * @apioption xAxis.title.enabled
  16159. */
  16160. /**
  16161. * The pixel distance between the axis labels or line and the title.
  16162. * Defaults to 0 for horizontal axes, 10 for vertical
  16163. *
  16164. * @sample {highcharts} highcharts/xaxis/title-margin/
  16165. * Y axis title margin of 60
  16166. *
  16167. * @type {number}
  16168. * @apioption xAxis.title.margin
  16169. */
  16170. /**
  16171. * The distance of the axis title from the axis line. By default,
  16172. * this distance is computed from the offset width of the labels,
  16173. * the labels' distance from the axis and the title's margin.
  16174. * However when the offset option is set, it overrides all this.
  16175. *
  16176. * @sample {highcharts} highcharts/yaxis/title-offset/
  16177. * Place the axis title on top of the axis
  16178. * @sample {highstock} highcharts/yaxis/title-offset/
  16179. * Place the axis title on top of the Y axis
  16180. *
  16181. * @type {number}
  16182. * @since 2.2.0
  16183. * @apioption xAxis.title.offset
  16184. */
  16185. /**
  16186. * Whether to reserve space for the title when laying out the axis.
  16187. *
  16188. * @type {boolean}
  16189. * @default true
  16190. * @since 5.0.11
  16191. * @product highcharts highstock gantt
  16192. * @apioption xAxis.title.reserveSpace
  16193. */
  16194. /**
  16195. * The rotation of the text in degrees. 0 is horizontal, 270 is
  16196. * vertical reading from bottom to top.
  16197. *
  16198. * @sample {highcharts} highcharts/yaxis/title-offset/
  16199. * Horizontal
  16200. */
  16201. rotation: 0,
  16202. /**
  16203. * The actual text of the axis title. It can contain basic HTML tags
  16204. * like `b`, `i` and `span` with style.
  16205. *
  16206. * @sample {highcharts} highcharts/xaxis/title-text/
  16207. * Custom HTML
  16208. * @sample {highstock} stock/xaxis/title-text/
  16209. * Titles for both axes
  16210. *
  16211. * @type {string|null}
  16212. * @apioption xAxis.title.text
  16213. */
  16214. /**
  16215. * Alignment of the text, can be `"left"`, `"right"` or `"center"`.
  16216. * Default alignment depends on the
  16217. * [title.align](xAxis.title.align):
  16218. *
  16219. * Horizontal axes:
  16220. * - for `align` = `"low"`, `textAlign` is set to `left`
  16221. * - for `align` = `"middle"`, `textAlign` is set to `center`
  16222. * - for `align` = `"high"`, `textAlign` is set to `right`
  16223. *
  16224. * Vertical axes:
  16225. * - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
  16226. * set to `right`
  16227. * - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
  16228. * set to `left`
  16229. * - for `align` = `"middle"`, `textAlign` is set to `center`
  16230. * - for `align` = `"high"` and `opposite` = `true` `textAlign` is
  16231. * set to `left`
  16232. * - for `align` = `"high"` and `opposite` = `false` `textAlign` is
  16233. * set to `right`
  16234. *
  16235. * @type {Highcharts.AlignValue}
  16236. * @apioption xAxis.title.textAlign
  16237. */
  16238. /**
  16239. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  16240. * to render the axis title.
  16241. *
  16242. * @product highcharts highstock gantt
  16243. */
  16244. useHTML: false,
  16245. /**
  16246. * Horizontal pixel offset of the title position.
  16247. *
  16248. * @since 4.1.6
  16249. * @product highcharts highstock gantt
  16250. */
  16251. x: 0,
  16252. /**
  16253. * Vertical pixel offset of the title position.
  16254. *
  16255. * @product highcharts highstock gantt
  16256. */
  16257. y: 0,
  16258. /**
  16259. * CSS styles for the title. If the title text is longer than the
  16260. * axis length, it will wrap to multiple lines by default. This can
  16261. * be customized by setting `textOverflow: 'ellipsis'`, by
  16262. * setting a specific `width` or by setting `whiteSpace: 'nowrap'`.
  16263. *
  16264. * In styled mode, the stroke width is given in the
  16265. * `.highcharts-axis-title` class.
  16266. *
  16267. * @sample {highcharts} highcharts/xaxis/title-style/
  16268. * Red
  16269. * @sample {highcharts} highcharts/css/axis/
  16270. * Styled mode
  16271. *
  16272. * @type {Highcharts.CSSObject}
  16273. */
  16274. style: {
  16275. /** @internal */
  16276. color: "#666666" /* Palette.neutralColor60 */,
  16277. /** @internal */
  16278. fontSize: '0.8em'
  16279. }
  16280. },
  16281. /**
  16282. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
  16283. * or `category`. In a datetime axis, the numbers are given in
  16284. * milliseconds, and tick marks are placed on appropriate values like
  16285. * full hours or days. In a category axis, the
  16286. * [point names](#series.line.data.name) of the chart's series are used
  16287. * for categories, if not a [categories](#xAxis.categories) array is
  16288. * defined.
  16289. *
  16290. * @sample {highcharts} highcharts/xaxis/type-linear/
  16291. * Linear
  16292. * @sample {highcharts} highcharts/yaxis/type-log/
  16293. * Logarithmic
  16294. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  16295. * Logarithmic with minor grid lines
  16296. * @sample {highcharts} highcharts/xaxis/type-log-both/
  16297. * Logarithmic on two axes
  16298. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  16299. * Logarithmic with extension to emulate negative values
  16300. *
  16301. * @type {Highcharts.AxisTypeValue}
  16302. * @product highcharts gantt
  16303. */
  16304. type: 'linear',
  16305. /**
  16306. * If there are multiple axes on the same side of the chart, the pixel
  16307. * margin between the axes. Defaults to 0 on vertical axes, 15 on
  16308. * horizontal axes.
  16309. *
  16310. * @type {number}
  16311. * @since 7.0.3
  16312. * @apioption xAxis.margin
  16313. */
  16314. /**
  16315. * Applies only when the axis `type` is `category`. When `uniqueNames`
  16316. * is true, points are placed on the X axis according to their names.
  16317. * If the same point name is repeated in the same or another series,
  16318. * the point is placed on the same X position as other points of the
  16319. * same name. When `uniqueNames` is false, the points are laid out in
  16320. * increasing X positions regardless of their names, and the X axis
  16321. * category will take the name of the last point in each position.
  16322. *
  16323. * @sample {highcharts} highcharts/xaxis/uniquenames-true/
  16324. * True by default
  16325. * @sample {highcharts} highcharts/xaxis/uniquenames-false/
  16326. * False
  16327. *
  16328. * @since 4.2.7
  16329. * @product highcharts gantt
  16330. */
  16331. uniqueNames: true,
  16332. /**
  16333. * Datetime axis only. An array determining what time intervals the
  16334. * ticks are allowed to fall on. Each array item is an array where the
  16335. * first value is the time unit and the second value another array of
  16336. * allowed multiples.
  16337. *
  16338. * Defaults to:
  16339. * ```js
  16340. * units: [[
  16341. * 'millisecond', // unit name
  16342. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  16343. * ], [
  16344. * 'second',
  16345. * [1, 2, 5, 10, 15, 30]
  16346. * ], [
  16347. * 'minute',
  16348. * [1, 2, 5, 10, 15, 30]
  16349. * ], [
  16350. * 'hour',
  16351. * [1, 2, 3, 4, 6, 8, 12]
  16352. * ], [
  16353. * 'day',
  16354. * [1, 2]
  16355. * ], [
  16356. * 'week',
  16357. * [1, 2]
  16358. * ], [
  16359. * 'month',
  16360. * [1, 2, 3, 4, 6]
  16361. * ], [
  16362. * 'year',
  16363. * null
  16364. * ]]
  16365. * ```
  16366. *
  16367. * @sample {highcharts} highcharts/xaxis/units/
  16368. * Axis units demonstrated
  16369. *
  16370. * @type {Array<Array<string,(Array<number>|null)>>}
  16371. * @product highcharts highstock gantt
  16372. * @apioption xAxis.units
  16373. */
  16374. /**
  16375. * Whether axis, including axis title, line, ticks and labels, should
  16376. * be visible.
  16377. *
  16378. * @since 4.1.9
  16379. * @product highcharts highstock gantt
  16380. */
  16381. visible: true,
  16382. /**
  16383. * Color of the minor, secondary grid lines.
  16384. *
  16385. * In styled mode, the stroke width is given in the
  16386. * `.highcharts-minor-grid-line` class.
  16387. *
  16388. * @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
  16389. * Bright grey lines from Y axis
  16390. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16391. * Styled mode
  16392. * @sample {highstock} stock/xaxis/minorgridlinecolor/
  16393. * Bright grey lines from Y axis
  16394. *
  16395. * @type {Highcharts.ColorType}
  16396. * @default #f2f2f2
  16397. */
  16398. minorGridLineColor: "#f2f2f2" /* Palette.neutralColor5 */,
  16399. /**
  16400. * Width of the minor, secondary grid lines.
  16401. *
  16402. * In styled mode, the stroke width is given in the
  16403. * `.highcharts-grid-line` class.
  16404. *
  16405. * @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
  16406. * 2px lines from Y axis
  16407. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16408. * Styled mode
  16409. * @sample {highstock} stock/xaxis/minorgridlinewidth/
  16410. * 2px lines from Y axis
  16411. */
  16412. minorGridLineWidth: 1,
  16413. /**
  16414. * Color for the minor tick marks.
  16415. *
  16416. * @sample {highcharts} highcharts/yaxis/minortickcolor/
  16417. * Black tick marks on Y axis
  16418. * @sample {highstock} stock/xaxis/minorticks/
  16419. * Black tick marks on Y axis
  16420. *
  16421. * @type {Highcharts.ColorType}
  16422. * @default #999999
  16423. */
  16424. minorTickColor: "#999999" /* Palette.neutralColor40 */,
  16425. /**
  16426. * The color of the line marking the axis itself.
  16427. *
  16428. * In styled mode, the line stroke is given in the
  16429. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  16430. *
  16431. * @sample {highcharts} highcharts/yaxis/linecolor/
  16432. * A red line on Y axis
  16433. * @sample {highcharts|highstock} highcharts/css/axis/
  16434. * Axes in styled mode
  16435. * @sample {highstock} stock/xaxis/linecolor/
  16436. * A red line on X axis
  16437. *
  16438. * @type {Highcharts.ColorType}
  16439. */
  16440. lineColor: "#333333" /* Palette.neutralColor80 */,
  16441. /**
  16442. * The width of the line marking the axis itself.
  16443. *
  16444. * In styled mode, the stroke width is given in the
  16445. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  16446. *
  16447. * @sample {highcharts} highcharts/yaxis/linecolor/
  16448. * A 1px line on Y axis
  16449. * @sample {highcharts|highstock} highcharts/css/axis/
  16450. * Axes in styled mode
  16451. * @sample {highstock} stock/xaxis/linewidth/
  16452. * A 2px line on X axis
  16453. *
  16454. * @default {highcharts|highstock} 1
  16455. * @default {highmaps} 0
  16456. */
  16457. lineWidth: 1,
  16458. /**
  16459. * Color of the grid lines extending the ticks across the plot area.
  16460. *
  16461. * In styled mode, the stroke is given in the `.highcharts-grid-line`
  16462. * class.
  16463. *
  16464. * @productdesc {highmaps}
  16465. * In Highmaps, the grid lines are hidden by default.
  16466. *
  16467. * @sample {highcharts} highcharts/yaxis/gridlinecolor/
  16468. * Green lines
  16469. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16470. * Styled mode
  16471. * @sample {highstock} stock/xaxis/gridlinecolor/
  16472. * Green lines
  16473. *
  16474. * @type {Highcharts.ColorType}
  16475. * @default #e6e6e6
  16476. */
  16477. gridLineColor: "#e6e6e6" /* Palette.neutralColor10 */,
  16478. /**
  16479. * The width of the grid lines extending the ticks across the plot area.
  16480. * Defaults to 1 on the Y axis and 0 on the X axis, except for 3d
  16481. * charts.
  16482. *
  16483. * In styled mode, the stroke width is given in the
  16484. * `.highcharts-grid-line` class.
  16485. *
  16486. * @sample {highcharts} highcharts/yaxis/gridlinewidth/
  16487. * 2px lines
  16488. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16489. * Styled mode
  16490. * @sample {highstock} stock/xaxis/gridlinewidth/
  16491. * 2px lines
  16492. *
  16493. * @type {number}
  16494. * @apioption xAxis.gridLineWidth
  16495. */
  16496. gridLineWidth: void 0,
  16497. /**
  16498. * The height as the vertical axis. If it's a number, it is
  16499. * interpreted as pixels.
  16500. *
  16501. * Since Highcharts 2: If it's a percentage string, it is interpreted
  16502. * as percentages of the total plot height.
  16503. *
  16504. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  16505. * Different axis position properties
  16506. *
  16507. * @type {number|string}
  16508. * @product highcharts highstock
  16509. * @apioption xAxis.height
  16510. */
  16511. /**
  16512. * The width as the horizontal axis. If it's a number, it is interpreted
  16513. * as pixels.
  16514. *
  16515. * Since Highcharts v5.0.13: If it's a percentage string, it is
  16516. * interpreted as percentages of the total plot width.
  16517. *
  16518. * @sample {highcharts} highcharts/xaxis/axis-position-properties
  16519. * Different axis position properties
  16520. *
  16521. * @type {number|string}
  16522. * @product highcharts highstock
  16523. * @apioption xAxis.width
  16524. */
  16525. /**
  16526. * Color for the main tick marks.
  16527. *
  16528. * In styled mode, the stroke is given in the `.highcharts-tick`
  16529. * class.
  16530. *
  16531. * @sample {highcharts} highcharts/xaxis/tickcolor/
  16532. * Red ticks on X axis
  16533. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  16534. * Styled mode
  16535. * @sample {highstock} stock/xaxis/ticks/
  16536. * Formatted ticks on X axis
  16537. *
  16538. * @type {Highcharts.ColorType}
  16539. */
  16540. tickColor: "#333333" /* Palette.neutralColor80 */
  16541. // tickWidth: 1
  16542. };
  16543. /**
  16544. * The Y axis or value axis. Normally this is the vertical axis,
  16545. * though if the chart is inverted this is the horizontal axis.
  16546. * In case of multiple axes, the yAxis node is an array of
  16547. * configuration objects.
  16548. *
  16549. * See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
  16550. * access to the axis.
  16551. *
  16552. * @type {*|Array<*>}
  16553. * @extends xAxis
  16554. * @excluding currentDateIndicator,ordinal,overscroll
  16555. * @optionparent yAxis
  16556. */
  16557. AxisDefaults.defaultYAxisOptions = {
  16558. /**
  16559. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
  16560. * `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
  16561. * `linear` for other chart types.
  16562. *
  16563. * In a datetime axis, the numbers are given in milliseconds, and tick
  16564. * marks are placed on appropriate values, like full hours or days. In a
  16565. * category or treegrid axis, the [point names](#series.line.data.name)
  16566. * of the chart's series are used for categories, if a
  16567. * [categories](#xAxis.categories) array is not defined.
  16568. *
  16569. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  16570. * Logarithmic with minor grid lines
  16571. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  16572. * Logarithmic with extension to emulate negative values
  16573. * @sample {gantt} gantt/treegrid-axis/demo
  16574. * Treegrid axis
  16575. *
  16576. * @type {Highcharts.AxisTypeValue}
  16577. * @default {highcharts} linear
  16578. * @default {gantt} treegrid
  16579. * @product highcharts gantt
  16580. * @apioption yAxis.type
  16581. */
  16582. /**
  16583. * The height of the Y axis. If it's a number, it is interpreted as
  16584. * pixels.
  16585. *
  16586. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  16587. * percentages of the total plot height.
  16588. *
  16589. * @see [yAxis.top](#yAxis.top)
  16590. *
  16591. * @sample {highstock} stock/demo/candlestick-and-volume/
  16592. * Percentage height panes
  16593. *
  16594. * @type {number|string}
  16595. * @product highcharts highstock
  16596. * @apioption yAxis.height
  16597. */
  16598. /**
  16599. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  16600. * to represent the maximum value of the Y axis.
  16601. *
  16602. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  16603. * Min and max colors
  16604. *
  16605. * @type {Highcharts.ColorType}
  16606. * @default #003399
  16607. * @since 4.0
  16608. * @product highcharts
  16609. * @apioption yAxis.maxColor
  16610. */
  16611. /**
  16612. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  16613. * to represent the minimum value of the Y axis.
  16614. *
  16615. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  16616. * Min and max color
  16617. *
  16618. * @type {Highcharts.ColorType}
  16619. * @default #e6ebf5
  16620. * @since 4.0
  16621. * @product highcharts
  16622. * @apioption yAxis.minColor
  16623. */
  16624. /**
  16625. * Whether to reverse the axis so that the highest number is closest
  16626. * to the origin.
  16627. *
  16628. * @sample {highcharts} highcharts/yaxis/reversed/
  16629. * Reversed Y axis
  16630. * @sample {highstock} stock/xaxis/reversed/
  16631. * Reversed Y axis
  16632. *
  16633. * @type {boolean}
  16634. * @default {highcharts} false
  16635. * @default {highstock} false
  16636. * @default {highmaps} true
  16637. * @default {gantt} true
  16638. * @apioption yAxis.reversed
  16639. */
  16640. /**
  16641. * If `true`, the first series in a stack will be drawn on top in a
  16642. * positive, non-reversed Y axis. If `false`, the first series is in
  16643. * the base of the stack.
  16644. *
  16645. * @sample {highcharts} highcharts/yaxis/reversedstacks-false/
  16646. * Non-reversed stacks
  16647. * @sample {highstock} highcharts/yaxis/reversedstacks-false/
  16648. * Non-reversed stacks
  16649. *
  16650. * @type {boolean}
  16651. * @default true
  16652. * @since 3.0.10
  16653. * @product highcharts highstock
  16654. * @apioption yAxis.reversedStacks
  16655. */
  16656. reversedStacks: true,
  16657. /**
  16658. * Solid gauge series only. Color stops for the solid gauge. Use this
  16659. * in cases where a linear gradient between a `minColor` and `maxColor`
  16660. * is not sufficient. The stops is an array of tuples, where the first
  16661. * item is a float between 0 and 1 assigning the relative position in
  16662. * the gradient, and the second item is the color.
  16663. *
  16664. * For solid gauges, the Y axis also inherits the concept of
  16665. * [data classes](https://api.highcharts.com/highmaps#colorAxis.dataClasses)
  16666. * from the Highmaps color axis.
  16667. *
  16668. * @sample {highcharts} highcharts/demo/gauge-solid/
  16669. * Gauge with stops
  16670. *
  16671. * @see [minColor](#yAxis.minColor)
  16672. * @see [maxColor](#yAxis.maxColor)
  16673. *
  16674. * @type {Array<Array<number,Highcharts.ColorType>>}
  16675. * @since 4.0
  16676. * @product highcharts
  16677. * @apioption yAxis.stops
  16678. */
  16679. /**
  16680. * The pixel width of the major tick marks.
  16681. *
  16682. * @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
  16683. * @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
  16684. *
  16685. * @type {number}
  16686. * @default 0
  16687. * @product highcharts highstock gantt
  16688. * @apioption yAxis.tickWidth
  16689. */
  16690. /**
  16691. * Whether to force the axis to end on a tick. Use this option with
  16692. * the `maxPadding` option to control the axis end.
  16693. *
  16694. * This option is always disabled, when panning type is
  16695. * either `y` or `xy`.
  16696. *
  16697. * @see [type](#chart.panning.type)
  16698. *
  16699. *
  16700. * @sample {highcharts} highcharts/yaxis/endontick/
  16701. * True by default
  16702. * @sample {highcharts} highcharts/yaxis/endontick-false/
  16703. * False
  16704. * @sample {highstock} stock/demo/basic-line/
  16705. * True by default
  16706. * @sample {highstock} stock/xaxis/endontick/
  16707. * False for Y axis
  16708. *
  16709. * @since 1.2.0
  16710. */
  16711. endOnTick: true,
  16712. /**
  16713. * Padding of the max value relative to the length of the axis. A
  16714. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  16715. * when you don't want the highest data value to appear on the edge
  16716. * of the plot area. When the axis' `max` option is set or a max extreme
  16717. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  16718. *
  16719. * Also the `softThreshold` option takes precedence over `maxPadding`,
  16720. * so if the data is tangent to the threshold, `maxPadding` may not
  16721. * apply unless `softThreshold` is set to false.
  16722. *
  16723. * @sample {highcharts} highcharts/yaxis/maxpadding-02/
  16724. * Max padding of 0.2
  16725. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  16726. * Greater min- and maxPadding
  16727. *
  16728. * @since 1.2.0
  16729. * @product highcharts highstock gantt
  16730. */
  16731. maxPadding: 0.05,
  16732. /**
  16733. * Padding of the min value relative to the length of the axis. A
  16734. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  16735. * when you don't want the lowest data value to appear on the edge
  16736. * of the plot area. When the axis' `min` option is set or a max extreme
  16737. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  16738. *
  16739. * Also the `softThreshold` option takes precedence over `minPadding`,
  16740. * so if the data is tangent to the threshold, `minPadding` may not
  16741. * apply unless `softThreshold` is set to false.
  16742. *
  16743. * @sample {highcharts} highcharts/yaxis/minpadding/
  16744. * Min padding of 0.2
  16745. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  16746. * Greater min- and maxPadding
  16747. *
  16748. * @since 1.2.0
  16749. * @product highcharts highstock gantt
  16750. */
  16751. minPadding: 0.05,
  16752. /**
  16753. * @productdesc {highstock}
  16754. * In Highcharts Stock 1.x, the Y axis was placed
  16755. * on the left side by default.
  16756. *
  16757. * @sample {highcharts} highcharts/yaxis/opposite/
  16758. * Secondary Y axis opposite
  16759. * @sample {highstock} stock/xaxis/opposite/
  16760. * Y axis on left side
  16761. *
  16762. * @type {boolean}
  16763. * @default {highstock} true
  16764. * @default {highcharts} false
  16765. * @product highstock highcharts gantt
  16766. * @apioption yAxis.opposite
  16767. */
  16768. /**
  16769. * @see [tickInterval](#xAxis.tickInterval)
  16770. * @see [tickPositioner](#xAxis.tickPositioner)
  16771. * @see [tickPositions](#xAxis.tickPositions)
  16772. */
  16773. tickPixelInterval: 72,
  16774. /**
  16775. * Whether to show the last tick label.
  16776. *
  16777. * @productdesc {highcharts|gantt}
  16778. * Defaults to `true` on cartesian charts, and `false` on polar charts.
  16779. *
  16780. * @productdesc {highstock}
  16781. * Defaults to `true` for categorized yAxis and `false` for other types
  16782. * of yAxis.
  16783. *
  16784. * @default undefined
  16785. */
  16786. showLastLabel: true,
  16787. /**
  16788. * @extends xAxis.labels
  16789. */
  16790. labels: {
  16791. /**
  16792. * The label's pixel distance from the perimeter of the plot area.
  16793. * On cartesian charts, this is overridden if the `labels.y` setting
  16794. * is set.
  16795. *
  16796. * On polar charts, if it's a percentage string, it is interpreted
  16797. * the same as [series.radius](#plotOptions.gauge.radius), so the
  16798. * label can be aligned under the gauge's shape.
  16799. *
  16800. * @sample {highcharts} highcharts/yaxis/labels-distance/
  16801. * Polar chart, labels centered under the arc
  16802. *
  16803. * @type {number|string}
  16804. * @product highcharts
  16805. * @apioption yAxis.labels.distance
  16806. */
  16807. /**
  16808. * The y position offset of all labels relative to the tick
  16809. * positions on the axis. For polar and radial axis consider the use
  16810. * of the [distance](#yAxis.labels.distance) option.
  16811. *
  16812. * @sample {highcharts} highcharts/xaxis/labels-x/
  16813. * Y axis labels placed on grid lines
  16814. *
  16815. * @type {number}
  16816. * @default {highcharts} 3
  16817. * @default {highstock} -2
  16818. * @default {highmaps} 3
  16819. * @apioption yAxis.labels.y
  16820. */
  16821. /**
  16822. * What part of the string the given position is anchored to. Can
  16823. * be one of `"left"`, `"center"` or `"right"`. The exact position
  16824. * also depends on the `labels.x` setting.
  16825. *
  16826. * Angular gauges and solid gauges defaults to `"center"`.
  16827. * Solid gauges with two labels have additional option `"auto"`
  16828. * for automatic horizontal and vertical alignment.
  16829. *
  16830. * @sample {highcharts} highcharts/yaxis/labels-align-left/
  16831. * Left
  16832. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  16833. * Solid gauge labels auto aligned
  16834. *
  16835. * @type {Highcharts.AlignValue}
  16836. * @default {highstock} right
  16837. * @apioption yAxis.labels.align
  16838. */
  16839. /**
  16840. * The x position offset of all labels relative to the tick
  16841. * positions on the axis. Defaults to -15 for left axis, 15 for
  16842. * right axis.
  16843. *
  16844. * @sample {highcharts} highcharts/xaxis/labels-x/
  16845. * Y axis labels placed on grid lines
  16846. *
  16847. * @type {number}
  16848. */
  16849. x: void 0
  16850. },
  16851. /**
  16852. * @sample {highcharts} highcharts/yaxis/max-200/
  16853. * Y axis max of 200
  16854. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  16855. * Y axis max on logarithmic axis
  16856. * @sample {highstock} stock/yaxis/min-max/
  16857. * Fixed min and max on Y axis
  16858. *
  16859. * @apioption yAxis.max
  16860. */
  16861. /**
  16862. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  16863. * -50 with startOnTick to false
  16864. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  16865. * -50 with startOnTick true by default
  16866. * @sample {highstock} stock/yaxis/min-max/
  16867. * Fixed min and max on Y axis
  16868. *
  16869. * @apioption yAxis.min
  16870. */
  16871. /**
  16872. * An optional scrollbar to display on the Y axis in response to
  16873. * limiting the minimum an maximum of the axis values.
  16874. *
  16875. * In styled mode, all the presentational options for the scrollbar
  16876. * are replaced by the classes `.highcharts-scrollbar-thumb`,
  16877. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  16878. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  16879. *
  16880. * @sample {highstock} stock/yaxis/scrollbar/
  16881. * Scrollbar on the Y axis
  16882. *
  16883. * @extends scrollbar
  16884. * @since 4.2.6
  16885. * @product highstock
  16886. * @excluding height
  16887. * @apioption yAxis.scrollbar
  16888. */
  16889. /**
  16890. * Enable the scrollbar on the Y axis.
  16891. *
  16892. * @sample {highstock} stock/yaxis/scrollbar/
  16893. * Enabled on Y axis
  16894. *
  16895. * @type {boolean}
  16896. * @default false
  16897. * @since 4.2.6
  16898. * @product highstock
  16899. * @apioption yAxis.scrollbar.enabled
  16900. */
  16901. /**
  16902. * Pixel margin between the scrollbar and the axis elements.
  16903. *
  16904. * @type {number}
  16905. * @default 10
  16906. * @since 4.2.6
  16907. * @product highstock
  16908. * @apioption yAxis.scrollbar.margin
  16909. */
  16910. /* eslint-disable highcharts/doclet-apioption-last */
  16911. /**
  16912. * Defines the position of the scrollbar. By default, it is positioned
  16913. * on the opposite of the main axis (right side of the chart).
  16914. * However, in the case of RTL languages could be set to `false`
  16915. * which positions the scrollbar on the left.
  16916. *
  16917. * Works only for vertical axes.
  16918. * This means yAxis in a non-inverted chart and xAxis in the inverted.
  16919. *
  16920. * @sample stock/yaxis/scrollbar-opposite/
  16921. * A scrollbar not on the opposite side
  16922. *
  16923. * @type {boolean}
  16924. * @default true
  16925. * @since 9.3.0
  16926. *
  16927. * @apioption yAxis.scrollbar.opposite
  16928. * @apioption xAxis.scrollbar.opposite
  16929. *
  16930. */
  16931. /* eslint-enable highcharts/doclet-apioption-last */
  16932. /**
  16933. * Whether to show the scrollbar when it is fully zoomed out at max
  16934. * range. Setting it to `false` on the Y axis makes the scrollbar stay
  16935. * hidden until the user zooms in, like common in browsers.
  16936. *
  16937. * @type {boolean}
  16938. * @default true
  16939. * @since 4.2.6
  16940. * @product highstock
  16941. * @apioption yAxis.scrollbar.showFull
  16942. */
  16943. /**
  16944. * The width of a vertical scrollbar or height of a horizontal
  16945. * scrollbar. Defaults to 20 on touch devices.
  16946. *
  16947. * @type {number}
  16948. * @default 14
  16949. * @since 4.2.6
  16950. * @product highstock
  16951. * @apioption yAxis.scrollbar.size
  16952. */
  16953. /**
  16954. * Z index of the scrollbar elements.
  16955. *
  16956. * @type {number}
  16957. * @default 3
  16958. * @since 4.2.6
  16959. * @product highstock
  16960. * @apioption yAxis.scrollbar.zIndex
  16961. */
  16962. /**
  16963. * A soft maximum for the axis. If the series data maximum is less
  16964. * than this, the axis will stay at this maximum, but if the series
  16965. * data maximum is higher, the axis will flex to show all data.
  16966. *
  16967. * **Note**: The [series.softThreshold](
  16968. * #plotOptions.series.softThreshold) option takes precedence over this
  16969. * option.
  16970. *
  16971. * @sample highcharts/yaxis/softmin-softmax/
  16972. * Soft min and max
  16973. *
  16974. * @type {number}
  16975. * @since 5.0.1
  16976. * @product highcharts highstock gantt
  16977. * @apioption yAxis.softMax
  16978. */
  16979. /**
  16980. * A soft minimum for the axis. If the series data minimum is greater
  16981. * than this, the axis will stay at this minimum, but if the series
  16982. * data minimum is lower, the axis will flex to show all data.
  16983. *
  16984. * **Note**: The [series.softThreshold](
  16985. * #plotOptions.series.softThreshold) option takes precedence over this
  16986. * option.
  16987. *
  16988. * @sample highcharts/yaxis/softmin-softmax/
  16989. * Soft min and max
  16990. *
  16991. * @type {number}
  16992. * @since 5.0.1
  16993. * @product highcharts highstock gantt
  16994. * @apioption yAxis.softMin
  16995. */
  16996. /**
  16997. * Defines the horizontal alignment of the stack total label. Can be one
  16998. * of `"left"`, `"center"` or `"right"`. The default value is calculated
  16999. * at runtime and depends on orientation and whether the stack is
  17000. * positive or negative.
  17001. *
  17002. * @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
  17003. * Aligned to the left
  17004. * @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
  17005. * Aligned in center
  17006. * @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
  17007. * Aligned to the right
  17008. *
  17009. * @type {Highcharts.AlignValue}
  17010. * @since 2.1.5
  17011. * @product highcharts
  17012. * @apioption yAxis.stackLabels.align
  17013. */
  17014. /**
  17015. * A format string for the data label. Available variables are the same
  17016. * as for `formatter`.
  17017. *
  17018. * @type {string}
  17019. * @default {total}
  17020. * @since 3.0.2
  17021. * @product highcharts highstock
  17022. * @apioption yAxis.stackLabels.format
  17023. */
  17024. /**
  17025. * Rotation of the labels in degrees.
  17026. *
  17027. * @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
  17028. * Labels rotated 45°
  17029. *
  17030. * @type {number}
  17031. * @default 0
  17032. * @since 2.1.5
  17033. * @product highcharts
  17034. * @apioption yAxis.stackLabels.rotation
  17035. */
  17036. /**
  17037. * The text alignment for the label. While `align` determines where the
  17038. * texts anchor point is placed with regards to the stack, `textAlign`
  17039. * determines how the text is aligned against its anchor point. Possible
  17040. * values are `"left"`, `"center"` and `"right"`. The default value is
  17041. * calculated at runtime and depends on orientation and whether the
  17042. * stack is positive or negative.
  17043. *
  17044. * @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
  17045. * Label in center position but text-aligned left
  17046. *
  17047. * @type {Highcharts.AlignValue}
  17048. * @since 2.1.5
  17049. * @product highcharts
  17050. * @apioption yAxis.stackLabels.textAlign
  17051. */
  17052. /**
  17053. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  17054. * to render the labels.
  17055. *
  17056. * @type {boolean}
  17057. * @default false
  17058. * @since 3.0
  17059. * @product highcharts highstock
  17060. * @apioption yAxis.stackLabels.useHTML
  17061. */
  17062. /**
  17063. * Defines the vertical alignment of the stack total label. Can be one
  17064. * of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
  17065. * at runtime and depends on orientation and whether the stack is
  17066. * positive or negative.
  17067. *
  17068. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
  17069. * Vertically aligned top
  17070. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
  17071. * Vertically aligned middle
  17072. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
  17073. * Vertically aligned bottom
  17074. *
  17075. * @type {Highcharts.VerticalAlignValue}
  17076. * @since 2.1.5
  17077. * @product highcharts
  17078. * @apioption yAxis.stackLabels.verticalAlign
  17079. */
  17080. /**
  17081. * The x position offset of the label relative to the left of the
  17082. * stacked bar. The default value is calculated at runtime and depends
  17083. * on orientation and whether the stack is positive or negative.
  17084. *
  17085. * @sample {highcharts} highcharts/yaxis/stacklabels-x/
  17086. * Stack total labels with x offset
  17087. *
  17088. * @type {number}
  17089. * @since 2.1.5
  17090. * @product highcharts
  17091. * @apioption yAxis.stackLabels.x
  17092. */
  17093. /**
  17094. * The y position offset of the label relative to the tick position
  17095. * on the axis. The default value is calculated at runtime and depends
  17096. * on orientation and whether the stack is positive or negative.
  17097. *
  17098. * @sample {highcharts} highcharts/yaxis/stacklabels-y/
  17099. * Stack total labels with y offset
  17100. *
  17101. * @type {number}
  17102. * @since 2.1.5
  17103. * @product highcharts
  17104. * @apioption yAxis.stackLabels.y
  17105. */
  17106. /**
  17107. * Whether to force the axis to start on a tick. Use this option with
  17108. * the `maxPadding` option to control the axis start.
  17109. *
  17110. * This option is always disabled, when panning type is
  17111. * either `y` or `xy`.
  17112. *
  17113. * @see [type](#chart.panning.type)
  17114. *
  17115. * @sample {highcharts} highcharts/xaxis/startontick-false/
  17116. * False by default
  17117. * @sample {highcharts} highcharts/xaxis/startontick-true/
  17118. * True
  17119. * @sample {highstock} stock/xaxis/endontick/
  17120. * False for Y axis
  17121. *
  17122. * @since 1.2.0
  17123. * @product highcharts highstock gantt
  17124. */
  17125. startOnTick: true,
  17126. title: {
  17127. /**
  17128. * The pixel distance between the axis labels and the title.
  17129. * Positive values are outside the axis line, negative are inside.
  17130. *
  17131. * @sample {highcharts} highcharts/xaxis/title-margin/
  17132. * Y axis title margin of 60
  17133. *
  17134. * @type {number}
  17135. * @default 40
  17136. * @apioption yAxis.title.margin
  17137. */
  17138. /**
  17139. * The rotation of the text in degrees. 0 is horizontal, 270 is
  17140. * vertical reading from bottom to top.
  17141. *
  17142. * @sample {highcharts} highcharts/yaxis/title-offset/
  17143. * Horizontal
  17144. */
  17145. rotation: 270,
  17146. /**
  17147. * The actual text of the axis title. Horizontal texts can contain
  17148. * HTML, but rotated texts are painted using vector techniques and
  17149. * must be clean text. The Y axis title is disabled by setting the
  17150. * `text` option to `undefined`.
  17151. *
  17152. * @sample {highcharts} highcharts/xaxis/title-text/
  17153. * Custom HTML
  17154. *
  17155. * @type {string|null}
  17156. * @default {highcharts} Values
  17157. * @default {highstock} undefined
  17158. * @product highcharts highstock gantt
  17159. */
  17160. text: 'Values'
  17161. },
  17162. /**
  17163. * The top position of the Y axis. If it's a number, it is interpreted
  17164. * as pixel position relative to the chart.
  17165. *
  17166. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  17167. * percentages of the plot height, offset from plot area top.
  17168. *
  17169. * @see [yAxis.height](#yAxis.height)
  17170. *
  17171. * @sample {highstock} stock/demo/candlestick-and-volume/
  17172. * Percentage height panes
  17173. *
  17174. * @type {number|string}
  17175. * @product highcharts highstock
  17176. * @apioption yAxis.top
  17177. */
  17178. /**
  17179. * The stack labels show the total value for each bar in a stacked
  17180. * column or bar chart. The label will be placed on top of positive
  17181. * columns and below negative columns. In case of an inverted column
  17182. * chart or a bar chart the label is placed to the right of positive
  17183. * bars and to the left of negative bars.
  17184. *
  17185. * @product highcharts
  17186. */
  17187. stackLabels: {
  17188. /**
  17189. * Enable or disable the initial animation when a series is
  17190. * displayed for the `stackLabels`. The animation can also be set as
  17191. * a configuration object. Please note that this option only
  17192. * applies to the initial animation.
  17193. * For other animations, see [chart.animation](#chart.animation)
  17194. * and the animation parameter under the API methods.
  17195. * The following properties are supported:
  17196. *
  17197. * - `defer`: The animation delay time in milliseconds.
  17198. *
  17199. * @sample {highcharts} highcharts/plotoptions/animation-defer/
  17200. * Animation defer settings
  17201. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  17202. * @since 8.2.0
  17203. * @apioption yAxis.stackLabels.animation
  17204. */
  17205. animation: {},
  17206. /**
  17207. * The animation delay time in milliseconds.
  17208. * Set to `0` renders stackLabel immediately.
  17209. * As `undefined` inherits defer time from the [series.animation.defer](#plotOptions.series.animation.defer).
  17210. *
  17211. * @type {number}
  17212. * @since 8.2.0
  17213. * @apioption yAxis.stackLabels.animation.defer
  17214. */
  17215. /**
  17216. * Allow the stack labels to overlap.
  17217. *
  17218. * @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
  17219. * Default false
  17220. *
  17221. * @since 5.0.13
  17222. * @product highcharts
  17223. */
  17224. allowOverlap: false,
  17225. /**
  17226. * The background color or gradient for the stack label.
  17227. *
  17228. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17229. * Stack labels box options
  17230. * @type {Highcharts.ColorType}
  17231. * @since 8.1.0
  17232. * @apioption yAxis.stackLabels.backgroundColor
  17233. */
  17234. /**
  17235. * The border color for the stack label. Defaults to `undefined`.
  17236. *
  17237. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17238. * Stack labels box options
  17239. * @type {Highcharts.ColorType}
  17240. * @since 8.1.0
  17241. * @apioption yAxis.stackLabels.borderColor
  17242. */
  17243. /**
  17244. * The border radius in pixels for the stack label.
  17245. *
  17246. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17247. * Stack labels box options
  17248. * @type {number}
  17249. * @default 0
  17250. * @since 8.1.0
  17251. * @apioption yAxis.stackLabels.borderRadius
  17252. */
  17253. /**
  17254. * The border width in pixels for the stack label.
  17255. *
  17256. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  17257. * Stack labels box options
  17258. * @type {number}
  17259. * @default 0
  17260. * @since 8.1.0
  17261. * @apioption yAxis.stackLabels.borderWidth
  17262. */
  17263. /**
  17264. * Enable or disable the stack total labels.
  17265. *
  17266. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
  17267. * Enabled stack total labels
  17268. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled-waterfall/
  17269. * Enabled stack labels in waterfall chart
  17270. *
  17271. * @since 2.1.5
  17272. * @product highcharts
  17273. */
  17274. enabled: false,
  17275. /**
  17276. * Whether to hide stack labels that are outside the plot area.
  17277. * By default, the stack label is moved
  17278. * inside the plot area according to the
  17279. * [overflow](/highcharts/#yAxis/stackLabels/overflow)
  17280. * option.
  17281. *
  17282. * @type {boolean}
  17283. * @since 7.1.3
  17284. */
  17285. crop: true,
  17286. /**
  17287. * How to handle stack total labels that flow outside the plot area.
  17288. * The default is set to `"justify"`,
  17289. * which aligns them inside the plot area.
  17290. * For columns and bars, this means it will be moved inside the bar.
  17291. * To display stack labels outside the plot area,
  17292. * set `crop` to `false` and `overflow` to `"allow"`.
  17293. *
  17294. * @sample highcharts/yaxis/stacklabels-overflow/
  17295. * Stack labels flows outside the plot area.
  17296. *
  17297. * @type {Highcharts.DataLabelsOverflowValue}
  17298. * @since 7.1.3
  17299. */
  17300. overflow: 'justify',
  17301. /* eslint-disable valid-jsdoc */
  17302. /**
  17303. * Callback JavaScript function to format the label. The value is
  17304. * given by `this.total`.
  17305. *
  17306. * @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
  17307. * Added units to stack total value
  17308. *
  17309. * @type {Highcharts.FormatterCallbackFunction<Highcharts.StackItemObject>}
  17310. * @since 2.1.5
  17311. * @product highcharts
  17312. */
  17313. formatter: function () {
  17314. const { numberFormatter } = this.axis.chart;
  17315. /* eslint-enable valid-jsdoc */
  17316. return numberFormatter(this.total || 0, -1);
  17317. },
  17318. /**
  17319. * CSS styles for the label.
  17320. *
  17321. * In styled mode, the styles are set in the
  17322. * `.highcharts-stack-label` class.
  17323. *
  17324. * @sample {highcharts} highcharts/yaxis/stacklabels-style/
  17325. * Red stack total labels
  17326. *
  17327. * @type {Highcharts.CSSObject}
  17328. * @since 2.1.5
  17329. * @product highcharts
  17330. */
  17331. style: {
  17332. /** @internal */
  17333. color: "#000000" /* Palette.neutralColor100 */,
  17334. /** @internal */
  17335. fontSize: '0.7em',
  17336. /** @internal */
  17337. fontWeight: 'bold',
  17338. /** @internal */
  17339. textOutline: '1px contrast'
  17340. }
  17341. },
  17342. gridLineWidth: 1,
  17343. lineWidth: 0
  17344. // tickWidth: 0
  17345. };
  17346. /**
  17347. * The Z axis or depth axis for 3D plots.
  17348. *
  17349. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  17350. * access to the axis.
  17351. *
  17352. * @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
  17353. * Z-Axis with Categories
  17354. * @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
  17355. * Z-Axis with styling
  17356. *
  17357. * @type {*|Array<*>}
  17358. * @extends xAxis
  17359. * @since 5.0.0
  17360. * @product highcharts
  17361. * @excluding breaks, crosshair, height, left, lineColor, lineWidth,
  17362. * nameToX, showEmpty, top, width
  17363. * @apioption zAxis
  17364. */
  17365. // This variable extends the defaultOptions for left axes.
  17366. AxisDefaults.defaultLeftAxisOptions = {
  17367. title: {
  17368. rotation: 270
  17369. }
  17370. };
  17371. // This variable extends the defaultOptions for right axes.
  17372. AxisDefaults.defaultRightAxisOptions = {
  17373. title: {
  17374. rotation: 90
  17375. }
  17376. };
  17377. // This variable extends the defaultOptions for bottom axes.
  17378. AxisDefaults.defaultBottomAxisOptions = {
  17379. labels: {
  17380. autoRotation: [-45]
  17381. // overflow: undefined,
  17382. // staggerLines: null
  17383. },
  17384. margin: 15,
  17385. title: {
  17386. rotation: 0
  17387. }
  17388. };
  17389. // This variable extends the defaultOptions for top axes.
  17390. AxisDefaults.defaultTopAxisOptions = {
  17391. labels: {
  17392. autoRotation: [-45]
  17393. // overflow: undefined
  17394. // staggerLines: null
  17395. },
  17396. margin: 15,
  17397. title: {
  17398. rotation: 0
  17399. }
  17400. };
  17401. })(AxisDefaults || (AxisDefaults = {}));
  17402. /* *
  17403. *
  17404. * Default Export
  17405. *
  17406. * */
  17407. return AxisDefaults;
  17408. });
  17409. _registerModule(_modules, 'Core/Foundation.js', [_modules['Core/Utilities.js']], function (U) {
  17410. /* *
  17411. *
  17412. * (c) 2010-2021 Torstein Honsi
  17413. *
  17414. * License: www.highcharts.com/license
  17415. *
  17416. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  17417. *
  17418. * */
  17419. const { addEvent, isFunction, objectEach, removeEvent } = U;
  17420. /* *
  17421. *
  17422. * Class Namespace
  17423. *
  17424. * */
  17425. var Foundation;
  17426. (function (Foundation) {
  17427. /* *
  17428. *
  17429. * Functions
  17430. *
  17431. * */
  17432. /* eslint-disable valid-jsdoc */
  17433. /**
  17434. * Register event options. If an event handler is set on the options, it
  17435. * should be subject to Chart.update, Axis.update and Series.update. This is
  17436. * contrary to general handlers that are set directly using addEvent either
  17437. * on the class or on the instance. #6538, #6943, #10861.
  17438. * @private
  17439. */
  17440. function registerEventOptions(component, options) {
  17441. // A lookup over those events that are added by _options_ (not
  17442. // programmatically). These are updated through .update()
  17443. component.eventOptions = component.eventOptions || {};
  17444. // Register event listeners
  17445. objectEach(options.events, function (event, eventType) {
  17446. // If event does not exist, or is changed by the .update()
  17447. // function
  17448. if (component.eventOptions[eventType] !== event) {
  17449. // Remove existing if set by option
  17450. if (component.eventOptions[eventType]) {
  17451. removeEvent(component, eventType, component.eventOptions[eventType]);
  17452. delete component.eventOptions[eventType];
  17453. }
  17454. if (isFunction(event)) {
  17455. component.eventOptions[eventType] = event;
  17456. addEvent(component, eventType, event, {
  17457. order: 0 // #14080 fire those events as firsts
  17458. });
  17459. }
  17460. }
  17461. });
  17462. }
  17463. Foundation.registerEventOptions = registerEventOptions;
  17464. })(Foundation || (Foundation = {}));
  17465. /* *
  17466. *
  17467. * Default Export
  17468. *
  17469. * */
  17470. return Foundation;
  17471. });
  17472. _registerModule(_modules, 'Core/Axis/Tick.js', [_modules['Core/Templating.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (F, H, U) {
  17473. /* *
  17474. *
  17475. * (c) 2010-2021 Torstein Honsi
  17476. *
  17477. * License: www.highcharts.com/license
  17478. *
  17479. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  17480. *
  17481. * */
  17482. const { deg2rad } = H;
  17483. const { clamp, correctFloat, defined, destroyObjectProperties, extend, fireEvent, isNumber, merge, objectEach, pick } = U;
  17484. /* *
  17485. *
  17486. * Class
  17487. *
  17488. * */
  17489. /* eslint-disable no-invalid-this, valid-jsdoc */
  17490. /**
  17491. * The Tick class.
  17492. *
  17493. * @class
  17494. * @name Highcharts.Tick
  17495. *
  17496. * @param {Highcharts.Axis} axis
  17497. * The axis of the tick.
  17498. *
  17499. * @param {number} pos
  17500. * The position of the tick on the axis in terms of axis values.
  17501. *
  17502. * @param {string} [type]
  17503. * The type of tick, either 'minor' or an empty string
  17504. *
  17505. * @param {boolean} [noLabel=false]
  17506. * Whether to disable the label or not. Defaults to false.
  17507. *
  17508. * @param {Object} [parameters]
  17509. * Optional parameters for the tick.
  17510. */
  17511. class Tick {
  17512. /* *
  17513. *
  17514. * Constructors
  17515. *
  17516. * */
  17517. constructor(axis, pos, type, noLabel, parameters) {
  17518. this.isNew = true;
  17519. this.isNewLabel = true;
  17520. /**
  17521. * The related axis of the tick.
  17522. * @name Highcharts.Tick#axis
  17523. * @type {Highcharts.Axis}
  17524. */
  17525. this.axis = axis;
  17526. /**
  17527. * The logical position of the tick on the axis in terms of axis values.
  17528. * @name Highcharts.Tick#pos
  17529. * @type {number}
  17530. */
  17531. this.pos = pos;
  17532. /**
  17533. * The tick type, which can be `"minor"`, or an empty string.
  17534. * @name Highcharts.Tick#type
  17535. * @type {string}
  17536. */
  17537. this.type = type || '';
  17538. this.parameters = parameters || {};
  17539. /**
  17540. * The mark offset of the tick on the axis. Usually `undefined`, numeric
  17541. * for grid axes.
  17542. * @name Highcharts.Tick#tickmarkOffset
  17543. * @type {number|undefined}
  17544. */
  17545. this.tickmarkOffset = this.parameters.tickmarkOffset;
  17546. this.options = this.parameters.options;
  17547. fireEvent(this, 'init');
  17548. if (!type && !noLabel) {
  17549. this.addLabel();
  17550. }
  17551. }
  17552. /* *
  17553. *
  17554. * Functions
  17555. *
  17556. * */
  17557. /**
  17558. * Write the tick label.
  17559. *
  17560. * @private
  17561. * @function Highcharts.Tick#addLabel
  17562. */
  17563. addLabel() {
  17564. const tick = this, axis = tick.axis, options = axis.options, chart = axis.chart, categories = axis.categories, log = axis.logarithmic, names = axis.names, pos = tick.pos, labelOptions = pick(tick.options && tick.options.labels, options.labels), tickPositions = axis.tickPositions, isFirst = pos === tickPositions[0], isLast = pos === tickPositions[tickPositions.length - 1], animateLabels = (!labelOptions.step || labelOptions.step === 1) &&
  17565. axis.tickInterval === 1, tickPositionInfo = tickPositions.info;
  17566. let label = tick.label, dateTimeLabelFormat, dateTimeLabelFormats, i;
  17567. // The context value
  17568. let value = this.parameters.category || (categories ?
  17569. pick(categories[pos], names[pos], pos) :
  17570. pos);
  17571. if (log && isNumber(value)) {
  17572. value = correctFloat(log.lin2log(value));
  17573. }
  17574. // Set the datetime label format. If a higher rank is set for this
  17575. // position, use that. If not, use the general format.
  17576. if (axis.dateTime) {
  17577. if (tickPositionInfo) {
  17578. dateTimeLabelFormats = chart.time.resolveDTLFormat(options.dateTimeLabelFormats[(!options.grid &&
  17579. tickPositionInfo.higherRanks[pos]) ||
  17580. tickPositionInfo.unitName]);
  17581. dateTimeLabelFormat = dateTimeLabelFormats.main;
  17582. }
  17583. else if (isNumber(value)) { // #1441
  17584. dateTimeLabelFormat = axis.dateTime.getXDateFormat(value, options.dateTimeLabelFormats ||
  17585. {});
  17586. }
  17587. }
  17588. // set properties for access in render method
  17589. /**
  17590. * True if the tick is the first one on the axis.
  17591. * @name Highcharts.Tick#isFirst
  17592. * @readonly
  17593. * @type {boolean|undefined}
  17594. */
  17595. tick.isFirst = isFirst;
  17596. /**
  17597. * True if the tick is the last one on the axis.
  17598. * @name Highcharts.Tick#isLast
  17599. * @readonly
  17600. * @type {boolean|undefined}
  17601. */
  17602. tick.isLast = isLast;
  17603. // Get the string
  17604. const ctx = {
  17605. axis,
  17606. chart,
  17607. dateTimeLabelFormat: dateTimeLabelFormat,
  17608. isFirst,
  17609. isLast,
  17610. pos,
  17611. tick: tick,
  17612. tickPositionInfo,
  17613. value
  17614. };
  17615. // Fire an event that allows modifying the context for use in
  17616. // `labels.format` and `labels.formatter`.
  17617. fireEvent(this, 'labelFormat', ctx);
  17618. // Label formatting. When `labels.format` is given, we first run the
  17619. // defaultFormatter and append the result to the context as `text`.
  17620. // Handy for adding prefix or suffix while keeping default number
  17621. // formatting.
  17622. const labelFormatter = (ctx) => {
  17623. if (labelOptions.formatter) {
  17624. return labelOptions.formatter.call(ctx, ctx);
  17625. }
  17626. if (labelOptions.format) {
  17627. ctx.text = axis.defaultLabelFormatter.call(ctx, ctx);
  17628. return F.format(labelOptions.format, ctx, chart);
  17629. }
  17630. return axis.defaultLabelFormatter.call(ctx, ctx);
  17631. };
  17632. const str = labelFormatter.call(ctx, ctx);
  17633. // Set up conditional formatting based on the format list if existing.
  17634. const list = dateTimeLabelFormats && dateTimeLabelFormats.list;
  17635. if (list) {
  17636. tick.shortenLabel = function () {
  17637. for (i = 0; i < list.length; i++) {
  17638. extend(ctx, { dateTimeLabelFormat: list[i] });
  17639. label.attr({
  17640. text: labelFormatter.call(ctx, ctx)
  17641. });
  17642. if (label.getBBox().width <
  17643. axis.getSlotWidth(tick) - 2 *
  17644. labelOptions.padding) {
  17645. return;
  17646. }
  17647. }
  17648. label.attr({
  17649. text: ''
  17650. });
  17651. };
  17652. }
  17653. else {
  17654. // #15692
  17655. tick.shortenLabel = void 0;
  17656. }
  17657. // Call only after first render
  17658. if (animateLabels && axis._addedPlotLB) {
  17659. tick.moveLabel(str, labelOptions);
  17660. }
  17661. // First call
  17662. if (!defined(label) && !tick.movedLabel) {
  17663. /**
  17664. * The rendered text label of the tick.
  17665. * @name Highcharts.Tick#label
  17666. * @type {Highcharts.SVGElement|undefined}
  17667. */
  17668. tick.label = label = tick.createLabel({ x: 0, y: 0 }, str, labelOptions);
  17669. // Base value to detect change for new calls to getBBox
  17670. tick.rotation = 0;
  17671. // update
  17672. }
  17673. else if (label && label.textStr !== str && !animateLabels) {
  17674. // When resetting text, also reset the width if dynamically set
  17675. // (#8809)
  17676. if (label.textWidth &&
  17677. !labelOptions.style.width &&
  17678. !label.styles.width) {
  17679. label.css({ width: null });
  17680. }
  17681. label.attr({ text: str });
  17682. label.textPxLength = label.getBBox().width;
  17683. }
  17684. }
  17685. /**
  17686. * Render and return the label of the tick.
  17687. *
  17688. * @private
  17689. * @function Highcharts.Tick#createLabel
  17690. */
  17691. createLabel(xy, str, labelOptions) {
  17692. const axis = this.axis, chart = axis.chart, label = defined(str) && labelOptions.enabled ?
  17693. chart.renderer
  17694. .text(str, xy.x, xy.y, labelOptions.useHTML)
  17695. .add(axis.labelGroup) :
  17696. null;
  17697. // Un-rotated length
  17698. if (label) {
  17699. // Without position absolute, IE export sometimes is wrong
  17700. if (!chart.styledMode) {
  17701. label.css(merge(labelOptions.style));
  17702. }
  17703. label.textPxLength = label.getBBox().width;
  17704. }
  17705. return label;
  17706. }
  17707. /**
  17708. * Destructor for the tick prototype
  17709. *
  17710. * @private
  17711. * @function Highcharts.Tick#destroy
  17712. */
  17713. destroy() {
  17714. destroyObjectProperties(this, this.axis);
  17715. }
  17716. /**
  17717. * Gets the x and y positions for ticks in terms of pixels.
  17718. *
  17719. * @private
  17720. * @function Highcharts.Tick#getPosition
  17721. *
  17722. * @param {boolean} horiz
  17723. * Whether the tick is on an horizontal axis or not.
  17724. *
  17725. * @param {number} tickPos
  17726. * Position of the tick.
  17727. *
  17728. * @param {number} tickmarkOffset
  17729. * Tickmark offset for all ticks.
  17730. *
  17731. * @param {boolean} [old]
  17732. * Whether the axis has changed or not.
  17733. *
  17734. * @return {Highcharts.PositionObject}
  17735. * The tick position.
  17736. *
  17737. * @emits Highcharts.Tick#event:afterGetPosition
  17738. */
  17739. getPosition(horiz, tickPos, tickmarkOffset, old) {
  17740. const axis = this.axis, chart = axis.chart, cHeight = (old && chart.oldChartHeight) || chart.chartHeight, pos = {
  17741. x: horiz ?
  17742. correctFloat(axis.translate(tickPos + tickmarkOffset, void 0, void 0, old) +
  17743. axis.transB) :
  17744. (axis.left +
  17745. axis.offset +
  17746. (axis.opposite ?
  17747. (((old && chart.oldChartWidth) ||
  17748. chart.chartWidth) -
  17749. axis.right -
  17750. axis.left) :
  17751. 0)),
  17752. y: horiz ?
  17753. (cHeight -
  17754. axis.bottom +
  17755. axis.offset -
  17756. (axis.opposite ? axis.height : 0)) :
  17757. correctFloat(cHeight -
  17758. axis.translate(tickPos + tickmarkOffset, void 0, void 0, old) -
  17759. axis.transB)
  17760. };
  17761. // Chrome workaround for #10516
  17762. pos.y = clamp(pos.y, -1e5, 1e5);
  17763. fireEvent(this, 'afterGetPosition', { pos: pos });
  17764. return pos;
  17765. }
  17766. /**
  17767. * Get the x, y position of the tick label
  17768. * @private
  17769. */
  17770. getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  17771. const axis = this.axis, transA = axis.transA, reversed = ( // #7911
  17772. axis.isLinked && axis.linkedParent ?
  17773. axis.linkedParent.reversed :
  17774. axis.reversed), staggerLines = axis.staggerLines, rotCorr = axis.tickRotCorr || { x: 0, y: 0 },
  17775. // Adjust for label alignment if we use reserveSpace: true (#5286)
  17776. labelOffsetCorrection = (!horiz && !axis.reserveSpaceDefault ?
  17777. -axis.labelOffset * (axis.labelAlign === 'center' ? 0.5 : 1) :
  17778. 0), distance = labelOptions.distance, pos = {};
  17779. let yOffset, line;
  17780. if (axis.side === 0) {
  17781. yOffset = label.rotation ? -distance : -label.getBBox().height;
  17782. }
  17783. else if (axis.side === 2) {
  17784. yOffset = rotCorr.y + distance;
  17785. }
  17786. else {
  17787. // #3140, #3140
  17788. yOffset = Math.cos(label.rotation * deg2rad) *
  17789. (rotCorr.y - label.getBBox(false, 0).height / 2);
  17790. }
  17791. if (defined(labelOptions.y)) {
  17792. yOffset = axis.side === 0 && axis.horiz ?
  17793. labelOptions.y + yOffset :
  17794. labelOptions.y;
  17795. }
  17796. x = x +
  17797. pick(labelOptions.x, [0, 1, 0, -1][axis.side] * distance) +
  17798. labelOffsetCorrection +
  17799. rotCorr.x -
  17800. (tickmarkOffset && horiz ?
  17801. tickmarkOffset * transA * (reversed ? -1 : 1) :
  17802. 0);
  17803. y = y + yOffset - (tickmarkOffset && !horiz ?
  17804. tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
  17805. // Correct for staggered labels
  17806. if (staggerLines) {
  17807. line = (index / (step || 1) % staggerLines);
  17808. if (axis.opposite) {
  17809. line = staggerLines - line - 1;
  17810. }
  17811. y += line * (axis.labelOffset / staggerLines);
  17812. }
  17813. pos.x = x;
  17814. pos.y = Math.round(y);
  17815. fireEvent(this, 'afterGetLabelPosition', { pos: pos, tickmarkOffset: tickmarkOffset, index: index });
  17816. return pos;
  17817. }
  17818. /**
  17819. * Get the offset height or width of the label
  17820. *
  17821. * @private
  17822. * @function Highcharts.Tick#getLabelSize
  17823. */
  17824. getLabelSize() {
  17825. return this.label ?
  17826. this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
  17827. 0;
  17828. }
  17829. /**
  17830. * Extendible method to return the path of the marker
  17831. * @private
  17832. */
  17833. getMarkPath(x, y, tickLength, tickWidth, horiz, renderer) {
  17834. return renderer.crispLine([[
  17835. 'M',
  17836. x,
  17837. y
  17838. ], [
  17839. 'L',
  17840. x + (horiz ? 0 : -tickLength),
  17841. y + (horiz ? tickLength : 0)
  17842. ]], tickWidth);
  17843. }
  17844. /**
  17845. * Handle the label overflow by adjusting the labels to the left and right
  17846. * edge, or hide them if they collide into the neighbour label.
  17847. *
  17848. * @private
  17849. * @function Highcharts.Tick#handleOverflow
  17850. */
  17851. handleOverflow(xy) {
  17852. const tick = this, axis = this.axis, labelOptions = axis.options.labels, pxPos = xy.x, chartWidth = axis.chart.chartWidth, spacing = axis.chart.spacing, leftBound = pick(axis.labelLeft, Math.min(axis.pos, spacing[3])), rightBound = pick(axis.labelRight, Math.max(!axis.isRadial ? axis.pos + axis.len : 0, chartWidth - spacing[1])), label = this.label, rotation = this.rotation, factor = {
  17853. left: 0,
  17854. center: 0.5,
  17855. right: 1
  17856. }[axis.labelAlign || label.attr('align')], labelWidth = label.getBBox().width, slotWidth = axis.getSlotWidth(tick), xCorrection = factor, css = {};
  17857. let modifiedSlotWidth = slotWidth, goRight = 1, leftPos, rightPos, textWidth;
  17858. // Check if the label overshoots the chart spacing box. If it does, move
  17859. // it. If it now overshoots the slotWidth, add ellipsis.
  17860. if (!rotation && labelOptions.overflow === 'justify') {
  17861. leftPos = pxPos - factor * labelWidth;
  17862. rightPos = pxPos + (1 - factor) * labelWidth;
  17863. if (leftPos < leftBound) {
  17864. modifiedSlotWidth =
  17865. xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
  17866. }
  17867. else if (rightPos > rightBound) {
  17868. modifiedSlotWidth =
  17869. rightBound - xy.x + modifiedSlotWidth * factor;
  17870. goRight = -1;
  17871. }
  17872. modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
  17873. if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
  17874. xy.x += (goRight *
  17875. (slotWidth -
  17876. modifiedSlotWidth -
  17877. xCorrection * (slotWidth - Math.min(labelWidth, modifiedSlotWidth))));
  17878. }
  17879. // If the label width exceeds the available space, set a text width
  17880. // to be picked up below. Also, if a width has been set before, we
  17881. // need to set a new one because the reported labelWidth will be
  17882. // limited by the box (#3938).
  17883. if (labelWidth > modifiedSlotWidth ||
  17884. (axis.autoRotation && (label.styles || {}).width)) {
  17885. textWidth = modifiedSlotWidth;
  17886. }
  17887. // Add ellipsis to prevent rotated labels to be clipped against the edge
  17888. // of the chart
  17889. }
  17890. else if (rotation < 0 &&
  17891. pxPos - factor * labelWidth < leftBound) {
  17892. textWidth = Math.round(pxPos / Math.cos(rotation * deg2rad) - leftBound);
  17893. }
  17894. else if (rotation > 0 &&
  17895. pxPos + factor * labelWidth > rightBound) {
  17896. textWidth = Math.round((chartWidth - pxPos) /
  17897. Math.cos(rotation * deg2rad));
  17898. }
  17899. if (textWidth) {
  17900. if (tick.shortenLabel) {
  17901. tick.shortenLabel();
  17902. }
  17903. else {
  17904. css.width = Math.floor(textWidth) + 'px';
  17905. if (!(labelOptions.style || {}).textOverflow) {
  17906. css.textOverflow = 'ellipsis';
  17907. }
  17908. label.css(css);
  17909. }
  17910. }
  17911. }
  17912. /**
  17913. * Try to replace the label if the same one already exists.
  17914. *
  17915. * @private
  17916. * @function Highcharts.Tick#moveLabel
  17917. */
  17918. moveLabel(str, labelOptions) {
  17919. const tick = this, label = tick.label, axis = tick.axis;
  17920. let moved = false, labelPos;
  17921. if (label && label.textStr === str) {
  17922. tick.movedLabel = label;
  17923. moved = true;
  17924. delete tick.label;
  17925. }
  17926. else { // Find a label with the same string
  17927. objectEach(axis.ticks, function (currentTick) {
  17928. if (!moved &&
  17929. !currentTick.isNew &&
  17930. currentTick !== tick &&
  17931. currentTick.label &&
  17932. currentTick.label.textStr === str) {
  17933. tick.movedLabel = currentTick.label;
  17934. moved = true;
  17935. currentTick.labelPos = tick.movedLabel.xy;
  17936. delete currentTick.label;
  17937. }
  17938. });
  17939. }
  17940. // Create new label if the actual one is moved
  17941. if (!moved && (tick.labelPos || label)) {
  17942. labelPos = tick.labelPos || label.xy;
  17943. tick.movedLabel = tick.createLabel(labelPos, str, labelOptions);
  17944. if (tick.movedLabel) {
  17945. tick.movedLabel.attr({ opacity: 0 });
  17946. }
  17947. }
  17948. }
  17949. /**
  17950. * Put everything in place
  17951. *
  17952. * @private
  17953. * @param {number} index
  17954. *
  17955. * @param {boolean} [old]
  17956. * Use old coordinates to prepare an animation into new position
  17957. *
  17958. * @param {number} [opacity]
  17959. */
  17960. render(index, old, opacity) {
  17961. const tick = this, axis = tick.axis, horiz = axis.horiz, pos = tick.pos, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), xy = tick.getPosition(horiz, pos, tickmarkOffset, old), x = xy.x, y = xy.y, reverseCrisp = ((horiz && x === axis.pos + axis.len) ||
  17962. (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
  17963. const labelOpacity = pick(opacity, tick.label && tick.label.newOpacity, // #15528
  17964. 1);
  17965. opacity = pick(opacity, 1);
  17966. this.isActive = true;
  17967. // Create the grid line
  17968. this.renderGridLine(old, opacity, reverseCrisp);
  17969. // create the tick mark
  17970. this.renderMark(xy, opacity, reverseCrisp);
  17971. // the label is created on init - now move it into place
  17972. this.renderLabel(xy, old, labelOpacity, index);
  17973. tick.isNew = false;
  17974. fireEvent(this, 'afterRender');
  17975. }
  17976. /**
  17977. * Renders the gridLine.
  17978. *
  17979. * @private
  17980. * @function Highcharts.Tick#renderGridLine
  17981. * @param {boolean} old Whether or not the tick is old
  17982. * @param {number} opacity The opacity of the grid line
  17983. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  17984. */
  17985. renderGridLine(old, opacity, reverseCrisp) {
  17986. const tick = this, axis = tick.axis, options = axis.options, attribs = {}, pos = tick.pos, type = tick.type, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), renderer = axis.chart.renderer;
  17987. let gridLine = tick.gridLine, gridLinePath, gridLineWidth = options.gridLineWidth, gridLineColor = options.gridLineColor, dashStyle = options.gridLineDashStyle;
  17988. if (tick.type === 'minor') {
  17989. gridLineWidth = options.minorGridLineWidth;
  17990. gridLineColor = options.minorGridLineColor;
  17991. dashStyle = options.minorGridLineDashStyle;
  17992. }
  17993. if (!gridLine) {
  17994. if (!axis.chart.styledMode) {
  17995. attribs.stroke = gridLineColor;
  17996. attribs['stroke-width'] = gridLineWidth || 0;
  17997. attribs.dashstyle = dashStyle;
  17998. }
  17999. if (!type) {
  18000. attribs.zIndex = 1;
  18001. }
  18002. if (old) {
  18003. opacity = 0;
  18004. }
  18005. /**
  18006. * The rendered grid line of the tick.
  18007. * @name Highcharts.Tick#gridLine
  18008. * @type {Highcharts.SVGElement|undefined}
  18009. */
  18010. tick.gridLine = gridLine = renderer.path()
  18011. .attr(attribs)
  18012. .addClass('highcharts-' + (type ? type + '-' : '') + 'grid-line')
  18013. .add(axis.gridGroup);
  18014. }
  18015. if (gridLine) {
  18016. gridLinePath = axis.getPlotLinePath({
  18017. value: pos + tickmarkOffset,
  18018. lineWidth: gridLine.strokeWidth() * reverseCrisp,
  18019. force: 'pass',
  18020. old: old,
  18021. acrossPanes: false // #18025
  18022. });
  18023. // If the parameter 'old' is set, the current call will be followed
  18024. // by another call, therefore do not do any animations this time
  18025. if (gridLinePath) {
  18026. gridLine[old || tick.isNew ? 'attr' : 'animate']({
  18027. d: gridLinePath,
  18028. opacity: opacity
  18029. });
  18030. }
  18031. }
  18032. }
  18033. /**
  18034. * Renders the tick mark.
  18035. *
  18036. * @private
  18037. * @function Highcharts.Tick#renderMark
  18038. * @param {Highcharts.PositionObject} xy The position vector of the mark
  18039. * @param {number} opacity The opacity of the mark
  18040. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  18041. */
  18042. renderMark(xy, opacity, reverseCrisp) {
  18043. const tick = this, axis = tick.axis, options = axis.options, renderer = axis.chart.renderer, type = tick.type, tickSize = axis.tickSize(type ? type + 'Tick' : 'tick'), x = xy.x, y = xy.y, tickWidth = pick(options[type !== 'minor' ? 'tickWidth' : 'minorTickWidth'], !type && axis.isXAxis ? 1 : 0), // X axis defaults to 1
  18044. tickColor = options[type !== 'minor' ? 'tickColor' : 'minorTickColor'];
  18045. let mark = tick.mark;
  18046. const isNewMark = !mark;
  18047. if (tickSize) {
  18048. // negate the length
  18049. if (axis.opposite) {
  18050. tickSize[0] = -tickSize[0];
  18051. }
  18052. // First time, create it
  18053. if (!mark) {
  18054. /**
  18055. * The rendered mark of the tick.
  18056. * @name Highcharts.Tick#mark
  18057. * @type {Highcharts.SVGElement|undefined}
  18058. */
  18059. tick.mark = mark = renderer.path()
  18060. .addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
  18061. .add(axis.axisGroup);
  18062. if (!axis.chart.styledMode) {
  18063. mark.attr({
  18064. stroke: tickColor,
  18065. 'stroke-width': tickWidth
  18066. });
  18067. }
  18068. }
  18069. mark[isNewMark ? 'attr' : 'animate']({
  18070. d: tick.getMarkPath(x, y, tickSize[0], mark.strokeWidth() * reverseCrisp, axis.horiz, renderer),
  18071. opacity: opacity
  18072. });
  18073. }
  18074. }
  18075. /**
  18076. * Renders the tick label.
  18077. * Note: The label should already be created in init(), so it should only
  18078. * have to be moved into place.
  18079. *
  18080. * @private
  18081. * @function Highcharts.Tick#renderLabel
  18082. * @param {Highcharts.PositionObject} xy The position vector of the label
  18083. * @param {boolean} old Whether or not the tick is old
  18084. * @param {number} opacity The opacity of the label
  18085. * @param {number} index The index of the tick
  18086. */
  18087. renderLabel(xy, old, opacity, index) {
  18088. const tick = this, axis = tick.axis, horiz = axis.horiz, options = axis.options, label = tick.label, labelOptions = options.labels, step = labelOptions.step, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), x = xy.x, y = xy.y;
  18089. let show = true;
  18090. if (label && isNumber(x)) {
  18091. label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
  18092. // Apply show first and show last. If the tick is both first and
  18093. // last, it is a single centered tick, in which case we show the
  18094. // label anyway (#2100).
  18095. if ((tick.isFirst &&
  18096. !tick.isLast &&
  18097. !options.showFirstLabel) ||
  18098. (tick.isLast &&
  18099. !tick.isFirst &&
  18100. !options.showLastLabel)) {
  18101. show = false;
  18102. // Handle label overflow and show or hide accordingly
  18103. }
  18104. else if (horiz &&
  18105. !labelOptions.step &&
  18106. !labelOptions.rotation &&
  18107. !old &&
  18108. opacity !== 0) {
  18109. tick.handleOverflow(xy);
  18110. }
  18111. // apply step
  18112. if (step && index % step) {
  18113. // show those indices dividable by step
  18114. show = false;
  18115. }
  18116. // Set the new position, and show or hide
  18117. if (show && isNumber(xy.y)) {
  18118. xy.opacity = opacity;
  18119. label[tick.isNewLabel ? 'attr' : 'animate'](xy).show(true);
  18120. tick.isNewLabel = false;
  18121. }
  18122. else {
  18123. label.hide(); // #1338, #15863
  18124. tick.isNewLabel = true;
  18125. }
  18126. }
  18127. }
  18128. /**
  18129. * Replace labels with the moved ones to perform animation. Additionally
  18130. * destroy unused labels.
  18131. *
  18132. * @private
  18133. * @function Highcharts.Tick#replaceMovedLabel
  18134. */
  18135. replaceMovedLabel() {
  18136. const tick = this, label = tick.label, axis = tick.axis;
  18137. // Animate and destroy
  18138. if (label && !tick.isNew) {
  18139. label.animate({ opacity: 0 }, void 0, label.destroy);
  18140. delete tick.label;
  18141. }
  18142. axis.isDirty = true;
  18143. tick.label = tick.movedLabel;
  18144. delete tick.movedLabel;
  18145. }
  18146. }
  18147. /* *
  18148. *
  18149. * Default Export
  18150. *
  18151. * */
  18152. /* *
  18153. *
  18154. * API Declarations
  18155. *
  18156. * */
  18157. /**
  18158. * Optional parameters for the tick.
  18159. * @private
  18160. * @interface Highcharts.TickParametersObject
  18161. */ /**
  18162. * Set category for the tick.
  18163. * @name Highcharts.TickParametersObject#category
  18164. * @type {string|undefined}
  18165. */ /**
  18166. * @name Highcharts.TickParametersObject#options
  18167. * @type {Highcharts.Dictionary<any>|undefined}
  18168. */ /**
  18169. * Set tickmarkOffset for the tick.
  18170. * @name Highcharts.TickParametersObject#tickmarkOffset
  18171. * @type {number|undefined}
  18172. */
  18173. /**
  18174. * Additonal time tick information.
  18175. *
  18176. * @interface Highcharts.TimeTicksInfoObject
  18177. * @extends Highcharts.TimeNormalizedObject
  18178. */ /**
  18179. * @name Highcharts.TimeTicksInfoObject#higherRanks
  18180. * @type {Array<string>}
  18181. */ /**
  18182. * @name Highcharts.TimeTicksInfoObject#totalRange
  18183. * @type {number}
  18184. */
  18185. (''); // keeps doclets above in JS file
  18186. return Tick;
  18187. });
  18188. _registerModule(_modules, 'Core/Axis/Axis.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/AxisDefaults.js'], _modules['Core/Color/Color.js'], _modules['Core/Defaults.js'], _modules['Core/Foundation.js'], _modules['Core/Globals.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Utilities.js']], function (A, AxisDefaults, Color, D, F, H, Tick, U) {
  18189. /* *
  18190. *
  18191. * (c) 2010-2021 Torstein Honsi
  18192. *
  18193. * License: www.highcharts.com/license
  18194. *
  18195. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  18196. *
  18197. * */
  18198. const { animObject } = A;
  18199. const { defaultOptions } = D;
  18200. const { registerEventOptions } = F;
  18201. const { deg2rad } = H;
  18202. const { arrayMax, arrayMin, clamp, correctFloat, defined, destroyObjectProperties, erase, error, extend, fireEvent, getClosestDistance, insertItem, isArray, isNumber, isString, merge, normalizeTickInterval, objectEach, pick, relativeLength, removeEvent, splat, syncTimeout } = U;
  18203. const getNormalizedTickInterval = (axis, tickInterval) => normalizeTickInterval(tickInterval, void 0, void 0, pick(axis.options.allowDecimals,
  18204. // If the tick interval is greather than 0.5, avoid decimals, as
  18205. // linear axes are often used to render discrete values (#3363). If
  18206. // a tick amount is set, allow decimals by default, as it increases
  18207. // the chances for a good fit.
  18208. tickInterval < 0.5 || axis.tickAmount !== void 0), !!axis.tickAmount);
  18209. /* *
  18210. *
  18211. * Class
  18212. *
  18213. * */
  18214. /**
  18215. * Create a new axis object. Called internally when instanciating a new chart or
  18216. * adding axes by {@link Highcharts.Chart#addAxis}.
  18217. *
  18218. * A chart can have from 0 axes (pie chart) to multiples. In a normal, single
  18219. * series cartesian chart, there is one X axis and one Y axis.
  18220. *
  18221. * The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
  18222. * an array of Axis objects. If there is only one axis, it can be referenced
  18223. * through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
  18224. * pattern goes for Y axes.
  18225. *
  18226. * If you need to get the axes from a series object, use the `series.xAxis` and
  18227. * `series.yAxis` properties. These are not arrays, as one series can only be
  18228. * associated to one X and one Y axis.
  18229. *
  18230. * A third way to reference the axis programmatically is by `id`. Add an `id` in
  18231. * the axis configuration options, and get the axis by
  18232. * {@link Highcharts.Chart#get}.
  18233. *
  18234. * Configuration options for the axes are given in options.xAxis and
  18235. * options.yAxis.
  18236. *
  18237. * @class
  18238. * @name Highcharts.Axis
  18239. *
  18240. * @param {Highcharts.Chart} chart
  18241. * The Chart instance to apply the axis on.
  18242. *
  18243. * @param {Highcharts.AxisOptions} userOptions
  18244. * Axis options
  18245. */
  18246. class Axis {
  18247. /* *
  18248. *
  18249. * Constructors
  18250. *
  18251. * */
  18252. constructor(chart, userOptions, coll) {
  18253. this.alternateBands = void 0;
  18254. this.bottom = void 0;
  18255. this.chart = void 0;
  18256. this.closestPointRange = void 0;
  18257. this.coll = void 0;
  18258. this.eventOptions = void 0;
  18259. this.hasNames = void 0;
  18260. this.hasVisibleSeries = void 0;
  18261. this.height = void 0;
  18262. this.index = void 0;
  18263. this.isLinked = void 0;
  18264. this.labelEdge = void 0; // @todo
  18265. this.labelFormatter = void 0;
  18266. this.left = void 0;
  18267. this.len = void 0;
  18268. this.max = void 0;
  18269. this.maxLabelLength = void 0;
  18270. this.min = void 0;
  18271. this.minorTickInterval = void 0;
  18272. this.minorTicks = void 0;
  18273. this.minPixelPadding = void 0;
  18274. this.names = void 0;
  18275. this.offset = void 0;
  18276. this.options = void 0;
  18277. this.overlap = void 0;
  18278. this.paddedTicks = void 0;
  18279. this.plotLinesAndBands = void 0;
  18280. this.plotLinesAndBandsGroups = void 0;
  18281. this.pointRange = void 0;
  18282. this.pointRangePadding = void 0;
  18283. this.pos = void 0;
  18284. this.positiveValuesOnly = void 0;
  18285. this.right = void 0;
  18286. this.series = void 0;
  18287. this.side = void 0;
  18288. this.tickAmount = void 0;
  18289. this.tickInterval = void 0;
  18290. this.tickmarkOffset = void 0;
  18291. this.tickPositions = void 0;
  18292. this.tickRotCorr = void 0;
  18293. this.ticks = void 0;
  18294. this.top = void 0;
  18295. this.transA = void 0;
  18296. this.transB = void 0;
  18297. this.translationSlope = void 0;
  18298. this.userOptions = void 0;
  18299. this.visible = void 0;
  18300. this.width = void 0;
  18301. this.zoomEnabled = void 0;
  18302. this.init(chart, userOptions, coll);
  18303. }
  18304. /* *
  18305. *
  18306. * Functions
  18307. *
  18308. * */
  18309. /**
  18310. * Overrideable function to initialize the axis.
  18311. *
  18312. * @see {@link Axis}
  18313. *
  18314. * @function Highcharts.Axis#init
  18315. *
  18316. * @param {Highcharts.Chart} chart
  18317. * The Chart instance to apply the axis on.
  18318. *
  18319. * @param {AxisOptions} userOptions
  18320. * Axis options.
  18321. *
  18322. * @emits Highcharts.Axis#event:afterInit
  18323. * @emits Highcharts.Axis#event:init
  18324. */
  18325. init(chart, userOptions, coll = this.coll) {
  18326. const isXAxis = coll === 'xAxis', axis = this;
  18327. /**
  18328. * The Chart that the axis belongs to.
  18329. *
  18330. * @name Highcharts.Axis#chart
  18331. * @type {Highcharts.Chart}
  18332. */
  18333. axis.chart = chart;
  18334. /**
  18335. * Whether the axis is horizontal.
  18336. *
  18337. * @name Highcharts.Axis#horiz
  18338. * @type {boolean|undefined}
  18339. */
  18340. axis.horiz = axis.isZAxis || (chart.inverted ? !isXAxis : isXAxis);
  18341. /**
  18342. * Whether the axis is the x-axis.
  18343. *
  18344. * @name Highcharts.Axis#isXAxis
  18345. * @type {boolean|undefined}
  18346. */
  18347. axis.isXAxis = isXAxis;
  18348. /**
  18349. * The collection where the axis belongs, for example `xAxis`, `yAxis`
  18350. * or `colorAxis`. Corresponds to properties on Chart, for example
  18351. * {@link Chart.xAxis}.
  18352. *
  18353. * @name Highcharts.Axis#coll
  18354. * @type {string}
  18355. */
  18356. axis.coll = coll;
  18357. fireEvent(this, 'init', { userOptions: userOptions });
  18358. // Needed in setOptions
  18359. axis.opposite = pick(userOptions.opposite, axis.opposite);
  18360. /**
  18361. * The side on which the axis is rendered. 0 is top, 1 is right, 2
  18362. * is bottom and 3 is left.
  18363. *
  18364. * @name Highcharts.Axis#side
  18365. * @type {number}
  18366. */
  18367. axis.side = pick(userOptions.side, axis.side, (axis.horiz ?
  18368. (axis.opposite ? 0 : 2) : // top : bottom
  18369. (axis.opposite ? 1 : 3)) // right : left
  18370. );
  18371. /**
  18372. * Current options for the axis after merge of defaults and user's
  18373. * options.
  18374. *
  18375. * @name Highcharts.Axis#options
  18376. * @type {Highcharts.AxisOptions}
  18377. */
  18378. axis.setOptions(userOptions);
  18379. const options = this.options, labelsOptions = options.labels, type = options.type;
  18380. /**
  18381. * User's options for this axis without defaults.
  18382. *
  18383. * @name Highcharts.Axis#userOptions
  18384. * @type {Highcharts.AxisOptions}
  18385. */
  18386. axis.userOptions = userOptions;
  18387. axis.minPixelPadding = 0;
  18388. /**
  18389. * Whether the axis is reversed. Based on the `axis.reversed`,
  18390. * option, but inverted charts have reversed xAxis by default.
  18391. *
  18392. * @name Highcharts.Axis#reversed
  18393. * @type {boolean}
  18394. */
  18395. axis.reversed = pick(options.reversed, axis.reversed);
  18396. axis.visible = options.visible;
  18397. axis.zoomEnabled = options.zoomEnabled;
  18398. // Initial categories
  18399. axis.hasNames =
  18400. type === 'category' || options.categories === true;
  18401. /**
  18402. * If categories are present for the axis, names are used instead of
  18403. * numbers for that axis.
  18404. *
  18405. * Since Highcharts 3.0, categories can also be extracted by giving each
  18406. * point a name and setting axis type to `category`. However, if you
  18407. * have multiple series, best practice remains defining the `categories`
  18408. * array.
  18409. *
  18410. * @see [xAxis.categories](/highcharts/xAxis.categories)
  18411. *
  18412. * @name Highcharts.Axis#categories
  18413. * @type {Array<string>}
  18414. * @readonly
  18415. */
  18416. axis.categories = options.categories || (axis.hasNames ? [] : void 0);
  18417. if (!axis.names) { // Preserve on update (#3830)
  18418. axis.names = [];
  18419. axis.names.keys = {};
  18420. }
  18421. // Placeholder for plotlines and plotbands groups
  18422. axis.plotLinesAndBandsGroups = {};
  18423. // Shorthand types
  18424. axis.positiveValuesOnly = !!axis.logarithmic;
  18425. // Flag, if axis is linked to another axis
  18426. axis.isLinked = defined(options.linkedTo);
  18427. /**
  18428. * List of major ticks mapped by postition on axis.
  18429. *
  18430. * @see {@link Highcharts.Tick}
  18431. *
  18432. * @name Highcharts.Axis#ticks
  18433. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  18434. */
  18435. axis.ticks = {};
  18436. axis.labelEdge = [];
  18437. /**
  18438. * List of minor ticks mapped by position on the axis.
  18439. *
  18440. * @see {@link Highcharts.Tick}
  18441. *
  18442. * @name Highcharts.Axis#minorTicks
  18443. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  18444. */
  18445. axis.minorTicks = {};
  18446. // List of plotLines/Bands
  18447. axis.plotLinesAndBands = [];
  18448. // Alternate bands
  18449. axis.alternateBands = {};
  18450. // Axis metrics
  18451. axis.len = 0;
  18452. axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
  18453. axis.range = options.range;
  18454. axis.offset = options.offset || 0;
  18455. /**
  18456. * The maximum value of the axis. In a logarithmic axis, this is the
  18457. * logarithm of the real value, and the real value can be obtained from
  18458. * {@link Axis#getExtremes}.
  18459. *
  18460. * @name Highcharts.Axis#max
  18461. * @type {number|null}
  18462. */
  18463. axis.max = null;
  18464. /**
  18465. * The minimum value of the axis. In a logarithmic axis, this is the
  18466. * logarithm of the real value, and the real value can be obtained from
  18467. * {@link Axis#getExtremes}.
  18468. *
  18469. * @name Highcharts.Axis#min
  18470. * @type {number|null}
  18471. */
  18472. axis.min = null;
  18473. /**
  18474. * The processed crosshair options.
  18475. *
  18476. * @name Highcharts.Axis#crosshair
  18477. * @type {boolean|Highcharts.AxisCrosshairOptions}
  18478. */
  18479. const crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1]);
  18480. axis.crosshair = crosshair === true ? {} : crosshair;
  18481. // Register. Don't add it again on Axis.update().
  18482. if (chart.axes.indexOf(axis) === -1) { //
  18483. if (isXAxis) { // #2713
  18484. chart.axes.splice(chart.xAxis.length, 0, axis);
  18485. }
  18486. else {
  18487. chart.axes.push(axis);
  18488. }
  18489. insertItem(this, chart[this.coll]);
  18490. }
  18491. chart.orderItems(axis.coll);
  18492. /**
  18493. * All series associated to the axis.
  18494. *
  18495. * @name Highcharts.Axis#series
  18496. * @type {Array<Highcharts.Series>}
  18497. */
  18498. axis.series = axis.series || []; // populated by Series
  18499. // Reversed axis
  18500. if (chart.inverted &&
  18501. !axis.isZAxis &&
  18502. isXAxis &&
  18503. typeof axis.reversed === 'undefined') {
  18504. axis.reversed = true;
  18505. }
  18506. axis.labelRotation = isNumber(labelsOptions.rotation) ?
  18507. labelsOptions.rotation :
  18508. void 0;
  18509. // Register event listeners
  18510. registerEventOptions(axis, options);
  18511. fireEvent(this, 'afterInit');
  18512. }
  18513. /**
  18514. * Merge and set options.
  18515. *
  18516. * @private
  18517. * @function Highcharts.Axis#setOptions
  18518. *
  18519. * @param {Highcharts.AxisOptions} userOptions
  18520. * Axis options.
  18521. *
  18522. * @emits Highcharts.Axis#event:afterSetOptions
  18523. */
  18524. setOptions(userOptions) {
  18525. this.options = merge(AxisDefaults.defaultXAxisOptions, (this.coll === 'yAxis') && AxisDefaults.defaultYAxisOptions, [
  18526. AxisDefaults.defaultTopAxisOptions,
  18527. AxisDefaults.defaultRightAxisOptions,
  18528. AxisDefaults.defaultBottomAxisOptions,
  18529. AxisDefaults.defaultLeftAxisOptions
  18530. ][this.side], merge(
  18531. // if set in setOptions (#1053):
  18532. defaultOptions[this.coll], userOptions));
  18533. fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
  18534. }
  18535. /**
  18536. * The default label formatter. The context is a special config object for
  18537. * the label. In apps, use the
  18538. * [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
  18539. * instead, except when a modification is needed.
  18540. *
  18541. * @function Highcharts.Axis#defaultLabelFormatter
  18542. *
  18543. * @param {Highcharts.AxisLabelsFormatterContextObject} this
  18544. * Formatter context of axis label.
  18545. *
  18546. * @param {Highcharts.AxisLabelsFormatterContextObject} [ctx]
  18547. * Formatter context of axis label.
  18548. *
  18549. * @return {string}
  18550. * The formatted label content.
  18551. */
  18552. defaultLabelFormatter(ctx) {
  18553. const axis = this.axis, chart = this.chart, { numberFormatter } = chart, value = isNumber(this.value) ? this.value : NaN, time = axis.chart.time, categories = axis.categories, dateTimeLabelFormat = this.dateTimeLabelFormat, lang = defaultOptions.lang, numericSymbols = lang.numericSymbols, numSymMagnitude = lang.numericSymbolMagnitude || 1000,
  18554. // make sure the same symbol is added for all labels on a linear
  18555. // axis
  18556. numericSymbolDetector = axis.logarithmic ?
  18557. Math.abs(value) :
  18558. axis.tickInterval;
  18559. let i = numericSymbols && numericSymbols.length, multi, ret;
  18560. if (categories) {
  18561. ret = `${this.value}`;
  18562. }
  18563. else if (dateTimeLabelFormat) { // datetime axis
  18564. ret = time.dateFormat(dateTimeLabelFormat, value);
  18565. }
  18566. else if (i && numericSymbolDetector >= 1000) {
  18567. // Decide whether we should add a numeric symbol like k (thousands)
  18568. // or M (millions). If we are to enable this in tooltip or other
  18569. // places as well, we can move this logic to the numberFormatter and
  18570. // enable it by a parameter.
  18571. while (i-- && typeof ret === 'undefined') {
  18572. multi = Math.pow(numSymMagnitude, i + 1);
  18573. if (
  18574. // Only accept a numeric symbol when the distance is more
  18575. // than a full unit. So for example if the symbol is k, we
  18576. // don't accept numbers like 0.5k.
  18577. numericSymbolDetector >= multi &&
  18578. // Accept one decimal before the symbol. Accepts 0.5k but
  18579. // not 0.25k. How does this work with the previous?
  18580. (value * 10) % multi === 0 &&
  18581. numericSymbols[i] !== null &&
  18582. value !== 0) { // #5480
  18583. ret = numberFormatter(value / multi, -1) + numericSymbols[i];
  18584. }
  18585. }
  18586. }
  18587. if (typeof ret === 'undefined') {
  18588. if (Math.abs(value) >= 10000) { // add thousands separators
  18589. ret = numberFormatter(value, -1);
  18590. }
  18591. else { // small numbers
  18592. ret = numberFormatter(value, -1, void 0, ''); // #2466
  18593. }
  18594. }
  18595. return ret;
  18596. }
  18597. /**
  18598. * Get the minimum and maximum for the series of each axis. The function
  18599. * analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
  18600. *
  18601. * @private
  18602. * @function Highcharts.Axis#getSeriesExtremes
  18603. *
  18604. * @emits Highcharts.Axis#event:afterGetSeriesExtremes
  18605. * @emits Highcharts.Axis#event:getSeriesExtremes
  18606. */
  18607. getSeriesExtremes() {
  18608. const axis = this, chart = axis.chart;
  18609. let xExtremes;
  18610. fireEvent(this, 'getSeriesExtremes', null, function () {
  18611. axis.hasVisibleSeries = false;
  18612. // Reset properties in case we're redrawing (#3353)
  18613. axis.dataMin = axis.dataMax = axis.threshold = null;
  18614. axis.softThreshold = !axis.isXAxis;
  18615. // Loop through this axis' series
  18616. axis.series.forEach(function (series) {
  18617. if (series.visible ||
  18618. !chart.options.chart.ignoreHiddenSeries) {
  18619. const seriesOptions = series.options;
  18620. let xData, threshold = seriesOptions.threshold, seriesDataMin, seriesDataMax;
  18621. axis.hasVisibleSeries = true;
  18622. // Validate threshold in logarithmic axes
  18623. if (axis.positiveValuesOnly && threshold <= 0) {
  18624. threshold = null;
  18625. }
  18626. // Get dataMin and dataMax for X axes
  18627. if (axis.isXAxis) {
  18628. xData = series.xData;
  18629. if (xData && xData.length) {
  18630. xData = axis.logarithmic ?
  18631. xData.filter((x) => x > 0) :
  18632. xData;
  18633. xExtremes = series.getXExtremes(xData);
  18634. // If xData contains values which is not numbers,
  18635. // then filter them out. To prevent performance hit,
  18636. // we only do this after we have already found
  18637. // seriesDataMin because in most cases all data is
  18638. // valid. #5234.
  18639. seriesDataMin = xExtremes.min;
  18640. seriesDataMax = xExtremes.max;
  18641. if (!isNumber(seriesDataMin) &&
  18642. // #5010:
  18643. !(seriesDataMin instanceof Date)) {
  18644. xData = xData.filter(isNumber);
  18645. xExtremes = series.getXExtremes(xData);
  18646. // Do it again with valid data
  18647. seriesDataMin = xExtremes.min;
  18648. seriesDataMax = xExtremes.max;
  18649. }
  18650. if (xData.length) {
  18651. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  18652. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  18653. }
  18654. }
  18655. // Get dataMin and dataMax for Y axes, as well as handle
  18656. // stacking and processed data
  18657. }
  18658. else {
  18659. // Get this particular series extremes
  18660. const dataExtremes = series.applyExtremes();
  18661. // Get the dataMin and dataMax so far. If percentage is
  18662. // used, the min and max are always 0 and 100. If
  18663. // seriesDataMin and seriesDataMax is null, then series
  18664. // doesn't have active y data, we continue with nulls
  18665. if (isNumber(dataExtremes.dataMin)) {
  18666. seriesDataMin = dataExtremes.dataMin;
  18667. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  18668. }
  18669. if (isNumber(dataExtremes.dataMax)) {
  18670. seriesDataMax = dataExtremes.dataMax;
  18671. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  18672. }
  18673. // Adjust to threshold
  18674. if (defined(threshold)) {
  18675. axis.threshold = threshold;
  18676. }
  18677. // If any series has a hard threshold, it takes
  18678. // precedence
  18679. if (!seriesOptions.softThreshold ||
  18680. axis.positiveValuesOnly) {
  18681. axis.softThreshold = false;
  18682. }
  18683. }
  18684. }
  18685. });
  18686. });
  18687. fireEvent(this, 'afterGetSeriesExtremes');
  18688. }
  18689. /**
  18690. * Translate from axis value to pixel position on the chart, or back. Use
  18691. * the `toPixels` and `toValue` functions in applications.
  18692. *
  18693. * @private
  18694. * @function Highcharts.Axis#translate
  18695. */
  18696. translate(val, backwards, cvsCoord, old, handleLog, pointPlacement) {
  18697. const axis = (this.linkedParent || this), // #1417
  18698. localMin = (old && axis.old ? axis.old.min : axis.min);
  18699. if (!isNumber(localMin)) {
  18700. return NaN;
  18701. }
  18702. const minPixelPadding = axis.minPixelPadding, doPostTranslate = (axis.isOrdinal ||
  18703. axis.brokenAxis && axis.brokenAxis.hasBreaks ||
  18704. (axis.logarithmic && handleLog)) && axis.lin2val;
  18705. let sign = 1, cvsOffset = 0, localA = old && axis.old ? axis.old.transA : axis.transA, returnValue = 0;
  18706. if (!localA) {
  18707. localA = axis.transA;
  18708. }
  18709. // In vertical axes, the canvas coordinates start from 0 at the top like
  18710. // in SVG.
  18711. if (cvsCoord) {
  18712. sign *= -1; // canvas coordinates inverts the value
  18713. cvsOffset = axis.len;
  18714. }
  18715. // Handle reversed axis
  18716. if (axis.reversed) {
  18717. sign *= -1;
  18718. cvsOffset -= sign * (axis.sector || axis.len);
  18719. }
  18720. // From pixels to value
  18721. if (backwards) { // reverse translation
  18722. val = val * sign + cvsOffset;
  18723. val -= minPixelPadding;
  18724. // from chart pixel to value:
  18725. returnValue = val / localA + localMin;
  18726. if (doPostTranslate) { // log, ordinal and broken axis
  18727. returnValue = axis.lin2val(returnValue);
  18728. }
  18729. // From value to pixels
  18730. }
  18731. else {
  18732. if (doPostTranslate) { // log, ordinal and broken axis
  18733. val = axis.val2lin(val);
  18734. }
  18735. const value = sign * (val - localMin) * localA;
  18736. returnValue = (!axis.isRadial ? correctFloat(value) : value) +
  18737. cvsOffset +
  18738. (sign * minPixelPadding) +
  18739. (isNumber(pointPlacement) ? localA * pointPlacement : 0);
  18740. }
  18741. return returnValue;
  18742. }
  18743. /**
  18744. * Translate a value in terms of axis units into pixels within the chart.
  18745. *
  18746. * @function Highcharts.Axis#toPixels
  18747. *
  18748. * @param {number} value
  18749. * A value in terms of axis units.
  18750. *
  18751. * @param {boolean} paneCoordinates
  18752. * Whether to return the pixel coordinate relative to the chart or just the
  18753. * axis/pane itself.
  18754. *
  18755. * @return {number}
  18756. * Pixel position of the value on the chart or axis.
  18757. */
  18758. toPixels(value, paneCoordinates) {
  18759. return this.translate(value, false, !this.horiz, void 0, true) +
  18760. (paneCoordinates ? 0 : this.pos);
  18761. }
  18762. /**
  18763. * Translate a pixel position along the axis to a value in terms of axis
  18764. * units.
  18765. *
  18766. * @function Highcharts.Axis#toValue
  18767. *
  18768. * @param {number} pixel
  18769. * The pixel value coordinate.
  18770. *
  18771. * @param {boolean} [paneCoordinates=false]
  18772. * Whether the input pixel is relative to the chart or just the axis/pane
  18773. * itself.
  18774. *
  18775. * @return {number}
  18776. * The axis value.
  18777. */
  18778. toValue(pixel, paneCoordinates) {
  18779. return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, void 0, true);
  18780. }
  18781. /**
  18782. * Create the path for a plot line that goes from the given value on
  18783. * this axis, across the plot to the opposite side. Also used internally for
  18784. * grid lines and crosshairs.
  18785. *
  18786. * @function Highcharts.Axis#getPlotLinePath
  18787. *
  18788. * @param {Highcharts.AxisPlotLinePathOptionsObject} options
  18789. * Options for the path.
  18790. *
  18791. * @return {Highcharts.SVGPathArray|null}
  18792. * The SVG path definition for the plot line.
  18793. */
  18794. getPlotLinePath(options) {
  18795. const axis = this, chart = axis.chart, axisLeft = axis.left, axisTop = axis.top, old = options.old, value = options.value, lineWidth = options.lineWidth, cHeight = (old && chart.oldChartHeight) || chart.chartHeight, cWidth = (old && chart.oldChartWidth) || chart.chartWidth, transB = axis.transB;
  18796. let translatedValue = options.translatedValue, force = options.force, x1, y1, x2, y2, skip;
  18797. // eslint-disable-next-line valid-jsdoc
  18798. /**
  18799. * Check if x is between a and b. If not, either move to a/b
  18800. * or skip, depending on the force parameter.
  18801. * @private
  18802. */
  18803. function between(x, a, b) {
  18804. if (force !== 'pass' && (x < a || x > b)) {
  18805. if (force) {
  18806. x = clamp(x, a, b);
  18807. }
  18808. else {
  18809. skip = true;
  18810. }
  18811. }
  18812. return x;
  18813. }
  18814. const evt = {
  18815. value: value,
  18816. lineWidth: lineWidth,
  18817. old: old,
  18818. force: force,
  18819. acrossPanes: options.acrossPanes,
  18820. translatedValue: translatedValue
  18821. };
  18822. fireEvent(this, 'getPlotLinePath', evt, function (e) {
  18823. translatedValue = pick(translatedValue, axis.translate(value, void 0, void 0, old));
  18824. // Keep the translated value within sane bounds, and avoid Infinity
  18825. // to fail the isNumber test (#7709).
  18826. translatedValue = clamp(translatedValue, -1e5, 1e5);
  18827. x1 = x2 = Math.round(translatedValue + transB);
  18828. y1 = y2 = Math.round(cHeight - translatedValue - transB);
  18829. if (!isNumber(translatedValue)) { // no min or max
  18830. skip = true;
  18831. force = false; // #7175, don't force it when path is invalid
  18832. }
  18833. else if (axis.horiz) {
  18834. y1 = axisTop;
  18835. y2 = cHeight - axis.bottom;
  18836. x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
  18837. }
  18838. else {
  18839. x1 = axisLeft;
  18840. x2 = cWidth - axis.right;
  18841. y1 = y2 = between(y1, axisTop, axisTop + axis.height);
  18842. }
  18843. e.path = skip && !force ?
  18844. null :
  18845. chart.renderer.crispLine([['M', x1, y1], ['L', x2, y2]], lineWidth || 1);
  18846. });
  18847. return evt.path;
  18848. }
  18849. /**
  18850. * Internal function to get the tick positions of a linear axis to round
  18851. * values like whole tens or every five.
  18852. *
  18853. * @function Highcharts.Axis#getLinearTickPositions
  18854. *
  18855. * @param {number} tickInterval
  18856. * The normalized tick interval.
  18857. *
  18858. * @param {number} min
  18859. * Axis minimum.
  18860. *
  18861. * @param {number} max
  18862. * Axis maximum.
  18863. *
  18864. * @return {Array<number>}
  18865. * An array of axis values where ticks should be placed.
  18866. */
  18867. getLinearTickPositions(tickInterval, min, max) {
  18868. const roundedMin = correctFloat(Math.floor(min / tickInterval) * tickInterval), roundedMax = correctFloat(Math.ceil(max / tickInterval) * tickInterval), tickPositions = [];
  18869. let pos, lastPos, precision;
  18870. // When the precision is higher than what we filter out in
  18871. // correctFloat, skip it (#6183).
  18872. if (correctFloat(roundedMin + tickInterval) === roundedMin) {
  18873. precision = 20;
  18874. }
  18875. // For single points, add a tick regardless of the relative position
  18876. // (#2662, #6274)
  18877. if (this.single) {
  18878. return [min];
  18879. }
  18880. // Populate the intermediate values
  18881. pos = roundedMin;
  18882. while (pos <= roundedMax) {
  18883. // Place the tick on the rounded value
  18884. tickPositions.push(pos);
  18885. // Always add the raw tickInterval, not the corrected one.
  18886. pos = correctFloat(pos + tickInterval, precision);
  18887. // If the interval is not big enough in the current min - max range
  18888. // to actually increase the loop variable, we need to break out to
  18889. // prevent endless loop. Issue #619
  18890. if (pos === lastPos) {
  18891. break;
  18892. }
  18893. // Record the last value
  18894. lastPos = pos;
  18895. }
  18896. return tickPositions;
  18897. }
  18898. /**
  18899. * Resolve the new minorTicks/minorTickInterval options into the legacy
  18900. * loosely typed minorTickInterval option.
  18901. *
  18902. * @function Highcharts.Axis#getMinorTickInterval
  18903. *
  18904. * @return {number|"auto"|null}
  18905. * Legacy option
  18906. */
  18907. getMinorTickInterval() {
  18908. const options = this.options;
  18909. if (options.minorTicks === true) {
  18910. return pick(options.minorTickInterval, 'auto');
  18911. }
  18912. if (options.minorTicks === false) {
  18913. return null;
  18914. }
  18915. return options.minorTickInterval;
  18916. }
  18917. /**
  18918. * Internal function to return the minor tick positions. For logarithmic
  18919. * axes, the same logic as for major ticks is reused.
  18920. *
  18921. * @function Highcharts.Axis#getMinorTickPositions
  18922. *
  18923. * @return {Array<number>}
  18924. * An array of axis values where ticks should be placed.
  18925. */
  18926. getMinorTickPositions() {
  18927. const axis = this, options = axis.options, tickPositions = axis.tickPositions, minorTickInterval = axis.minorTickInterval, pointRangePadding = axis.pointRangePadding || 0, min = axis.min - pointRangePadding, // #1498
  18928. max = axis.max + pointRangePadding, // #1498
  18929. range = max - min;
  18930. let minorTickPositions = [], pos;
  18931. // If minor ticks get too dense, they are hard to read, and may cause
  18932. // long running script. So we don't draw them.
  18933. if (range && range / minorTickInterval < axis.len / 3) { // #3875
  18934. const logarithmic = axis.logarithmic;
  18935. if (logarithmic) {
  18936. // For each interval in the major ticks, compute the minor ticks
  18937. // separately.
  18938. this.paddedTicks.forEach(function (_pos, i, paddedTicks) {
  18939. if (i) {
  18940. minorTickPositions.push.apply(minorTickPositions, logarithmic.getLogTickPositions(minorTickInterval, paddedTicks[i - 1], paddedTicks[i], true));
  18941. }
  18942. });
  18943. }
  18944. else if (axis.dateTime &&
  18945. this.getMinorTickInterval() === 'auto') { // #1314
  18946. minorTickPositions = minorTickPositions.concat(axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(minorTickInterval), min, max, options.startOfWeek));
  18947. }
  18948. else {
  18949. for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) {
  18950. // Very, very, tight grid lines (#5771)
  18951. if (pos === minorTickPositions[0]) {
  18952. break;
  18953. }
  18954. minorTickPositions.push(pos);
  18955. }
  18956. }
  18957. }
  18958. if (minorTickPositions.length !== 0) {
  18959. axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
  18960. }
  18961. return minorTickPositions;
  18962. }
  18963. /**
  18964. * Adjust the min and max for the minimum range. Keep in mind that the
  18965. * series data is not yet processed, so we don't have information on data
  18966. * cropping and grouping, or updated `axis.pointRange` or
  18967. * `series.pointRange`. The data can't be processed until we have finally
  18968. * established min and max.
  18969. *
  18970. * @private
  18971. * @function Highcharts.Axis#adjustForMinRange
  18972. */
  18973. adjustForMinRange() {
  18974. const axis = this, options = axis.options, logarithmic = axis.logarithmic;
  18975. let min = axis.min, max = axis.max, zoomOffset, spaceAvailable, closestDataRange, minArgs, maxArgs, minRange;
  18976. // Set the automatic minimum range based on the closest point distance
  18977. if (axis.isXAxis &&
  18978. typeof axis.minRange === 'undefined' &&
  18979. !logarithmic) {
  18980. if (defined(options.min) ||
  18981. defined(options.max) ||
  18982. defined(options.floor) ||
  18983. defined(options.ceiling)) {
  18984. axis.minRange = null; // don't do this again
  18985. }
  18986. else {
  18987. // Find the closest distance between raw data points, as opposed
  18988. // to closestPointRange that applies to processed points
  18989. // (cropped and grouped)
  18990. closestDataRange = getClosestDistance(axis.series.map((s) => { var _a;
  18991. // If xIncrement, we only need to measure the two first
  18992. // points to get the distance. Saves processing time.
  18993. return (s.xIncrement ? (_a = s.xData) === null || _a === void 0 ? void 0 : _a.slice(0, 2) : s.xData) || []; })) || 0;
  18994. axis.minRange = Math.min(closestDataRange * 5, axis.dataMax - axis.dataMin);
  18995. }
  18996. }
  18997. // if minRange is exceeded, adjust
  18998. if (max - min < axis.minRange) {
  18999. spaceAvailable =
  19000. axis.dataMax - axis.dataMin >=
  19001. axis.minRange;
  19002. minRange = axis.minRange;
  19003. zoomOffset = (minRange - max + min) / 2;
  19004. // if min and max options have been set, don't go beyond it
  19005. minArgs = [
  19006. min - zoomOffset,
  19007. pick(options.min, min - zoomOffset)
  19008. ];
  19009. // If space is available, stay within the data range
  19010. if (spaceAvailable) {
  19011. minArgs[2] = logarithmic ?
  19012. logarithmic.log2lin(axis.dataMin) :
  19013. axis.dataMin;
  19014. }
  19015. min = arrayMax(minArgs);
  19016. maxArgs = [
  19017. min + minRange,
  19018. pick(options.max, min + minRange)
  19019. ];
  19020. // If space is availabe, stay within the data range
  19021. if (spaceAvailable) {
  19022. maxArgs[2] = logarithmic ?
  19023. logarithmic.log2lin(axis.dataMax) :
  19024. axis.dataMax;
  19025. }
  19026. max = arrayMin(maxArgs);
  19027. // now if the max is adjusted, adjust the min back
  19028. if (max - min < minRange) {
  19029. minArgs[0] = max - minRange;
  19030. minArgs[1] = pick(options.min, max - minRange);
  19031. min = arrayMax(minArgs);
  19032. }
  19033. }
  19034. // Record modified extremes
  19035. axis.min = min;
  19036. axis.max = max;
  19037. }
  19038. /**
  19039. * Find the closestPointRange across all series, including the single data
  19040. * series.
  19041. *
  19042. * @private
  19043. * @function Highcharts.Axis#getClosest
  19044. */
  19045. getClosest() {
  19046. let closestSingleDistance, closestDistance;
  19047. if (this.categories) {
  19048. closestDistance = 1;
  19049. }
  19050. else {
  19051. const singleXs = [];
  19052. this.series.forEach(function (series) {
  19053. var _a;
  19054. const seriesClosest = series.closestPointRange, visible = series.visible ||
  19055. !series.chart.options.chart.ignoreHiddenSeries;
  19056. if (((_a = series.xData) === null || _a === void 0 ? void 0 : _a.length) === 1) {
  19057. singleXs.push(series.xData[0]);
  19058. }
  19059. else if (!series.noSharedTooltip &&
  19060. defined(seriesClosest) &&
  19061. visible) {
  19062. closestDistance = defined(closestDistance) ?
  19063. Math.min(closestDistance, seriesClosest) :
  19064. seriesClosest;
  19065. }
  19066. });
  19067. if (singleXs.length) {
  19068. singleXs.sort((a, b) => a - b);
  19069. closestSingleDistance = getClosestDistance([singleXs]);
  19070. }
  19071. }
  19072. if (closestSingleDistance && closestDistance) {
  19073. return Math.min(closestSingleDistance, closestDistance);
  19074. }
  19075. return closestSingleDistance || closestDistance;
  19076. }
  19077. /**
  19078. * When a point name is given and no x, search for the name in the existing
  19079. * categories, or if categories aren't provided, search names or create a
  19080. * new category (#2522).
  19081. *
  19082. * @private
  19083. * @function Highcharts.Axis#nameToX
  19084. *
  19085. * @param {Highcharts.Point} point
  19086. * The point to inspect.
  19087. *
  19088. * @return {number}
  19089. * The X value that the point is given.
  19090. */
  19091. nameToX(point) {
  19092. const explicitCategories = isArray(this.options.categories), names = explicitCategories ? this.categories : this.names;
  19093. let nameX = point.options.x, x;
  19094. point.series.requireSorting = false;
  19095. if (!defined(nameX)) {
  19096. nameX = this.options.uniqueNames && names ?
  19097. (explicitCategories ?
  19098. names.indexOf(point.name) :
  19099. pick(names.keys[point.name], -1)) :
  19100. point.series.autoIncrement();
  19101. }
  19102. if (nameX === -1) { // Not found in currenct categories
  19103. if (!explicitCategories && names) {
  19104. x = names.length;
  19105. }
  19106. }
  19107. else {
  19108. x = nameX;
  19109. }
  19110. // Write the last point's name to the names array
  19111. if (typeof x !== 'undefined') {
  19112. this.names[x] = point.name;
  19113. // Backwards mapping is much faster than array searching (#7725)
  19114. this.names.keys[point.name] = x;
  19115. }
  19116. else if (point.x) {
  19117. x = point.x; // #17438
  19118. }
  19119. return x;
  19120. }
  19121. /**
  19122. * When changes have been done to series data, update the axis.names.
  19123. *
  19124. * @private
  19125. * @function Highcharts.Axis#updateNames
  19126. */
  19127. updateNames() {
  19128. const axis = this, names = this.names, i = names.length;
  19129. if (i > 0) {
  19130. Object.keys(names.keys).forEach(function (key) {
  19131. delete (names.keys)[key];
  19132. });
  19133. names.length = 0;
  19134. this.minRange = this.userMinRange; // Reset
  19135. (this.series || []).forEach(function (series) {
  19136. // Reset incrementer (#5928)
  19137. series.xIncrement = null;
  19138. // When adding a series, points are not yet generated
  19139. if (!series.points || series.isDirtyData) {
  19140. // When we're updating the series with data that is longer
  19141. // than it was, and cropThreshold is passed, we need to make
  19142. // sure that the axis.max is increased _before_ running the
  19143. // premature processData. Otherwise this early iteration of
  19144. // processData will crop the points to axis.max, and the
  19145. // names array will be too short (#5857).
  19146. axis.max = Math.max(axis.max, series.xData.length - 1);
  19147. series.processData();
  19148. series.generatePoints();
  19149. }
  19150. series.data.forEach(function (point, i) {
  19151. let x;
  19152. if (point &&
  19153. point.options &&
  19154. typeof point.name !== 'undefined' // #9562
  19155. ) {
  19156. x = axis.nameToX(point);
  19157. if (typeof x !== 'undefined' && x !== point.x) {
  19158. point.x = x;
  19159. series.xData[i] = x;
  19160. }
  19161. }
  19162. });
  19163. });
  19164. }
  19165. }
  19166. /**
  19167. * Update translation information.
  19168. *
  19169. * @private
  19170. * @function Highcharts.Axis#setAxisTranslation
  19171. *
  19172. * @emits Highcharts.Axis#event:afterSetAxisTranslation
  19173. */
  19174. setAxisTranslation() {
  19175. const axis = this, range = axis.max - axis.min, linkedParent = axis.linkedParent, hasCategories = !!axis.categories, isXAxis = axis.isXAxis;
  19176. let pointRange = axis.axisPointRange || 0, closestPointRange, minPointOffset = 0, pointRangePadding = 0, ordinalCorrection, transA = axis.transA;
  19177. // Adjust translation for padding. Y axis with categories need to go
  19178. // through the same (#1784).
  19179. if (isXAxis || hasCategories || pointRange) {
  19180. // Get the closest points
  19181. closestPointRange = axis.getClosest();
  19182. if (linkedParent) {
  19183. minPointOffset = linkedParent.minPointOffset;
  19184. pointRangePadding = linkedParent.pointRangePadding;
  19185. }
  19186. else {
  19187. axis.series.forEach(function (series) {
  19188. const seriesPointRange = hasCategories ?
  19189. 1 :
  19190. (isXAxis ?
  19191. pick(series.options.pointRange, closestPointRange, 0) :
  19192. (axis.axisPointRange || 0)), // #2806
  19193. pointPlacement = series.options.pointPlacement;
  19194. pointRange = Math.max(pointRange, seriesPointRange);
  19195. if (!axis.single || hasCategories) {
  19196. // TODO: series should internally set x- and y-
  19197. // pointPlacement to simplify this logic.
  19198. const isPointPlacementAxis = series.is('xrange') ?
  19199. !isXAxis :
  19200. isXAxis;
  19201. // minPointOffset is the value padding to the left of
  19202. // the axis in order to make room for points with a
  19203. // pointRange, typically columns. When the
  19204. // pointPlacement option is 'between' or 'on', this
  19205. // padding does not apply.
  19206. minPointOffset = Math.max(minPointOffset, isPointPlacementAxis && isString(pointPlacement) ?
  19207. 0 :
  19208. seriesPointRange / 2);
  19209. // Determine the total padding needed to the length of
  19210. // the axis to make room for the pointRange. If the
  19211. // series' pointPlacement is 'on', no padding is added.
  19212. pointRangePadding = Math.max(pointRangePadding, isPointPlacementAxis && pointPlacement === 'on' ?
  19213. 0 :
  19214. seriesPointRange);
  19215. }
  19216. });
  19217. }
  19218. // Record minPointOffset and pointRangePadding
  19219. ordinalCorrection = (axis.ordinal && axis.ordinal.slope && closestPointRange) ?
  19220. axis.ordinal.slope / closestPointRange :
  19221. 1; // #988, #1853
  19222. axis.minPointOffset = minPointOffset =
  19223. minPointOffset * ordinalCorrection;
  19224. axis.pointRangePadding =
  19225. pointRangePadding = pointRangePadding * ordinalCorrection;
  19226. // pointRange means the width reserved for each point, like in a
  19227. // column chart
  19228. axis.pointRange = Math.min(pointRange, axis.single && hasCategories ? 1 : range);
  19229. // closestPointRange means the closest distance between points. In
  19230. // columns it is mostly equal to pointRange, but in lines pointRange
  19231. // is 0 while closestPointRange is some other value
  19232. if (isXAxis && closestPointRange) {
  19233. axis.closestPointRange = closestPointRange;
  19234. }
  19235. }
  19236. // Secondary values
  19237. axis.translationSlope = axis.transA = transA =
  19238. axis.staticScale ||
  19239. axis.len / ((range + pointRangePadding) || 1);
  19240. // Translation addend
  19241. axis.transB = axis.horiz ? axis.left : axis.bottom;
  19242. axis.minPixelPadding = transA * minPointOffset;
  19243. fireEvent(this, 'afterSetAxisTranslation');
  19244. }
  19245. /**
  19246. * @private
  19247. * @function Highcharts.Axis#minFromRange
  19248. */
  19249. minFromRange() {
  19250. const axis = this;
  19251. return axis.max - axis.range;
  19252. }
  19253. /**
  19254. * Set the tick positions to round values and optionally extend the extremes
  19255. * to the nearest tick.
  19256. *
  19257. * @private
  19258. * @function Highcharts.Axis#setTickInterval
  19259. *
  19260. * @param {boolean} secondPass
  19261. * TO-DO: parameter description
  19262. *
  19263. * @emits Highcharts.Axis#event:foundExtremes
  19264. */
  19265. setTickInterval(secondPass) {
  19266. const axis = this, chart = axis.chart, log = axis.logarithmic, options = axis.options, isXAxis = axis.isXAxis, isLinked = axis.isLinked, tickPixelIntervalOption = options.tickPixelInterval, categories = axis.categories, softThreshold = axis.softThreshold;
  19267. let maxPadding = options.maxPadding, minPadding = options.minPadding, length, linkedParentExtremes,
  19268. // Only non-negative tickInterval is valid, #12961
  19269. tickIntervalOption = isNumber(options.tickInterval) && options.tickInterval >= 0 ?
  19270. options.tickInterval : void 0, threshold = isNumber(axis.threshold) ? axis.threshold : null, thresholdMin, thresholdMax, hardMin, hardMax;
  19271. if (!axis.dateTime && !categories && !isLinked) {
  19272. this.getTickAmount();
  19273. }
  19274. // Min or max set either by zooming/setExtremes or initial options
  19275. hardMin = pick(axis.userMin, options.min);
  19276. hardMax = pick(axis.userMax, options.max);
  19277. // Linked axis gets the extremes from the parent axis
  19278. if (isLinked) {
  19279. axis.linkedParent = chart[axis.coll][options.linkedTo];
  19280. linkedParentExtremes = axis.linkedParent.getExtremes();
  19281. axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
  19282. axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
  19283. if (options.type !== axis.linkedParent.options.type) {
  19284. // Can't link axes of different type
  19285. error(11, 1, chart);
  19286. }
  19287. // Initial min and max from the extreme data values
  19288. }
  19289. else {
  19290. // Adjust to hard threshold
  19291. if (softThreshold && defined(threshold)) {
  19292. if (axis.dataMin >= threshold) {
  19293. thresholdMin = threshold;
  19294. minPadding = 0;
  19295. }
  19296. else if (axis.dataMax <= threshold) {
  19297. thresholdMax = threshold;
  19298. maxPadding = 0;
  19299. }
  19300. }
  19301. axis.min = pick(hardMin, thresholdMin, axis.dataMin);
  19302. axis.max = pick(hardMax, thresholdMax, axis.dataMax);
  19303. }
  19304. if (log) {
  19305. if (axis.positiveValuesOnly &&
  19306. !secondPass &&
  19307. Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
  19308. // Can't plot negative values on log axis
  19309. error(10, 1, chart);
  19310. }
  19311. // The correctFloat cures #934, float errors on full tens. But it
  19312. // was too aggressive for #4360 because of conversion back to lin,
  19313. // therefore use precision 15.
  19314. axis.min = correctFloat(log.log2lin(axis.min), 16);
  19315. axis.max = correctFloat(log.log2lin(axis.max), 16);
  19316. }
  19317. // handle zoomed range
  19318. if (axis.range && defined(axis.max)) {
  19319. // #618, #6773:
  19320. axis.userMin = axis.min = hardMin =
  19321. Math.max(axis.dataMin, axis.minFromRange());
  19322. axis.userMax = hardMax = axis.max;
  19323. axis.range = null; // don't use it when running setExtremes
  19324. }
  19325. // Hook for Highcharts Stock Scroller.
  19326. // Consider combining with beforePadding.
  19327. fireEvent(axis, 'foundExtremes');
  19328. // Hook for adjusting this.min and this.max. Used by bubble series.
  19329. if (axis.beforePadding) {
  19330. axis.beforePadding();
  19331. }
  19332. // Adjust min and max for the minimum range
  19333. axis.adjustForMinRange();
  19334. // Handle options for floor, ceiling, softMin and softMax (#6359)
  19335. if (!isNumber(axis.userMin)) {
  19336. if (isNumber(options.softMin) && options.softMin < axis.min) {
  19337. axis.min = hardMin = options.softMin; // #6894
  19338. }
  19339. }
  19340. if (!isNumber(axis.userMax)) {
  19341. if (isNumber(options.softMax) && options.softMax > axis.max) {
  19342. axis.max = hardMax = options.softMax; // #6894
  19343. }
  19344. }
  19345. // Pad the values to get clear of the chart's edges. To avoid
  19346. // tickInterval taking the padding into account, we do this after
  19347. // computing tick interval (#1337).
  19348. if (!categories &&
  19349. !axis.axisPointRange &&
  19350. !(axis.stacking && axis.stacking.usePercentage) &&
  19351. !isLinked &&
  19352. defined(axis.min) &&
  19353. defined(axis.max)) {
  19354. length = axis.max - axis.min;
  19355. if (length) {
  19356. if (!defined(hardMin) && minPadding) {
  19357. axis.min -= length * minPadding;
  19358. }
  19359. if (!defined(hardMax) && maxPadding) {
  19360. axis.max += length * maxPadding;
  19361. }
  19362. }
  19363. }
  19364. if (!isNumber(axis.userMin) && isNumber(options.floor)) {
  19365. axis.min = Math.max(axis.min, options.floor);
  19366. }
  19367. if (!isNumber(axis.userMax) && isNumber(options.ceiling)) {
  19368. axis.max = Math.min(axis.max, options.ceiling);
  19369. }
  19370. // When the threshold is soft, adjust the extreme value only if the data
  19371. // extreme and the padded extreme land on either side of the threshold.
  19372. // For example, a series of [0, 1, 2, 3] would make the yAxis add a tick
  19373. // for -1 because of the default minPadding and startOnTick options.
  19374. // This is prevented by the softThreshold option.
  19375. if (softThreshold && defined(axis.dataMin)) {
  19376. threshold = threshold || 0;
  19377. if (!defined(hardMin) &&
  19378. axis.min < threshold &&
  19379. axis.dataMin >= threshold) {
  19380. axis.min = axis.options.minRange ?
  19381. Math.min(threshold, axis.max -
  19382. axis.minRange) :
  19383. threshold;
  19384. }
  19385. else if (!defined(hardMax) &&
  19386. axis.max > threshold &&
  19387. axis.dataMax <= threshold) {
  19388. axis.max = axis.options.minRange ?
  19389. Math.max(threshold, axis.min +
  19390. axis.minRange) :
  19391. threshold;
  19392. }
  19393. }
  19394. // If min is bigger than highest, or if max less than lowest value, the
  19395. // chart should not render points. (#14417)
  19396. if (isNumber(axis.min) &&
  19397. isNumber(axis.max) &&
  19398. !this.chart.polar &&
  19399. (axis.min > axis.max)) {
  19400. if (defined(axis.options.min)) {
  19401. axis.max = axis.min;
  19402. }
  19403. else if (defined(axis.options.max)) {
  19404. axis.min = axis.max;
  19405. }
  19406. }
  19407. // get tickInterval
  19408. if (axis.min === axis.max ||
  19409. typeof axis.min === 'undefined' ||
  19410. typeof axis.max === 'undefined') {
  19411. axis.tickInterval = 1;
  19412. }
  19413. else if (isLinked &&
  19414. axis.linkedParent &&
  19415. !tickIntervalOption &&
  19416. tickPixelIntervalOption ===
  19417. axis.linkedParent.options.tickPixelInterval) {
  19418. axis.tickInterval = tickIntervalOption =
  19419. axis.linkedParent.tickInterval;
  19420. }
  19421. else {
  19422. axis.tickInterval = pick(tickIntervalOption, this.tickAmount ?
  19423. ((axis.max - axis.min) /
  19424. Math.max(this.tickAmount - 1, 1)) :
  19425. void 0,
  19426. // For categoried axis, 1 is default, for linear axis use
  19427. // tickPix
  19428. categories ?
  19429. 1 :
  19430. // don't let it be more than the data range
  19431. (axis.max - axis.min) *
  19432. tickPixelIntervalOption /
  19433. Math.max(axis.len, tickPixelIntervalOption));
  19434. }
  19435. // Now we're finished detecting min and max, crop and group series data.
  19436. // This is in turn needed in order to find tick positions in ordinal
  19437. // axes.
  19438. if (isXAxis && !secondPass) {
  19439. const hasExtremesChanged = axis.min !==
  19440. (axis.old && axis.old.min) ||
  19441. axis.max !== (axis.old && axis.old.max);
  19442. // First process all series assigned to that axis.
  19443. axis.series.forEach(function (series) {
  19444. // Allows filtering out points outside the plot area.
  19445. series.forceCrop = (series.forceCropping &&
  19446. series.forceCropping());
  19447. series.processData(hasExtremesChanged);
  19448. });
  19449. // Then apply grouping if needed. The hasExtremesChanged helps to
  19450. // decide if the data grouping should be skipped in the further
  19451. // calculations #16319.
  19452. fireEvent(this, 'postProcessData', { hasExtremesChanged });
  19453. }
  19454. // set the translation factor used in translate function
  19455. axis.setAxisTranslation();
  19456. // hook for ordinal axes and radial axes
  19457. fireEvent(this, 'initialAxisTranslation');
  19458. // In column-like charts, don't cramp in more ticks than there are
  19459. // points (#1943, #4184)
  19460. if (axis.pointRange && !tickIntervalOption) {
  19461. axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
  19462. }
  19463. // Before normalizing the tick interval, handle minimum tick interval.
  19464. // This applies only if tickInterval is not defined.
  19465. const minTickInterval = pick(options.minTickInterval,
  19466. // In datetime axes, don't go below the data interval, except when
  19467. // there are scatter-like series involved (#13369).
  19468. axis.dateTime &&
  19469. !axis.series.some((s) => s.noSharedTooltip) ?
  19470. axis.closestPointRange : 0);
  19471. if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
  19472. axis.tickInterval = minTickInterval;
  19473. }
  19474. // For linear axes, normalize the interval
  19475. if (!axis.dateTime && !axis.logarithmic && !tickIntervalOption) {
  19476. axis.tickInterval = getNormalizedTickInterval(axis, axis.tickInterval);
  19477. }
  19478. // Prevent ticks from getting so close that we can't draw the labels
  19479. if (!this.tickAmount) {
  19480. axis.tickInterval = axis.unsquish();
  19481. }
  19482. this.setTickPositions();
  19483. }
  19484. /**
  19485. * Now we have computed the normalized tickInterval, get the tick positions.
  19486. *
  19487. * @private
  19488. * @function Highcharts.Axis#setTickPositions
  19489. *
  19490. * @emits Highcharts.Axis#event:afterSetTickPositions
  19491. */
  19492. setTickPositions() {
  19493. const axis = this, options = this.options, tickPositionsOption = options.tickPositions, tickPositioner = options.tickPositioner, minorTickIntervalOption = this.getMinorTickInterval(), hasVerticalPanning = this.hasVerticalPanning(), isColorAxis = this.coll === 'colorAxis', startOnTick = ((isColorAxis || !hasVerticalPanning) && options.startOnTick), endOnTick = ((isColorAxis || !hasVerticalPanning) && options.endOnTick);
  19494. let tickPositions = [], tickPositionerResult;
  19495. // Set the tickmarkOffset
  19496. this.tickmarkOffset = (this.categories &&
  19497. options.tickmarkPlacement === 'between' &&
  19498. this.tickInterval === 1) ? 0.5 : 0; // #3202
  19499. // get minorTickInterval
  19500. this.minorTickInterval =
  19501. minorTickIntervalOption === 'auto' &&
  19502. this.tickInterval ?
  19503. this.tickInterval / options.minorTicksPerMajor :
  19504. minorTickIntervalOption;
  19505. // When there is only one point, or all points have the same value on
  19506. // this axis, then min and max are equal and tickPositions.length is 0
  19507. // or 1. In this case, add some padding in order to center the point,
  19508. // but leave it with one tick. #1337.
  19509. this.single =
  19510. this.min === this.max &&
  19511. defined(this.min) &&
  19512. !this.tickAmount &&
  19513. (
  19514. // Data is on integer (#6563)
  19515. parseInt(this.min, 10) === this.min ||
  19516. // Between integers and decimals are not allowed (#6274)
  19517. options.allowDecimals !== false);
  19518. /**
  19519. * Contains the current positions that are laid out on the axis. The
  19520. * positions are numbers in terms of axis values. In a category axis
  19521. * they are integers, in a datetime axis they are also integers, but
  19522. * designating milliseconds.
  19523. *
  19524. * This property is read only - for modifying the tick positions, use
  19525. * the `tickPositioner` callback or [axis.tickPositions(
  19526. * https://api.highcharts.com/highcharts/xAxis.tickPositions) option
  19527. * instead.
  19528. *
  19529. * @name Highcharts.Axis#tickPositions
  19530. * @type {Highcharts.AxisTickPositionsArray|undefined}
  19531. */
  19532. if (tickPositionsOption) {
  19533. // Find the tick positions. Work on a copy (#1565)
  19534. tickPositions = tickPositionsOption.slice();
  19535. }
  19536. else if (isNumber(this.min) && isNumber(this.max)) {
  19537. // Too many ticks (#6405). Create a friendly warning and provide two
  19538. // ticks so at least we can show the data series.
  19539. if ((!axis.ordinal || !axis.ordinal.positions) &&
  19540. ((this.max - this.min) /
  19541. this.tickInterval >
  19542. Math.max(2 * this.len, 200))) {
  19543. tickPositions = [this.min, this.max];
  19544. error(19, false, this.chart);
  19545. }
  19546. else if (axis.dateTime) {
  19547. tickPositions = axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(this.tickInterval, options.units), this.min, this.max, options.startOfWeek, axis.ordinal && axis.ordinal.positions, this.closestPointRange, true);
  19548. }
  19549. else if (axis.logarithmic) {
  19550. tickPositions = axis.logarithmic.getLogTickPositions(this.tickInterval, this.min, this.max);
  19551. }
  19552. else {
  19553. const startingTickInterval = this.tickInterval;
  19554. let adjustedTickInterval = startingTickInterval;
  19555. while (adjustedTickInterval <= startingTickInterval * 2) {
  19556. tickPositions = this.getLinearTickPositions(this.tickInterval, this.min, this.max);
  19557. // If there are more tick positions than the set tickAmount,
  19558. // increase the tickInterval and continue until it fits.
  19559. // (#17100)
  19560. if (this.tickAmount &&
  19561. tickPositions.length > this.tickAmount) {
  19562. this.tickInterval = getNormalizedTickInterval(this, adjustedTickInterval *= 1.1);
  19563. }
  19564. else {
  19565. break;
  19566. }
  19567. }
  19568. }
  19569. // Too dense ticks, keep only the first and last (#4477)
  19570. if (tickPositions.length > this.len) {
  19571. tickPositions = [
  19572. tickPositions[0],
  19573. tickPositions[tickPositions.length - 1]
  19574. ];
  19575. // Reduce doubled value (#7339)
  19576. if (tickPositions[0] === tickPositions[1]) {
  19577. tickPositions.length = 1;
  19578. }
  19579. }
  19580. // Run the tick positioner callback, that allows modifying auto tick
  19581. // positions.
  19582. if (tickPositioner) {
  19583. // Make it available to the positioner
  19584. this.tickPositions = tickPositions;
  19585. tickPositionerResult = tickPositioner.apply(axis, [this.min, this.max]);
  19586. if (tickPositionerResult) {
  19587. tickPositions = tickPositionerResult;
  19588. }
  19589. }
  19590. }
  19591. this.tickPositions = tickPositions;
  19592. // Reset min/max or remove extremes based on start/end on tick
  19593. this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
  19594. this.trimTicks(tickPositions, startOnTick, endOnTick);
  19595. if (!this.isLinked && isNumber(this.min) && isNumber(this.max)) {
  19596. // Substract half a unit (#2619, #2846, #2515, #3390), but not in
  19597. // case of multiple ticks (#6897)
  19598. if (this.single &&
  19599. tickPositions.length < 2 &&
  19600. !this.categories &&
  19601. !this.series.some((s) => (s.is('heatmap') && s.options.pointPlacement === 'between'))) {
  19602. this.min -= 0.5;
  19603. this.max += 0.5;
  19604. }
  19605. if (!tickPositionsOption && !tickPositionerResult) {
  19606. this.adjustTickAmount();
  19607. }
  19608. }
  19609. fireEvent(this, 'afterSetTickPositions');
  19610. }
  19611. /**
  19612. * Handle startOnTick and endOnTick by either adapting to padding min/max or
  19613. * rounded min/max. Also handle single data points.
  19614. *
  19615. * @private
  19616. * @function Highcharts.Axis#trimTicks
  19617. *
  19618. * @param {Array<number>} tickPositions
  19619. * TO-DO: parameter description
  19620. *
  19621. * @param {boolean} [startOnTick]
  19622. * TO-DO: parameter description
  19623. *
  19624. * @param {boolean} [endOnTick]
  19625. * TO-DO: parameter description
  19626. */
  19627. trimTicks(tickPositions, startOnTick, endOnTick) {
  19628. const roundedMin = tickPositions[0], roundedMax = tickPositions[tickPositions.length - 1], minPointOffset = (!this.isOrdinal && this.minPointOffset) || 0; // (#12716)
  19629. fireEvent(this, 'trimTicks');
  19630. if (!this.isLinked) {
  19631. if (startOnTick && roundedMin !== -Infinity) { // #6502
  19632. this.min = roundedMin;
  19633. }
  19634. else {
  19635. while (this.min - minPointOffset > tickPositions[0]) {
  19636. tickPositions.shift();
  19637. }
  19638. }
  19639. if (endOnTick) {
  19640. this.max = roundedMax;
  19641. }
  19642. else {
  19643. while (this.max + minPointOffset <
  19644. tickPositions[tickPositions.length - 1]) {
  19645. tickPositions.pop();
  19646. }
  19647. }
  19648. // If no tick are left, set one tick in the middle (#3195)
  19649. if (tickPositions.length === 0 &&
  19650. defined(roundedMin) &&
  19651. !this.options.tickPositions) {
  19652. tickPositions.push((roundedMax + roundedMin) / 2);
  19653. }
  19654. }
  19655. }
  19656. /**
  19657. * Check if there are multiple axes in the same pane.
  19658. *
  19659. * @private
  19660. * @function Highcharts.Axis#alignToOthers
  19661. *
  19662. * @return {boolean|undefined}
  19663. * True if there are other axes.
  19664. */
  19665. alignToOthers() {
  19666. const axis = this, alignedAxes = [this], options = axis.options, alignThresholds = (this.coll === 'yAxis' &&
  19667. this.chart.options.chart.alignThresholds), thresholdAlignments = [];
  19668. let hasOther;
  19669. axis.thresholdAlignment = void 0;
  19670. if ((
  19671. // Only if alignTicks or alignThresholds is true
  19672. (this.chart.options.chart.alignTicks !== false &&
  19673. options.alignTicks) || (alignThresholds)) &&
  19674. // Disabled when startOnTick or endOnTick are false (#7604)
  19675. options.startOnTick !== false &&
  19676. options.endOnTick !== false &&
  19677. // Don't try to align ticks on a log axis, they are not evenly
  19678. // spaced (#6021)
  19679. !axis.logarithmic) {
  19680. // Get a key identifying which pane the axis belongs to
  19681. const getKey = (axis) => {
  19682. const { horiz, options } = axis;
  19683. return [
  19684. horiz ? options.left : options.top,
  19685. options.width,
  19686. options.height,
  19687. options.pane
  19688. ].join(',');
  19689. };
  19690. const thisKey = getKey(this);
  19691. this.chart[this.coll].forEach(function (otherAxis) {
  19692. const { series } = otherAxis;
  19693. if (
  19694. // #4442
  19695. series.length &&
  19696. series.some((s) => s.visible) &&
  19697. otherAxis !== axis &&
  19698. getKey(otherAxis) === thisKey) {
  19699. hasOther = true; // #4201
  19700. alignedAxes.push(otherAxis);
  19701. }
  19702. });
  19703. }
  19704. if (hasOther && alignThresholds) {
  19705. // Handle alignThresholds. The `thresholdAlignments` array keeps
  19706. // records of where each axis in the group wants its threshold, from
  19707. // 0 which is on `axis.min`, to 1 which is on `axis.max`.
  19708. alignedAxes.forEach((otherAxis) => {
  19709. const threshAlign = otherAxis.getThresholdAlignment(axis);
  19710. if (isNumber(threshAlign)) {
  19711. thresholdAlignments.push(threshAlign);
  19712. }
  19713. });
  19714. // For each of the axes in the group, record the average
  19715. // `thresholdAlignment`.
  19716. const thresholdAlignment = thresholdAlignments.length > 1 ?
  19717. thresholdAlignments.reduce((sum, n) => (sum += n), 0) / thresholdAlignments.length :
  19718. void 0;
  19719. alignedAxes.forEach((axis) => {
  19720. axis.thresholdAlignment = thresholdAlignment;
  19721. });
  19722. }
  19723. return hasOther;
  19724. }
  19725. /**
  19726. * Where the axis wants its threshold, from 0 which is on `axis.min`, to 1 which
  19727. * is on `axis.max`.
  19728. *
  19729. * @private
  19730. * @function Highcharts.Axis#getThresholdAlignment
  19731. */
  19732. getThresholdAlignment(callerAxis) {
  19733. if (!isNumber(this.dataMin) ||
  19734. (this !== callerAxis &&
  19735. this.series.some((s) => (s.isDirty || s.isDirtyData)))) {
  19736. this.getSeriesExtremes();
  19737. }
  19738. if (isNumber(this.threshold)) {
  19739. let thresholdAlignment = clamp(((this.threshold - (this.dataMin || 0)) /
  19740. ((this.dataMax || 0) - (this.dataMin || 0))), 0, 1);
  19741. if (this.options.reversed) {
  19742. thresholdAlignment = 1 - thresholdAlignment;
  19743. }
  19744. return thresholdAlignment;
  19745. }
  19746. }
  19747. /**
  19748. * Find the max ticks of either the x and y axis collection, and record it
  19749. * in `this.tickAmount`.
  19750. *
  19751. * @private
  19752. * @function Highcharts.Axis#getTickAmount
  19753. */
  19754. getTickAmount() {
  19755. const axis = this, options = this.options, tickPixelInterval = options.tickPixelInterval;
  19756. let tickAmount = options.tickAmount;
  19757. if (!defined(options.tickInterval) &&
  19758. !tickAmount &&
  19759. this.len < tickPixelInterval &&
  19760. !this.isRadial &&
  19761. !axis.logarithmic &&
  19762. options.startOnTick &&
  19763. options.endOnTick) {
  19764. tickAmount = 2;
  19765. }
  19766. if (!tickAmount && this.alignToOthers()) {
  19767. // Add 1 because 4 tick intervals require 5 ticks (including first
  19768. // and last)
  19769. tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
  19770. }
  19771. // For tick amounts of 2 and 3, compute five ticks and remove the
  19772. // intermediate ones. This prevents the axis from adding ticks that are
  19773. // too far away from the data extremes.
  19774. if (tickAmount < 4) {
  19775. this.finalTickAmt = tickAmount;
  19776. tickAmount = 5;
  19777. }
  19778. this.tickAmount = tickAmount;
  19779. }
  19780. /**
  19781. * When using multiple axes, adjust the number of ticks to match the highest
  19782. * number of ticks in that group.
  19783. *
  19784. * @private
  19785. * @function Highcharts.Axis#adjustTickAmount
  19786. */
  19787. adjustTickAmount() {
  19788. const axis = this, { finalTickAmt, max, min, options, tickPositions, tickAmount, thresholdAlignment } = axis, currentTickAmount = tickPositions && tickPositions.length, threshold = pick(axis.threshold, axis.softThreshold ? 0 : null);
  19789. let len, i, tickInterval = axis.tickInterval, thresholdTickIndex;
  19790. const
  19791. // Extend the tickPositions by appending a position
  19792. append = () => tickPositions.push(correctFloat(tickPositions[tickPositions.length - 1] +
  19793. tickInterval)),
  19794. // Extend the tickPositions by prepending a position
  19795. prepend = () => tickPositions.unshift(correctFloat(tickPositions[0] - tickInterval));
  19796. // If `thresholdAlignment` is a number, it means the `alignThresholds`
  19797. // option is true. The `thresholdAlignment` is a scalar value between 0
  19798. // and 1 for where the threshold should be relative to `axis.min` and
  19799. // `axis.max`. Now that we know the tick amount, convert this to the
  19800. // tick index. Unless `thresholdAlignment` is exactly 0 or 1, avoid the
  19801. // first or last tick because that would lead to series being clipped.
  19802. if (isNumber(thresholdAlignment)) {
  19803. thresholdTickIndex = thresholdAlignment < 0.5 ?
  19804. Math.ceil(thresholdAlignment * (tickAmount - 1)) :
  19805. Math.floor(thresholdAlignment * (tickAmount - 1));
  19806. if (options.reversed) {
  19807. thresholdTickIndex = tickAmount - 1 - thresholdTickIndex;
  19808. }
  19809. }
  19810. if (axis.hasData() && isNumber(min) && isNumber(max)) { // #14769
  19811. // Adjust extremes and translation to the modified tick positions
  19812. const adjustExtremes = () => {
  19813. axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
  19814. // Do not crop when ticks are not extremes (#9841)
  19815. axis.min = options.startOnTick ?
  19816. tickPositions[0] :
  19817. Math.min(min, tickPositions[0]);
  19818. axis.max = options.endOnTick ?
  19819. tickPositions[tickPositions.length - 1] :
  19820. Math.max(max, tickPositions[tickPositions.length - 1]);
  19821. };
  19822. // When the axis is subject to the alignThresholds option. Use
  19823. // axis.threshold because the local threshold includes the
  19824. // `softThreshold`.
  19825. if (isNumber(thresholdTickIndex) && isNumber(axis.threshold)) {
  19826. // Throw away the previously computed tickPositions and start
  19827. // from scratch with only the threshold itself, then add ticks
  19828. // below the threshold first, then fill up above the threshold.
  19829. // If we are not able to fill up to axis.max, double the
  19830. // tickInterval and run again.
  19831. while (tickPositions[thresholdTickIndex] !== threshold ||
  19832. tickPositions.length !== tickAmount ||
  19833. tickPositions[0] > min ||
  19834. tickPositions[tickPositions.length - 1] < max) {
  19835. tickPositions.length = 0;
  19836. tickPositions.push(axis.threshold);
  19837. while (tickPositions.length < tickAmount) {
  19838. if (
  19839. // Start by prepending positions until the threshold
  19840. // is at the required index...
  19841. tickPositions[thresholdTickIndex] === void 0 ||
  19842. tickPositions[thresholdTickIndex] > axis.threshold) {
  19843. prepend();
  19844. }
  19845. else {
  19846. // ... then append positions until we have the
  19847. // required length
  19848. append();
  19849. }
  19850. }
  19851. // Safety vent
  19852. if (tickInterval > axis.tickInterval * 8) {
  19853. break;
  19854. }
  19855. tickInterval *= 2;
  19856. }
  19857. adjustExtremes();
  19858. }
  19859. else if (currentTickAmount < tickAmount) {
  19860. while (tickPositions.length < tickAmount) {
  19861. // Extend evenly for both sides unless we're on the
  19862. // threshold (#3965)
  19863. if (tickPositions.length % 2 || min === threshold) {
  19864. append();
  19865. }
  19866. else {
  19867. prepend();
  19868. }
  19869. }
  19870. adjustExtremes();
  19871. }
  19872. // The finalTickAmt property is set in getTickAmount
  19873. if (defined(finalTickAmt)) {
  19874. i = len = tickPositions.length;
  19875. while (i--) {
  19876. if (
  19877. // Remove every other tick
  19878. (finalTickAmt === 3 && i % 2 === 1) ||
  19879. // Remove all but first and last
  19880. (finalTickAmt <= 2 && i > 0 && i < len - 1)) {
  19881. tickPositions.splice(i, 1);
  19882. }
  19883. }
  19884. axis.finalTickAmt = void 0;
  19885. }
  19886. }
  19887. }
  19888. /**
  19889. * Set the scale based on data min and max, user set min and max or options.
  19890. *
  19891. * @private
  19892. * @function Highcharts.Axis#setScale
  19893. *
  19894. * @emits Highcharts.Axis#event:afterSetScale
  19895. */
  19896. setScale() {
  19897. const axis = this;
  19898. let isDirtyData = false, isXAxisDirty = false;
  19899. axis.series.forEach(function (series) {
  19900. isDirtyData = isDirtyData || series.isDirtyData || series.isDirty;
  19901. // When x axis is dirty, we need new data extremes for y as
  19902. // well:
  19903. isXAxisDirty = (isXAxisDirty ||
  19904. (series.xAxis && series.xAxis.isDirty) ||
  19905. false);
  19906. });
  19907. // set the new axisLength
  19908. axis.setAxisSize();
  19909. const isDirtyAxisLength = axis.len !== (axis.old && axis.old.len);
  19910. // do we really need to go through all this?
  19911. if (isDirtyAxisLength ||
  19912. isDirtyData ||
  19913. isXAxisDirty ||
  19914. axis.isLinked ||
  19915. axis.forceRedraw ||
  19916. axis.userMin !== (axis.old && axis.old.userMin) ||
  19917. axis.userMax !== (axis.old && axis.old.userMax) ||
  19918. axis.alignToOthers()) {
  19919. if (axis.stacking) {
  19920. axis.stacking.resetStacks();
  19921. axis.stacking.buildStacks();
  19922. }
  19923. axis.forceRedraw = false;
  19924. // #18066 delete minRange property to ensure that it will be
  19925. // calculated again after dirty data in series
  19926. if (!axis.userMinRange) {
  19927. axis.minRange = void 0;
  19928. }
  19929. // get data extremes if needed
  19930. axis.getSeriesExtremes();
  19931. // get fixed positions based on tickInterval
  19932. axis.setTickInterval();
  19933. // Mark as dirty if it is not already set to dirty and extremes have
  19934. // changed. #595.
  19935. if (!axis.isDirty) {
  19936. axis.isDirty =
  19937. isDirtyAxisLength ||
  19938. axis.min !== (axis.old && axis.old.min) ||
  19939. axis.max !== (axis.old && axis.old.max);
  19940. }
  19941. }
  19942. else if (axis.stacking) {
  19943. axis.stacking.cleanStacks();
  19944. }
  19945. // Recalculate panning state object, when the data
  19946. // has changed. It is required when vertical panning is enabled.
  19947. if (isDirtyData && axis.panningState) {
  19948. axis.panningState.isDirty = true;
  19949. }
  19950. fireEvent(this, 'afterSetScale');
  19951. }
  19952. /**
  19953. * Set the minimum and maximum of the axes after render time. If the
  19954. * `startOnTick` and `endOnTick` options are true, the minimum and maximum
  19955. * values are rounded off to the nearest tick. To prevent this, these
  19956. * options can be set to false before calling setExtremes. Also, setExtremes
  19957. * will not allow a range lower than the `minRange` option, which by default
  19958. * is the range of five points.
  19959. *
  19960. * @sample highcharts/members/axis-setextremes/
  19961. * Set extremes from a button
  19962. * @sample highcharts/members/axis-setextremes-datetime/
  19963. * Set extremes on a datetime axis
  19964. * @sample highcharts/members/axis-setextremes-off-ticks/
  19965. * Set extremes off ticks
  19966. * @sample stock/members/axis-setextremes/
  19967. * Set extremes in Highcharts Stock
  19968. *
  19969. * @function Highcharts.Axis#setExtremes
  19970. *
  19971. * @param {number} [newMin]
  19972. * The new minimum value.
  19973. *
  19974. * @param {number} [newMax]
  19975. * The new maximum value.
  19976. *
  19977. * @param {boolean} [redraw=true]
  19978. * Whether to redraw the chart or wait for an explicit call to
  19979. * {@link Highcharts.Chart#redraw}
  19980. *
  19981. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  19982. * Enable or modify animations.
  19983. *
  19984. * @param {*} [eventArguments]
  19985. * Arguments to be accessed in event handler.
  19986. *
  19987. * @emits Highcharts.Axis#event:setExtremes
  19988. */
  19989. setExtremes(newMin, newMax, redraw, animation, eventArguments) {
  19990. const axis = this, chart = axis.chart;
  19991. redraw = pick(redraw, true); // defaults to true
  19992. axis.series.forEach(function (serie) {
  19993. delete serie.kdTree;
  19994. });
  19995. // Extend the arguments with min and max
  19996. eventArguments = extend(eventArguments, {
  19997. min: newMin,
  19998. max: newMax
  19999. });
  20000. // Fire the event
  20001. fireEvent(axis, 'setExtremes', eventArguments, function () {
  20002. axis.userMin = newMin;
  20003. axis.userMax = newMax;
  20004. axis.eventArgs = eventArguments;
  20005. if (redraw) {
  20006. chart.redraw(animation);
  20007. }
  20008. });
  20009. }
  20010. /**
  20011. * Overridable method for zooming chart. Pulled out in a separate method to
  20012. * allow overriding in stock charts.
  20013. *
  20014. * @private
  20015. * @function Highcharts.Axis#zoom
  20016. */
  20017. zoom(newMin, newMax) {
  20018. const axis = this, dataMin = this.dataMin, dataMax = this.dataMax, options = this.options, min = Math.min(dataMin, pick(options.min, dataMin)), max = Math.max(dataMax, pick(options.max, dataMax)), evt = {
  20019. newMin: newMin,
  20020. newMax: newMax
  20021. };
  20022. fireEvent(this, 'zoom', evt, function (e) {
  20023. // Use e.newMin and e.newMax - event handlers may have altered them
  20024. let newMin = e.newMin, newMax = e.newMax;
  20025. if (newMin !== axis.min || newMax !== axis.max) { // #5790
  20026. // Prevent pinch zooming out of range. Check for defined is for
  20027. // #1946. #1734.
  20028. if (!axis.allowZoomOutside) {
  20029. // #6014, sometimes newMax will be smaller than min (or
  20030. // newMin will be larger than max).
  20031. if (defined(dataMin)) {
  20032. if (newMin < min) {
  20033. newMin = min;
  20034. }
  20035. if (newMin > max) {
  20036. newMin = max;
  20037. }
  20038. }
  20039. if (defined(dataMax)) {
  20040. if (newMax < min) {
  20041. newMax = min;
  20042. }
  20043. if (newMax > max) {
  20044. newMax = max;
  20045. }
  20046. }
  20047. }
  20048. // In full view, displaying the reset zoom button is not
  20049. // required
  20050. axis.displayBtn = (typeof newMin !== 'undefined' ||
  20051. typeof newMax !== 'undefined');
  20052. // Do it
  20053. axis.setExtremes(newMin, newMax, false, void 0, { trigger: 'zoom' });
  20054. }
  20055. e.zoomed = true;
  20056. });
  20057. return evt.zoomed;
  20058. }
  20059. /**
  20060. * Update the axis metrics.
  20061. *
  20062. * @private
  20063. * @function Highcharts.Axis#setAxisSize
  20064. */
  20065. setAxisSize() {
  20066. const chart = this.chart, options = this.options,
  20067. // [top, right, bottom, left]
  20068. offsets = options.offsets || [0, 0, 0, 0], horiz = this.horiz,
  20069. // Check for percentage based input values. Rounding fixes problems
  20070. // with column overflow and plot line filtering (#4898, #4899)
  20071. width = this.width = Math.round(relativeLength(pick(options.width, chart.plotWidth - offsets[3] + offsets[1]), chart.plotWidth)), height = this.height = Math.round(relativeLength(pick(options.height, chart.plotHeight - offsets[0] + offsets[2]), chart.plotHeight)), top = this.top = Math.round(relativeLength(pick(options.top, chart.plotTop + offsets[0]), chart.plotHeight, chart.plotTop)), left = this.left = Math.round(relativeLength(pick(options.left, chart.plotLeft + offsets[3]), chart.plotWidth, chart.plotLeft));
  20072. // Expose basic values to use in Series object and navigator
  20073. this.bottom = chart.chartHeight - height - top;
  20074. this.right = chart.chartWidth - width - left;
  20075. // Direction agnostic properties
  20076. this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
  20077. this.pos = horiz ? left : top; // distance from SVG origin
  20078. }
  20079. /**
  20080. * Get the current extremes for the axis.
  20081. *
  20082. * @sample highcharts/members/axis-getextremes/
  20083. * Report extremes by click on a button
  20084. *
  20085. * @function Highcharts.Axis#getExtremes
  20086. *
  20087. * @return {Highcharts.ExtremesObject}
  20088. * An object containing extremes information.
  20089. */
  20090. getExtremes() {
  20091. const axis = this, log = axis.logarithmic;
  20092. return {
  20093. min: log ?
  20094. correctFloat(log.lin2log(axis.min)) :
  20095. axis.min,
  20096. max: log ?
  20097. correctFloat(log.lin2log(axis.max)) :
  20098. axis.max,
  20099. dataMin: axis.dataMin,
  20100. dataMax: axis.dataMax,
  20101. userMin: axis.userMin,
  20102. userMax: axis.userMax
  20103. };
  20104. }
  20105. /**
  20106. * Get the zero plane either based on zero or on the min or max value.
  20107. * Used in bar and area plots.
  20108. *
  20109. * @function Highcharts.Axis#getThreshold
  20110. *
  20111. * @param {number} threshold
  20112. * The threshold in axis values.
  20113. *
  20114. * @return {number}
  20115. * The translated threshold position in terms of pixels, and corrected to
  20116. * stay within the axis bounds.
  20117. */
  20118. getThreshold(threshold) {
  20119. const axis = this, log = axis.logarithmic, realMin = log ? log.lin2log(axis.min) : axis.min, realMax = log ? log.lin2log(axis.max) : axis.max;
  20120. if (threshold === null || threshold === -Infinity) {
  20121. threshold = realMin;
  20122. }
  20123. else if (threshold === Infinity) {
  20124. threshold = realMax;
  20125. }
  20126. else if (realMin > threshold) {
  20127. threshold = realMin;
  20128. }
  20129. else if (realMax < threshold) {
  20130. threshold = realMax;
  20131. }
  20132. return axis.translate(threshold, 0, 1, 0, 1);
  20133. }
  20134. /**
  20135. * Compute auto alignment for the axis label based on which side the axis is
  20136. * on and the given rotation for the label.
  20137. *
  20138. * @private
  20139. * @function Highcharts.Axis#autoLabelAlign
  20140. *
  20141. * @param {number} rotation
  20142. * The rotation in degrees as set by either the `rotation` or `autoRotation`
  20143. * options.
  20144. *
  20145. * @return {Highcharts.AlignValue}
  20146. * Can be `"center"`, `"left"` or `"right"`.
  20147. */
  20148. autoLabelAlign(rotation) {
  20149. const angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360, evt = { align: 'center' };
  20150. fireEvent(this, 'autoLabelAlign', evt, function (e) {
  20151. if (angle > 15 && angle < 165) {
  20152. e.align = 'right';
  20153. }
  20154. else if (angle > 195 && angle < 345) {
  20155. e.align = 'left';
  20156. }
  20157. });
  20158. return evt.align;
  20159. }
  20160. /**
  20161. * Get the tick length and width for the axis based on axis options.
  20162. *
  20163. * @private
  20164. * @function Highcharts.Axis#tickSize
  20165. *
  20166. * @param {string} [prefix]
  20167. * 'tick' or 'minorTick'
  20168. *
  20169. * @return {Array<number,number>|undefined}
  20170. * An array of tickLength and tickWidth
  20171. */
  20172. tickSize(prefix) {
  20173. const options = this.options, tickWidth = pick(options[prefix === 'tick' ? 'tickWidth' : 'minorTickWidth'],
  20174. // Default to 1 on linear and datetime X axes
  20175. prefix === 'tick' && this.isXAxis && !this.categories ? 1 : 0);
  20176. let tickLength = options[prefix === 'tick' ? 'tickLength' : 'minorTickLength'], tickSize;
  20177. if (tickWidth && tickLength) {
  20178. // Negate the length
  20179. if (options[prefix + 'Position'] === 'inside') {
  20180. tickLength = -tickLength;
  20181. }
  20182. tickSize = [tickLength, tickWidth];
  20183. }
  20184. const e = { tickSize };
  20185. fireEvent(this, 'afterTickSize', e);
  20186. return e.tickSize;
  20187. }
  20188. /**
  20189. * Return the size of the labels.
  20190. *
  20191. * @private
  20192. * @function Highcharts.Axis#labelMetrics
  20193. */
  20194. labelMetrics() {
  20195. const renderer = this.chart.renderer, ticks = this.ticks, tick = ticks[Object.keys(ticks)[0]] || {};
  20196. return this.chart.renderer.fontMetrics(tick.label ||
  20197. tick.movedLabel ||
  20198. renderer.box);
  20199. }
  20200. /**
  20201. * Prevent the ticks from getting so close we can't draw the labels. On a
  20202. * horizontal axis, this is handled by rotating the labels, removing ticks
  20203. * and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
  20204. *
  20205. * @private
  20206. * @function Highcharts.Axis#unsquish
  20207. */
  20208. unsquish() {
  20209. const labelOptions = this.options.labels, horiz = this.horiz, tickInterval = this.tickInterval, slotSize = this.len / (((this.categories ? 1 : 0) +
  20210. this.max -
  20211. this.min) /
  20212. tickInterval), rotationOption = labelOptions.rotation,
  20213. // We don't know the actual rendered line height at this point, but
  20214. // it defaults to 0.75em
  20215. lineHeight = this.labelMetrics().h * 0.75, range = Math.max(this.max - this.min, 0),
  20216. // Return the multiple of tickInterval that is needed to avoid
  20217. // collision
  20218. getStep = function (spaceNeeded) {
  20219. let step = spaceNeeded / (slotSize || 1);
  20220. step = step > 1 ? Math.ceil(step) : 1;
  20221. // Guard for very small or negative angles (#9835)
  20222. if (step * tickInterval > range &&
  20223. spaceNeeded !== Infinity &&
  20224. slotSize !== Infinity &&
  20225. range) {
  20226. step = Math.ceil(range / tickInterval);
  20227. }
  20228. return correctFloat(step * tickInterval);
  20229. };
  20230. let newTickInterval = tickInterval, rotation, bestScore = Number.MAX_VALUE, autoRotation;
  20231. if (horiz) {
  20232. if (!labelOptions.staggerLines) {
  20233. if (isNumber(rotationOption)) {
  20234. autoRotation = [rotationOption];
  20235. }
  20236. else if (slotSize < labelOptions.autoRotationLimit) {
  20237. autoRotation = labelOptions.autoRotation;
  20238. }
  20239. }
  20240. if (autoRotation) {
  20241. let step, score;
  20242. // Loop over the given autoRotation options, and determine which
  20243. // gives the best score. The best score is that with the lowest
  20244. // number of steps and a rotation closest to horizontal.
  20245. for (const rot of autoRotation) {
  20246. if (rot === rotationOption ||
  20247. (rot && rot >= -90 && rot <= 90)) { // #3891
  20248. step = getStep(Math.abs(lineHeight / Math.sin(deg2rad * rot)));
  20249. score = step + Math.abs(rot / 360);
  20250. if (score < bestScore) {
  20251. bestScore = score;
  20252. rotation = rot;
  20253. newTickInterval = step;
  20254. }
  20255. }
  20256. }
  20257. }
  20258. }
  20259. else { // #4411
  20260. newTickInterval = getStep(lineHeight);
  20261. }
  20262. this.autoRotation = autoRotation;
  20263. this.labelRotation = pick(rotation, isNumber(rotationOption) ? rotationOption : 0);
  20264. return labelOptions.step ? tickInterval : newTickInterval;
  20265. }
  20266. /**
  20267. * Get the general slot width for labels/categories on this axis. This may
  20268. * change between the pre-render (from Axis.getOffset) and the final tick
  20269. * rendering and placement.
  20270. *
  20271. * @private
  20272. * @function Highcharts.Axis#getSlotWidth
  20273. *
  20274. * @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
  20275. * basing on tick label. It is used in highcharts-3d module, where the slots
  20276. * has different widths depending on perspective angles.
  20277. *
  20278. * @return {number}
  20279. * The pixel width allocated to each axis label.
  20280. */
  20281. getSlotWidth(tick) {
  20282. // #5086, #1580, #1931
  20283. const chart = this.chart, horiz = this.horiz, labelOptions = this.options.labels, slotCount = Math.max(this.tickPositions.length - (this.categories ? 0 : 1), 1), marginLeft = chart.margin[3];
  20284. // Used by grid axis
  20285. if (tick && isNumber(tick.slotWidth)) { // #13221, can be 0
  20286. return tick.slotWidth;
  20287. }
  20288. if (horiz && labelOptions.step < 2) {
  20289. if (labelOptions.rotation) { // #4415
  20290. return 0;
  20291. }
  20292. return ((this.staggerLines || 1) * this.len) / slotCount;
  20293. }
  20294. if (!horiz) {
  20295. // #7028
  20296. const cssWidth = labelOptions.style.width;
  20297. if (cssWidth !== void 0) {
  20298. return parseInt(String(cssWidth), 10);
  20299. }
  20300. if (marginLeft) {
  20301. return marginLeft - chart.spacing[3];
  20302. }
  20303. }
  20304. // Last resort, a fraction of the available size
  20305. return chart.chartWidth * 0.33;
  20306. }
  20307. /**
  20308. * Render the axis labels and determine whether ellipsis or rotation need to
  20309. * be applied.
  20310. *
  20311. * @private
  20312. * @function Highcharts.Axis#renderUnsquish
  20313. */
  20314. renderUnsquish() {
  20315. const chart = this.chart, renderer = chart.renderer, tickPositions = this.tickPositions, ticks = this.ticks, labelOptions = this.options.labels, labelStyleOptions = labelOptions.style, horiz = this.horiz, slotWidth = this.getSlotWidth(), innerWidth = Math.max(1, Math.round(slotWidth - 2 * labelOptions.padding)), attr = {}, labelMetrics = this.labelMetrics(), textOverflowOption = labelStyleOptions.textOverflow;
  20316. let commonWidth, commonTextOverflow, maxLabelLength = 0, label, i, pos;
  20317. // Set rotation option unless it is "auto", like in gauges
  20318. if (!isString(labelOptions.rotation)) {
  20319. // #4443
  20320. attr.rotation = labelOptions.rotation || 0;
  20321. }
  20322. // Get the longest label length
  20323. tickPositions.forEach(function (tickPosition) {
  20324. const tick = ticks[tickPosition];
  20325. // Replace label - sorting animation
  20326. if (tick.movedLabel) {
  20327. tick.replaceMovedLabel();
  20328. }
  20329. if (tick &&
  20330. tick.label &&
  20331. tick.label.textPxLength > maxLabelLength) {
  20332. maxLabelLength = tick.label.textPxLength;
  20333. }
  20334. });
  20335. this.maxLabelLength = maxLabelLength;
  20336. // Handle auto rotation on horizontal axis
  20337. if (this.autoRotation) {
  20338. // Apply rotation only if the label is too wide for the slot, and
  20339. // the label is wider than its height.
  20340. if (maxLabelLength > innerWidth &&
  20341. maxLabelLength > labelMetrics.h) {
  20342. attr.rotation = this.labelRotation;
  20343. }
  20344. else {
  20345. this.labelRotation = 0;
  20346. }
  20347. // Handle word-wrap or ellipsis on vertical axis
  20348. }
  20349. else if (slotWidth) {
  20350. // For word-wrap or ellipsis
  20351. commonWidth = innerWidth;
  20352. if (!textOverflowOption) {
  20353. commonTextOverflow = 'clip';
  20354. // On vertical axis, only allow word wrap if there is room
  20355. // for more lines.
  20356. i = tickPositions.length;
  20357. while (!horiz && i--) {
  20358. pos = tickPositions[i];
  20359. label = ticks[pos].label;
  20360. if (label) {
  20361. // Reset ellipsis in order to get the correct
  20362. // bounding box (#4070)
  20363. if (label.styles &&
  20364. label.styles.textOverflow === 'ellipsis') {
  20365. label.css({ textOverflow: 'clip' });
  20366. // Set the correct width in order to read
  20367. // the bounding box height (#4678, #5034)
  20368. }
  20369. else if (label.textPxLength > slotWidth) {
  20370. label.css({ width: slotWidth + 'px' });
  20371. }
  20372. if (label.getBBox().height > (this.len / tickPositions.length -
  20373. (labelMetrics.h - labelMetrics.f))) {
  20374. label.specificTextOverflow = 'ellipsis';
  20375. }
  20376. }
  20377. }
  20378. }
  20379. }
  20380. // Add ellipsis if the label length is significantly longer than ideal
  20381. if (attr.rotation) {
  20382. commonWidth = (maxLabelLength > chart.chartHeight * 0.5 ?
  20383. chart.chartHeight * 0.33 :
  20384. maxLabelLength);
  20385. if (!textOverflowOption) {
  20386. commonTextOverflow = 'ellipsis';
  20387. }
  20388. }
  20389. // Set the explicit or automatic label alignment
  20390. this.labelAlign = labelOptions.align ||
  20391. this.autoLabelAlign(this.labelRotation);
  20392. if (this.labelAlign) {
  20393. attr.align = this.labelAlign;
  20394. }
  20395. // Apply general and specific CSS
  20396. tickPositions.forEach(function (pos) {
  20397. const tick = ticks[pos], label = tick && tick.label, widthOption = labelStyleOptions.width, css = {};
  20398. if (label) {
  20399. // This needs to go before the CSS in old IE (#4502)
  20400. label.attr(attr);
  20401. if (tick.shortenLabel) {
  20402. tick.shortenLabel();
  20403. }
  20404. else if (commonWidth &&
  20405. !widthOption &&
  20406. // Setting width in this case messes with the bounding box
  20407. // (#7975)
  20408. labelStyleOptions.whiteSpace !== 'nowrap' &&
  20409. (
  20410. // Speed optimizing, #7656
  20411. commonWidth < label.textPxLength ||
  20412. // Resetting CSS, #4928
  20413. label.element.tagName === 'SPAN')) {
  20414. css.width = commonWidth + 'px';
  20415. if (!textOverflowOption) {
  20416. css.textOverflow = (label.specificTextOverflow ||
  20417. commonTextOverflow);
  20418. }
  20419. label.css(css);
  20420. // Reset previously shortened label (#8210)
  20421. }
  20422. else if (label.styles &&
  20423. label.styles.width &&
  20424. !css.width &&
  20425. !widthOption) {
  20426. label.css({ width: null });
  20427. }
  20428. delete label.specificTextOverflow;
  20429. tick.rotation = attr.rotation;
  20430. }
  20431. }, this);
  20432. // Note: Why is this not part of getLabelPosition?
  20433. this.tickRotCorr = renderer.rotCorr(labelMetrics.b, this.labelRotation || 0, this.side !== 0);
  20434. }
  20435. /**
  20436. * Return true if the axis has associated data.
  20437. *
  20438. * @function Highcharts.Axis#hasData
  20439. *
  20440. * @return {boolean}
  20441. * True if the axis has associated visible series and those series have
  20442. * either valid data points or explicit `min` and `max` settings.
  20443. */
  20444. hasData() {
  20445. return this.series.some(function (s) {
  20446. return s.hasData();
  20447. }) ||
  20448. (this.options.showEmpty &&
  20449. defined(this.min) &&
  20450. defined(this.max));
  20451. }
  20452. /**
  20453. * Adds the title defined in axis.options.title.
  20454. *
  20455. * @function Highcharts.Axis#addTitle
  20456. *
  20457. * @param {boolean} [display]
  20458. * Whether or not to display the title.
  20459. */
  20460. addTitle(display) {
  20461. const axis = this, renderer = axis.chart.renderer, horiz = axis.horiz, opposite = axis.opposite, options = axis.options, axisTitleOptions = options.title, styledMode = axis.chart.styledMode;
  20462. let textAlign;
  20463. if (!axis.axisTitle) {
  20464. textAlign = axisTitleOptions.textAlign;
  20465. if (!textAlign) {
  20466. textAlign = (horiz ? {
  20467. low: 'left',
  20468. middle: 'center',
  20469. high: 'right'
  20470. } : {
  20471. low: opposite ? 'right' : 'left',
  20472. middle: 'center',
  20473. high: opposite ? 'left' : 'right'
  20474. })[axisTitleOptions.align];
  20475. }
  20476. axis.axisTitle = renderer
  20477. .text(axisTitleOptions.text || '', 0, 0, axisTitleOptions.useHTML)
  20478. .attr({
  20479. zIndex: 7,
  20480. rotation: axisTitleOptions.rotation,
  20481. align: textAlign
  20482. })
  20483. .addClass('highcharts-axis-title');
  20484. // #7814, don't mutate style option
  20485. if (!styledMode) {
  20486. axis.axisTitle.css(merge(axisTitleOptions.style));
  20487. }
  20488. axis.axisTitle.add(axis.axisGroup);
  20489. axis.axisTitle.isNew = true;
  20490. }
  20491. // Max width defaults to the length of the axis
  20492. if (!styledMode &&
  20493. !axisTitleOptions.style.width &&
  20494. !axis.isRadial) {
  20495. axis.axisTitle.css({
  20496. width: axis.len + 'px'
  20497. });
  20498. }
  20499. // hide or show the title depending on whether showEmpty is set
  20500. axis.axisTitle[display ? 'show' : 'hide'](display);
  20501. }
  20502. /**
  20503. * Generates a tick for initial positioning.
  20504. *
  20505. * @private
  20506. * @function Highcharts.Axis#generateTick
  20507. *
  20508. * @param {number} pos
  20509. * The tick position in axis values.
  20510. *
  20511. * @param {number} [i]
  20512. * The index of the tick in {@link Axis.tickPositions}.
  20513. */
  20514. generateTick(pos) {
  20515. const axis = this, ticks = axis.ticks;
  20516. if (!ticks[pos]) {
  20517. ticks[pos] = new Tick(axis, pos);
  20518. }
  20519. else {
  20520. ticks[pos].addLabel(); // update labels depending on tick interval
  20521. }
  20522. }
  20523. /**
  20524. * Render the tick labels to a preliminary position to get their sizes
  20525. *
  20526. * @private
  20527. * @function Highcharts.Axis#getOffset
  20528. *
  20529. * @emits Highcharts.Axis#event:afterGetOffset
  20530. */
  20531. getOffset() {
  20532. const axis = this, { chart, horiz, options, side, ticks, tickPositions, coll, axisParent // Used in color axis
  20533. } = axis, renderer = chart.renderer, invertedSide = (chart.inverted && !axis.isZAxis ?
  20534. [1, 0, 3, 2][side] :
  20535. side), hasData = axis.hasData(), axisTitleOptions = options.title, labelOptions = options.labels, hasCrossing = isNumber(options.crossing), axisOffset = chart.axisOffset, clipOffset = chart.clipOffset, directionFactor = [-1, 1, 1, -1][side], className = options.className;
  20536. let showAxis, titleOffset = 0, titleOffsetOption, titleMargin = 0, labelOffset = 0, // reset
  20537. labelOffsetPadded, lineHeightCorrection;
  20538. // For reuse in Axis.render
  20539. axis.showAxis = showAxis = hasData || options.showEmpty;
  20540. // Set/reset staggerLines
  20541. axis.staggerLines = (axis.horiz && labelOptions.staggerLines) || void 0;
  20542. // Create the axisGroup and gridGroup elements on first iteration
  20543. if (!axis.axisGroup) {
  20544. const createGroup = (name, suffix, zIndex) => renderer.g(name)
  20545. .attr({ zIndex })
  20546. .addClass(`highcharts-${coll.toLowerCase()}${suffix} ` +
  20547. (this.isRadial ? `highcharts-radial-axis${suffix} ` : '') +
  20548. (className || ''))
  20549. .add(axisParent);
  20550. axis.gridGroup = createGroup('grid', '-grid', options.gridZIndex);
  20551. axis.axisGroup = createGroup('axis', '', options.zIndex);
  20552. axis.labelGroup = createGroup('axis-labels', '-labels', labelOptions.zIndex);
  20553. }
  20554. if (hasData || axis.isLinked) {
  20555. // Generate ticks
  20556. tickPositions.forEach(function (pos) {
  20557. // i is not used here, but may be used in overrides
  20558. axis.generateTick(pos);
  20559. });
  20560. axis.renderUnsquish();
  20561. // Left side must be align: right and right side must
  20562. // have align: left for labels
  20563. axis.reserveSpaceDefault = (side === 0 ||
  20564. side === 2 ||
  20565. { 1: 'left', 3: 'right' }[side] === axis.labelAlign);
  20566. if (pick(labelOptions.reserveSpace, hasCrossing ? false : null, axis.labelAlign === 'center' ? true : null, axis.reserveSpaceDefault)) {
  20567. tickPositions.forEach(function (pos) {
  20568. // get the highest offset
  20569. labelOffset = Math.max(ticks[pos].getLabelSize(), labelOffset);
  20570. });
  20571. }
  20572. if (axis.staggerLines) {
  20573. labelOffset *= axis.staggerLines;
  20574. }
  20575. axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
  20576. }
  20577. else { // doesn't have data
  20578. objectEach(ticks, function (tick, n) {
  20579. tick.destroy();
  20580. delete ticks[n];
  20581. });
  20582. }
  20583. if (axisTitleOptions &&
  20584. axisTitleOptions.text &&
  20585. axisTitleOptions.enabled !== false) {
  20586. axis.addTitle(showAxis);
  20587. if (showAxis &&
  20588. !hasCrossing &&
  20589. axisTitleOptions.reserveSpace !== false) {
  20590. axis.titleOffset = titleOffset =
  20591. axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
  20592. titleOffsetOption = axisTitleOptions.offset;
  20593. titleMargin = defined(titleOffsetOption) ?
  20594. 0 :
  20595. pick(axisTitleOptions.margin, horiz ? 5 : 10);
  20596. }
  20597. }
  20598. // Render the axis line
  20599. axis.renderLine();
  20600. // handle automatic or user set offset
  20601. axis.offset = directionFactor * pick(options.offset, axisOffset[side] ? axisOffset[side] + (options.margin || 0) : 0);
  20602. axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
  20603. if (side === 0) {
  20604. lineHeightCorrection = -axis.labelMetrics().h;
  20605. }
  20606. else if (side === 2) {
  20607. lineHeightCorrection = axis.tickRotCorr.y;
  20608. }
  20609. else {
  20610. lineHeightCorrection = 0;
  20611. }
  20612. // Find the padded label offset
  20613. labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
  20614. if (labelOffset) {
  20615. labelOffsetPadded -= lineHeightCorrection;
  20616. labelOffsetPadded += directionFactor * (horiz ?
  20617. pick(labelOptions.y, axis.tickRotCorr.y +
  20618. directionFactor * labelOptions.distance) :
  20619. pick(labelOptions.x, directionFactor * labelOptions.distance));
  20620. }
  20621. axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
  20622. if (axis.getMaxLabelDimensions) {
  20623. axis.maxLabelDimensions = axis.getMaxLabelDimensions(ticks, tickPositions);
  20624. }
  20625. // Due to GridAxis.tickSize, tickSize should be calculated after ticks
  20626. // has rendered.
  20627. if (coll !== 'colorAxis') {
  20628. const tickSize = this.tickSize('tick');
  20629. axisOffset[side] = Math.max(axisOffset[side], (axis.axisTitleMargin || 0) + titleOffset +
  20630. directionFactor * axis.offset, labelOffsetPadded, // #3027
  20631. tickPositions && tickPositions.length && tickSize ?
  20632. tickSize[0] + directionFactor * axis.offset :
  20633. 0 // #4866
  20634. );
  20635. // Decide the clipping needed to keep the graph inside
  20636. // the plot area and axis lines
  20637. const clip = !axis.axisLine || options.offset ?
  20638. 0 :
  20639. // #4308, #4371:
  20640. Math.floor(axis.axisLine.strokeWidth() / 2) * 2;
  20641. clipOffset[invertedSide] =
  20642. Math.max(clipOffset[invertedSide], clip);
  20643. }
  20644. fireEvent(this, 'afterGetOffset');
  20645. }
  20646. /**
  20647. * Internal function to get the path for the axis line. Extended for polar
  20648. * charts.
  20649. *
  20650. * @function Highcharts.Axis#getLinePath
  20651. *
  20652. * @param {number} lineWidth
  20653. * The line width in pixels.
  20654. *
  20655. * @return {Highcharts.SVGPathArray}
  20656. * The SVG path definition in array form.
  20657. */
  20658. getLinePath(lineWidth) {
  20659. const chart = this.chart, opposite = this.opposite, offset = this.offset, horiz = this.horiz, lineLeft = this.left + (opposite ? this.width : 0) + offset, lineTop = chart.chartHeight - this.bottom -
  20660. (opposite ? this.height : 0) + offset;
  20661. if (opposite) {
  20662. lineWidth *= -1; // crispify the other way - #1480, #1687
  20663. }
  20664. return chart.renderer
  20665. .crispLine([
  20666. [
  20667. 'M',
  20668. horiz ?
  20669. this.left :
  20670. lineLeft,
  20671. horiz ?
  20672. lineTop :
  20673. this.top
  20674. ],
  20675. [
  20676. 'L',
  20677. horiz ?
  20678. chart.chartWidth - this.right :
  20679. lineLeft,
  20680. horiz ?
  20681. lineTop :
  20682. chart.chartHeight - this.bottom
  20683. ]
  20684. ], lineWidth);
  20685. }
  20686. /**
  20687. * Render the axis line. Called internally when rendering and redrawing the
  20688. * axis.
  20689. *
  20690. * @function Highcharts.Axis#renderLine
  20691. */
  20692. renderLine() {
  20693. if (!this.axisLine) {
  20694. this.axisLine = this.chart.renderer.path()
  20695. .addClass('highcharts-axis-line')
  20696. .add(this.axisGroup);
  20697. if (!this.chart.styledMode) {
  20698. this.axisLine.attr({
  20699. stroke: this.options.lineColor,
  20700. 'stroke-width': this.options.lineWidth,
  20701. zIndex: 7
  20702. });
  20703. }
  20704. }
  20705. }
  20706. /**
  20707. * Position the axis title.
  20708. *
  20709. * @private
  20710. * @function Highcharts.Axis#getTitlePosition
  20711. *
  20712. * @return {Highcharts.PositionObject}
  20713. * X and Y positions for the title.
  20714. */
  20715. getTitlePosition(axisTitle) {
  20716. // compute anchor points for each of the title align options
  20717. const horiz = this.horiz, axisLeft = this.left, axisTop = this.top, axisLength = this.len, axisTitleOptions = this.options.title, margin = horiz ? axisLeft : axisTop, opposite = this.opposite, offset = this.offset, xOption = axisTitleOptions.x, yOption = axisTitleOptions.y, fontMetrics = this.chart.renderer.fontMetrics(axisTitle),
  20718. // The part of a multiline text that is below the baseline of the
  20719. // first line. Subtract 1 to preserve pixel-perfectness from the
  20720. // old behaviour (v5.0.12), where only one line was allowed.
  20721. textHeightOvershoot = axisTitle ? Math.max(axisTitle.getBBox(false, 0).height - fontMetrics.h - 1, 0) : 0,
  20722. // the position in the length direction of the axis
  20723. alongAxis = ({
  20724. low: margin + (horiz ? 0 : axisLength),
  20725. middle: margin + axisLength / 2,
  20726. high: margin + (horiz ? axisLength : 0)
  20727. })[axisTitleOptions.align],
  20728. // the position in the perpendicular direction of the axis
  20729. offAxis = (horiz ? axisTop + this.height : axisLeft) +
  20730. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  20731. (opposite ? -1 : 1) * // so does opposite axes
  20732. (this.axisTitleMargin || 0) +
  20733. [
  20734. -textHeightOvershoot,
  20735. textHeightOvershoot,
  20736. fontMetrics.f,
  20737. -textHeightOvershoot // left
  20738. ][this.side], titlePosition = {
  20739. x: horiz ?
  20740. alongAxis + xOption :
  20741. offAxis + (opposite ? this.width : 0) + offset + xOption,
  20742. y: horiz ?
  20743. offAxis + yOption - (opposite ? this.height : 0) + offset :
  20744. alongAxis + yOption
  20745. };
  20746. fireEvent(this, 'afterGetTitlePosition', { titlePosition: titlePosition });
  20747. return titlePosition;
  20748. }
  20749. /**
  20750. * Render a minor tick into the given position. If a minor tick already
  20751. * exists in this position, move it.
  20752. *
  20753. * @function Highcharts.Axis#renderMinorTick
  20754. *
  20755. * @param {number} pos
  20756. * The position in axis values.
  20757. *
  20758. * @param {boolean} slideIn
  20759. * Whether the tick should animate in from last computed position
  20760. */
  20761. renderMinorTick(pos, slideIn) {
  20762. const axis = this;
  20763. const minorTicks = axis.minorTicks;
  20764. if (!minorTicks[pos]) {
  20765. minorTicks[pos] = new Tick(axis, pos, 'minor');
  20766. }
  20767. // Render new ticks in old position
  20768. if (slideIn && minorTicks[pos].isNew) {
  20769. minorTicks[pos].render(null, true);
  20770. }
  20771. minorTicks[pos].render(null, false, 1);
  20772. }
  20773. /**
  20774. * Render a major tick into the given position. If a tick already exists
  20775. * in this position, move it.
  20776. *
  20777. * @function Highcharts.Axis#renderTick
  20778. *
  20779. * @param {number} pos
  20780. * The position in axis values.
  20781. *
  20782. * @param {number} i
  20783. * The tick index.
  20784. *
  20785. * @param {boolean} slideIn
  20786. * Whether the tick should animate in from last computed position
  20787. */
  20788. renderTick(pos, i, slideIn) {
  20789. const axis = this, isLinked = axis.isLinked, ticks = axis.ticks;
  20790. // Linked axes need an extra check to find out if
  20791. if (!isLinked ||
  20792. (pos >= axis.min && pos <= axis.max) ||
  20793. (axis.grid && axis.grid.isColumn)) {
  20794. if (!ticks[pos]) {
  20795. ticks[pos] = new Tick(axis, pos);
  20796. }
  20797. // NOTE this seems like overkill. Could be handled in tick.render by
  20798. // setting old position in attr, then set new position in animate.
  20799. // render new ticks in old position
  20800. if (slideIn && ticks[pos].isNew) {
  20801. // Start with negative opacity so that it is visible from
  20802. // halfway into the animation
  20803. ticks[pos].render(i, true, -1);
  20804. }
  20805. ticks[pos].render(i);
  20806. }
  20807. }
  20808. /**
  20809. * Render the axis.
  20810. *
  20811. * @private
  20812. * @function Highcharts.Axis#render
  20813. *
  20814. * @emits Highcharts.Axis#event:afterRender
  20815. */
  20816. render() {
  20817. const axis = this, chart = axis.chart, log = axis.logarithmic, renderer = chart.renderer, options = axis.options, isLinked = axis.isLinked, tickPositions = axis.tickPositions, axisTitle = axis.axisTitle, ticks = axis.ticks, minorTicks = axis.minorTicks, alternateBands = axis.alternateBands, stackLabelOptions = options.stackLabels, alternateGridColor = options.alternateGridColor, crossing = options.crossing, tickmarkOffset = axis.tickmarkOffset, axisLine = axis.axisLine, showAxis = axis.showAxis, animation = animObject(renderer.globalAnimation);
  20818. let from, to;
  20819. // Reset
  20820. axis.labelEdge.length = 0;
  20821. axis.overlap = false;
  20822. // Mark all elements inActive before we go over and mark the active ones
  20823. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  20824. objectEach(coll, function (tick) {
  20825. tick.isActive = false;
  20826. });
  20827. });
  20828. // Crossing
  20829. if (isNumber(crossing)) {
  20830. const otherAxis = this.isXAxis ? chart.yAxis[0] : chart.xAxis[0], directionFactor = [1, -1, -1, 1][this.side];
  20831. if (otherAxis) {
  20832. let px = otherAxis.toPixels(crossing, true);
  20833. if (axis.horiz) {
  20834. px = otherAxis.len - px;
  20835. }
  20836. axis.offset = directionFactor * px;
  20837. }
  20838. }
  20839. // If the series has data draw the ticks. Else only the line and title
  20840. if (axis.hasData() || isLinked) {
  20841. const slideInTicks = axis.chart.hasRendered &&
  20842. axis.old && isNumber(axis.old.min);
  20843. // minor ticks
  20844. if (axis.minorTickInterval && !axis.categories) {
  20845. axis.getMinorTickPositions().forEach(function (pos) {
  20846. axis.renderMinorTick(pos, slideInTicks);
  20847. });
  20848. }
  20849. // Major ticks. Pull out the first item and render it last so that
  20850. // we can get the position of the neighbour label. #808.
  20851. if (tickPositions.length) { // #1300
  20852. tickPositions.forEach(function (pos, i) {
  20853. axis.renderTick(pos, i, slideInTicks);
  20854. });
  20855. // In a categorized axis, the tick marks are displayed
  20856. // between labels. So we need to add a tick mark and
  20857. // grid line at the left edge of the X axis.
  20858. if (tickmarkOffset && (axis.min === 0 || axis.single)) {
  20859. if (!ticks[-1]) {
  20860. ticks[-1] = new Tick(axis, -1, null, true);
  20861. }
  20862. ticks[-1].render(-1);
  20863. }
  20864. }
  20865. // alternate grid color
  20866. if (alternateGridColor) {
  20867. tickPositions.forEach(function (pos, i) {
  20868. to = typeof tickPositions[i + 1] !== 'undefined' ?
  20869. tickPositions[i + 1] + tickmarkOffset :
  20870. axis.max - tickmarkOffset;
  20871. if (i % 2 === 0 &&
  20872. pos < axis.max &&
  20873. to <= axis.max + (chart.polar ?
  20874. -tickmarkOffset :
  20875. tickmarkOffset)) { // #2248, #4660
  20876. if (!alternateBands[pos]) {
  20877. // Should be imported from PlotLineOrBand.js, but
  20878. // the dependency cycle with axis is a problem
  20879. alternateBands[pos] = new H.PlotLineOrBand(axis);
  20880. }
  20881. from = pos + tickmarkOffset; // #949
  20882. alternateBands[pos].options = {
  20883. from: log ? log.lin2log(from) : from,
  20884. to: log ? log.lin2log(to) : to,
  20885. color: alternateGridColor,
  20886. className: 'highcharts-alternate-grid'
  20887. };
  20888. alternateBands[pos].render();
  20889. alternateBands[pos].isActive = true;
  20890. }
  20891. });
  20892. }
  20893. // custom plot lines and bands
  20894. if (!axis._addedPlotLB) { // only first time
  20895. axis._addedPlotLB = true;
  20896. (options.plotLines || [])
  20897. .concat(options.plotBands || [])
  20898. .forEach(function (plotLineOptions) {
  20899. axis
  20900. .addPlotBandOrLine(plotLineOptions);
  20901. });
  20902. }
  20903. } // end if hasData
  20904. // Remove inactive ticks
  20905. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  20906. const forDestruction = [], delay = animation.duration, destroyInactiveItems = function () {
  20907. let i = forDestruction.length;
  20908. while (i--) {
  20909. // When resizing rapidly, the same items
  20910. // may be destroyed in different timeouts,
  20911. // or the may be reactivated
  20912. if (coll[forDestruction[i]] &&
  20913. !coll[forDestruction[i]].isActive) {
  20914. coll[forDestruction[i]].destroy();
  20915. delete coll[forDestruction[i]];
  20916. }
  20917. }
  20918. };
  20919. objectEach(coll, function (tick, pos) {
  20920. if (!tick.isActive) {
  20921. // Render to zero opacity
  20922. tick.render(pos, false, 0);
  20923. tick.isActive = false;
  20924. forDestruction.push(pos);
  20925. }
  20926. });
  20927. // When the objects are finished fading out, destroy them
  20928. syncTimeout(destroyInactiveItems, coll === alternateBands ||
  20929. !chart.hasRendered ||
  20930. !delay ?
  20931. 0 :
  20932. delay);
  20933. });
  20934. // Set the axis line path
  20935. if (axisLine) {
  20936. axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
  20937. d: this.getLinePath(axisLine.strokeWidth())
  20938. });
  20939. axisLine.isPlaced = true;
  20940. // Show or hide the line depending on options.showEmpty
  20941. axisLine[showAxis ? 'show' : 'hide'](showAxis);
  20942. }
  20943. if (axisTitle && showAxis) {
  20944. axisTitle[axisTitle.isNew ? 'attr' : 'animate'](axis.getTitlePosition(axisTitle));
  20945. axisTitle.isNew = false;
  20946. }
  20947. // Stacked totals:
  20948. if (stackLabelOptions && stackLabelOptions.enabled && axis.stacking) {
  20949. axis.stacking.renderStackTotals();
  20950. }
  20951. // End stacked totals
  20952. // Record old scaling for updating/animation
  20953. axis.old = {
  20954. len: axis.len,
  20955. max: axis.max,
  20956. min: axis.min,
  20957. transA: axis.transA,
  20958. userMax: axis.userMax,
  20959. userMin: axis.userMin
  20960. };
  20961. axis.isDirty = false;
  20962. fireEvent(this, 'afterRender');
  20963. }
  20964. /**
  20965. * Redraw the axis to reflect changes in the data or axis extremes. Called
  20966. * internally from Highcharts.Chart#redraw.
  20967. *
  20968. * @private
  20969. * @function Highcharts.Axis#redraw
  20970. */
  20971. redraw() {
  20972. if (this.visible) {
  20973. // render the axis
  20974. this.render();
  20975. // move plot lines and bands
  20976. this.plotLinesAndBands.forEach(function (plotLine) {
  20977. plotLine.render();
  20978. });
  20979. }
  20980. // mark associated series as dirty and ready for redraw
  20981. this.series.forEach(function (series) {
  20982. series.isDirty = true;
  20983. });
  20984. }
  20985. /**
  20986. * Returns an array of axis properties, that should be untouched during
  20987. * reinitialization.
  20988. *
  20989. * @private
  20990. * @function Highcharts.Axis#getKeepProps
  20991. */
  20992. getKeepProps() {
  20993. return (this.keepProps || Axis.keepProps);
  20994. }
  20995. /**
  20996. * Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
  20997. * to fully remove the axis.
  20998. *
  20999. * @private
  21000. * @function Highcharts.Axis#destroy
  21001. *
  21002. * @param {boolean} [keepEvents]
  21003. * Whether to preserve events, used internally in Axis.update.
  21004. */
  21005. destroy(keepEvents) {
  21006. const axis = this, plotLinesAndBands = axis.plotLinesAndBands, eventOptions = this.eventOptions;
  21007. fireEvent(this, 'destroy', { keepEvents: keepEvents });
  21008. // Remove the events
  21009. if (!keepEvents) {
  21010. removeEvent(axis);
  21011. }
  21012. // Destroy collections
  21013. [axis.ticks, axis.minorTicks, axis.alternateBands].forEach(function (coll) {
  21014. destroyObjectProperties(coll);
  21015. });
  21016. if (plotLinesAndBands) {
  21017. let i = plotLinesAndBands.length;
  21018. while (i--) { // #1975
  21019. plotLinesAndBands[i].destroy();
  21020. }
  21021. }
  21022. // Destroy elements
  21023. ['axisLine', 'axisTitle', 'axisGroup',
  21024. 'gridGroup', 'labelGroup', 'cross', 'scrollbar'].forEach(function (prop) {
  21025. if (axis[prop]) {
  21026. axis[prop] = axis[prop].destroy();
  21027. }
  21028. });
  21029. // Destroy each generated group for plotlines and plotbands
  21030. for (const plotGroup in axis.plotLinesAndBandsGroups) { // eslint-disable-line guard-for-in
  21031. axis.plotLinesAndBandsGroups[plotGroup] =
  21032. axis.plotLinesAndBandsGroups[plotGroup].destroy();
  21033. }
  21034. // Delete all properties and fall back to the prototype.
  21035. objectEach(axis, function (val, key) {
  21036. if (axis.getKeepProps().indexOf(key) === -1) {
  21037. delete axis[key];
  21038. }
  21039. });
  21040. this.eventOptions = eventOptions;
  21041. }
  21042. /**
  21043. * Internal function to draw a crosshair.
  21044. *
  21045. * @function Highcharts.Axis#drawCrosshair
  21046. *
  21047. * @param {Highcharts.PointerEventObject} [e]
  21048. * The event arguments from the modified pointer event, extended with
  21049. * `chartX` and `chartY`
  21050. *
  21051. * @param {Highcharts.Point} [point]
  21052. * The Point object if the crosshair snaps to points.
  21053. *
  21054. * @emits Highcharts.Axis#event:afterDrawCrosshair
  21055. * @emits Highcharts.Axis#event:drawCrosshair
  21056. */
  21057. drawCrosshair(e, point) {
  21058. const options = this.crosshair, snap = pick(options && options.snap, true), chart = this.chart;
  21059. let path, pos, categorized, graphic = this.cross, crossOptions;
  21060. fireEvent(this, 'drawCrosshair', { e: e, point: point });
  21061. // Use last available event when updating non-snapped crosshairs without
  21062. // mouse interaction (#5287)
  21063. if (!e) {
  21064. e = this.cross && this.cross.e;
  21065. }
  21066. if (
  21067. // Disabled in options
  21068. !options ||
  21069. // Snap
  21070. ((defined(point) || !snap) === false)) {
  21071. this.hideCrosshair();
  21072. }
  21073. else {
  21074. // Get the path
  21075. if (!snap) {
  21076. pos = e &&
  21077. (this.horiz ?
  21078. e.chartX - this.pos :
  21079. this.len - e.chartY + this.pos);
  21080. }
  21081. else if (defined(point)) {
  21082. // #3834
  21083. pos = pick(this.coll !== 'colorAxis' ?
  21084. point.crosshairPos : // 3D axis extension
  21085. null, this.isXAxis ?
  21086. point.plotX :
  21087. this.len - point.plotY);
  21088. }
  21089. if (defined(pos)) {
  21090. crossOptions = {
  21091. // value, only used on radial
  21092. value: point && (this.isXAxis ?
  21093. point.x :
  21094. pick(point.stackY, point.y)),
  21095. translatedValue: pos
  21096. };
  21097. if (chart.polar) {
  21098. // Additional information required for crosshairs in
  21099. // polar chart
  21100. extend(crossOptions, {
  21101. isCrosshair: true,
  21102. chartX: e && e.chartX,
  21103. chartY: e && e.chartY,
  21104. point: point
  21105. });
  21106. }
  21107. path = this.getPlotLinePath(crossOptions) ||
  21108. null; // #3189
  21109. }
  21110. if (!defined(path)) {
  21111. this.hideCrosshair();
  21112. return;
  21113. }
  21114. categorized = this.categories && !this.isRadial;
  21115. // Draw the cross
  21116. if (!graphic) {
  21117. this.cross = graphic = chart.renderer
  21118. .path()
  21119. .addClass('highcharts-crosshair highcharts-crosshair-' +
  21120. (categorized ? 'category ' : 'thin ') +
  21121. (options.className || ''))
  21122. .attr({
  21123. zIndex: pick(options.zIndex, 2)
  21124. })
  21125. .add();
  21126. // Presentational attributes
  21127. if (!chart.styledMode) {
  21128. graphic.attr({
  21129. stroke: options.color ||
  21130. (categorized ?
  21131. Color
  21132. .parse("#ccd3ff" /* Palette.highlightColor20 */)
  21133. .setOpacity(0.25)
  21134. .get() :
  21135. "#cccccc" /* Palette.neutralColor20 */),
  21136. 'stroke-width': pick(options.width, 1)
  21137. }).css({
  21138. 'pointer-events': 'none'
  21139. });
  21140. if (options.dashStyle) {
  21141. graphic.attr({
  21142. dashstyle: options.dashStyle
  21143. });
  21144. }
  21145. }
  21146. }
  21147. graphic.show().attr({
  21148. d: path
  21149. });
  21150. if (categorized && !options.width) {
  21151. graphic.attr({
  21152. 'stroke-width': this.transA
  21153. });
  21154. }
  21155. this.cross.e = e;
  21156. }
  21157. fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
  21158. }
  21159. /**
  21160. * Hide the crosshair if visible.
  21161. *
  21162. * @function Highcharts.Axis#hideCrosshair
  21163. */
  21164. hideCrosshair() {
  21165. if (this.cross) {
  21166. this.cross.hide();
  21167. }
  21168. fireEvent(this, 'afterHideCrosshair');
  21169. }
  21170. /**
  21171. * Check whether the chart has vertical panning ('y' or 'xy' type).
  21172. *
  21173. * @private
  21174. * @function Highcharts.Axis#hasVerticalPanning
  21175. */
  21176. hasVerticalPanning() {
  21177. const panningOptions = this.chart.options.chart.panning;
  21178. return Boolean(panningOptions &&
  21179. panningOptions.enabled && // #14624
  21180. /y/.test(panningOptions.type));
  21181. }
  21182. /**
  21183. * Update an axis object with a new set of options. The options are merged
  21184. * with the existing options, so only new or altered options need to be
  21185. * specified.
  21186. *
  21187. * @sample highcharts/members/axis-update/
  21188. * Axis update demo
  21189. *
  21190. * @function Highcharts.Axis#update
  21191. *
  21192. * @param {Highcharts.AxisOptions} options
  21193. * The new options that will be merged in with existing options on the axis.
  21194. *
  21195. * @param {boolean} [redraw=true]
  21196. * Whether to redraw the chart after the axis is altered. If doing more
  21197. * operations on the chart, it is a good idea to set redraw to false and
  21198. * call {@link Chart#redraw} after.
  21199. */
  21200. update(options, redraw) {
  21201. const chart = this.chart;
  21202. options = merge(this.userOptions, options);
  21203. this.destroy(true);
  21204. this.init(chart, options);
  21205. chart.isDirtyBox = true;
  21206. if (pick(redraw, true)) {
  21207. chart.redraw();
  21208. }
  21209. }
  21210. /**
  21211. * Remove the axis from the chart.
  21212. *
  21213. * @sample highcharts/members/chart-addaxis/
  21214. * Add and remove axes
  21215. *
  21216. * @function Highcharts.Axis#remove
  21217. *
  21218. * @param {boolean} [redraw=true]
  21219. * Whether to redraw the chart following the remove.
  21220. */
  21221. remove(redraw) {
  21222. const chart = this.chart, coll = this.coll, axisSeries = this.series;
  21223. let i = axisSeries.length;
  21224. // Remove associated series (#2687)
  21225. while (i--) {
  21226. if (axisSeries[i]) {
  21227. axisSeries[i].remove(false);
  21228. }
  21229. }
  21230. // Remove the axis
  21231. erase(chart.axes, this);
  21232. erase(chart[coll] || [], this);
  21233. chart.orderItems(coll);
  21234. this.destroy();
  21235. chart.isDirtyBox = true;
  21236. if (pick(redraw, true)) {
  21237. chart.redraw();
  21238. }
  21239. }
  21240. /**
  21241. * Update the axis title by options after render time.
  21242. *
  21243. * @sample highcharts/members/axis-settitle/
  21244. * Set a new Y axis title
  21245. *
  21246. * @function Highcharts.Axis#setTitle
  21247. *
  21248. * @param {Highcharts.AxisTitleOptions} titleOptions
  21249. * The additional title options.
  21250. *
  21251. * @param {boolean} [redraw=true]
  21252. * Whether to redraw the chart after setting the title.
  21253. */
  21254. setTitle(titleOptions, redraw) {
  21255. this.update({ title: titleOptions }, redraw);
  21256. }
  21257. /**
  21258. * Set new axis categories and optionally redraw.
  21259. *
  21260. * @sample highcharts/members/axis-setcategories/
  21261. * Set categories by click on a button
  21262. *
  21263. * @function Highcharts.Axis#setCategories
  21264. *
  21265. * @param {Array<string>} categories
  21266. * The new categories.
  21267. *
  21268. * @param {boolean} [redraw=true]
  21269. * Whether to redraw the chart.
  21270. */
  21271. setCategories(categories, redraw) {
  21272. this.update({ categories: categories }, redraw);
  21273. }
  21274. }
  21275. /* *
  21276. *
  21277. * Static Properties
  21278. *
  21279. * */
  21280. Axis.defaultOptions = AxisDefaults.defaultXAxisOptions;
  21281. // Properties to survive after destroy, needed for Axis.update (#4317,
  21282. // #5773, #5881).
  21283. Axis.keepProps = [
  21284. 'coll',
  21285. 'extKey',
  21286. 'hcEvents',
  21287. 'names',
  21288. 'series',
  21289. 'userMax',
  21290. 'userMin'
  21291. ];
  21292. /* *
  21293. *
  21294. * Default Export
  21295. *
  21296. * */
  21297. /* *
  21298. *
  21299. * API Declarations
  21300. *
  21301. * */
  21302. /**
  21303. * Options for the path on the Axis to be calculated.
  21304. * @interface Highcharts.AxisPlotLinePathOptionsObject
  21305. */ /**
  21306. * Axis value.
  21307. * @name Highcharts.AxisPlotLinePathOptionsObject#value
  21308. * @type {number|undefined}
  21309. */ /**
  21310. * Line width used for calculation crisp line coordinates. Defaults to 1.
  21311. * @name Highcharts.AxisPlotLinePathOptionsObject#lineWidth
  21312. * @type {number|undefined}
  21313. */ /**
  21314. * If `false`, the function will return null when it falls outside the axis
  21315. * bounds. If `true`, the function will return a path aligned to the plot area
  21316. * sides if it falls outside. If `pass`, it will return a path outside.
  21317. * @name Highcharts.AxisPlotLinePathOptionsObject#force
  21318. * @type {string|boolean|undefined}
  21319. */ /**
  21320. * Used in Highcharts Stock. When `true`, plot paths
  21321. * (crosshair, plotLines, gridLines)
  21322. * will be rendered on all axes when defined on the first axis.
  21323. * @name Highcharts.AxisPlotLinePathOptionsObject#acrossPanes
  21324. * @type {boolean|undefined}
  21325. */ /**
  21326. * Use old coordinates (for resizing and rescaling).
  21327. * If not set, defaults to `false`.
  21328. * @name Highcharts.AxisPlotLinePathOptionsObject#old
  21329. * @type {boolean|undefined}
  21330. */ /**
  21331. * If given, return the plot line path of a pixel position on the axis.
  21332. * @name Highcharts.AxisPlotLinePathOptionsObject#translatedValue
  21333. * @type {number|undefined}
  21334. */ /**
  21335. * Used in Polar axes. Reverse the positions for concatenation of polygonal
  21336. * plot bands
  21337. * @name Highcharts.AxisPlotLinePathOptionsObject#reverse
  21338. * @type {boolean|undefined}
  21339. */
  21340. /**
  21341. * Options for crosshairs on axes.
  21342. *
  21343. * @product highstock
  21344. *
  21345. * @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
  21346. */
  21347. /**
  21348. * @typedef {"navigator"|"pan"|"rangeSelectorButton"|"rangeSelectorInput"|"scrollbar"|"traverseUpButton"|"zoom"} Highcharts.AxisExtremesTriggerValue
  21349. */
  21350. /**
  21351. * @callback Highcharts.AxisEventCallbackFunction
  21352. *
  21353. * @param {Highcharts.Axis} this
  21354. */
  21355. /**
  21356. * @callback Highcharts.AxisLabelsFormatterCallbackFunction
  21357. *
  21358. * @param {Highcharts.AxisLabelsFormatterContextObject} this
  21359. *
  21360. * @param {Highcharts.AxisLabelsFormatterContextObject} ctx
  21361. *
  21362. * @return {string}
  21363. */
  21364. /**
  21365. * @interface Highcharts.AxisLabelsFormatterContextObject
  21366. */ /**
  21367. * The axis item of the label
  21368. * @name Highcharts.AxisLabelsFormatterContextObject#axis
  21369. * @type {Highcharts.Axis}
  21370. */ /**
  21371. * The chart instance.
  21372. * @name Highcharts.AxisLabelsFormatterContextObject#chart
  21373. * @type {Highcharts.Chart}
  21374. */ /**
  21375. * Default formatting of date/time labels.
  21376. * @name Highcharts.AxisLabelsFormatterContextObject#dateTimeLabelFormat
  21377. * @type {string|undefined}
  21378. */ /**
  21379. * Whether the label belongs to the first tick on the axis.
  21380. * @name Highcharts.AxisLabelsFormatterContextObject#isFirst
  21381. * @type {boolean}
  21382. */ /**
  21383. * Whether the label belongs to the last tick on the axis.
  21384. * @name Highcharts.AxisLabelsFormatterContextObject#isLast
  21385. * @type {boolean}
  21386. */ /**
  21387. * The position on the axis in terms of axis values. For category axes, a
  21388. * zero-based index. For datetime axes, the JavaScript time in milliseconds
  21389. * since 1970.
  21390. * @name Highcharts.AxisLabelsFormatterContextObject#pos
  21391. * @type {number}
  21392. */ /**
  21393. * The preformatted text as the result of the default formatting. For example
  21394. * dates will be formatted as strings, and numbers with language-specific comma
  21395. * separators, thousands separators and numeric symbols like `k` or `M`.
  21396. * @name Highcharts.AxisLabelsFormatterContextObject#text
  21397. * @type {string|undefined}
  21398. */ /**
  21399. * The Tick instance.
  21400. * @name Highcharts.AxisLabelsFormatterContextObject#tick
  21401. * @type {Highcharts.Tick}
  21402. */ /**
  21403. * This can be either a numeric value or a category string.
  21404. * @name Highcharts.AxisLabelsFormatterContextObject#value
  21405. * @type {number|string}
  21406. */
  21407. /**
  21408. * Options for axes.
  21409. *
  21410. * @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
  21411. */
  21412. /**
  21413. * @callback Highcharts.AxisPointBreakEventCallbackFunction
  21414. *
  21415. * @param {Highcharts.Axis} this
  21416. *
  21417. * @param {Highcharts.AxisPointBreakEventObject} evt
  21418. */
  21419. /**
  21420. * @interface Highcharts.AxisPointBreakEventObject
  21421. */ /**
  21422. * @name Highcharts.AxisPointBreakEventObject#brk
  21423. * @type {Highcharts.Dictionary<number>}
  21424. */ /**
  21425. * @name Highcharts.AxisPointBreakEventObject#point
  21426. * @type {Highcharts.Point}
  21427. */ /**
  21428. * @name Highcharts.AxisPointBreakEventObject#preventDefault
  21429. * @type {Function}
  21430. */ /**
  21431. * @name Highcharts.AxisPointBreakEventObject#target
  21432. * @type {Highcharts.SVGElement}
  21433. */ /**
  21434. * @name Highcharts.AxisPointBreakEventObject#type
  21435. * @type {"pointBreak"|"pointInBreak"}
  21436. */
  21437. /**
  21438. * @callback Highcharts.AxisSetExtremesEventCallbackFunction
  21439. *
  21440. * @param {Highcharts.Axis} this
  21441. *
  21442. * @param {Highcharts.AxisSetExtremesEventObject} evt
  21443. */
  21444. /**
  21445. * @interface Highcharts.AxisSetExtremesEventObject
  21446. * @extends Highcharts.ExtremesObject
  21447. */ /**
  21448. * @name Highcharts.AxisSetExtremesEventObject#preventDefault
  21449. * @type {Function}
  21450. */ /**
  21451. * @name Highcharts.AxisSetExtremesEventObject#target
  21452. * @type {Highcharts.SVGElement}
  21453. */ /**
  21454. * @name Highcharts.AxisSetExtremesEventObject#trigger
  21455. * @type {Highcharts.AxisExtremesTriggerValue|string}
  21456. */ /**
  21457. * @name Highcharts.AxisSetExtremesEventObject#type
  21458. * @type {"setExtremes"}
  21459. */
  21460. /**
  21461. * @callback Highcharts.AxisTickPositionerCallbackFunction
  21462. *
  21463. * @param {Highcharts.Axis} this
  21464. *
  21465. * @return {Highcharts.AxisTickPositionsArray}
  21466. */
  21467. /**
  21468. * @interface Highcharts.AxisTickPositionsArray
  21469. * @augments Array<number>
  21470. */
  21471. /**
  21472. * @typedef {"high"|"low"|"middle"} Highcharts.AxisTitleAlignValue
  21473. */
  21474. /**
  21475. * @typedef {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} Highcharts.AxisTitleOptions
  21476. */
  21477. /**
  21478. * @typedef {"linear"|"logarithmic"|"datetime"|"category"|"treegrid"} Highcharts.AxisTypeValue
  21479. */
  21480. /**
  21481. * The returned object literal from the {@link Highcharts.Axis#getExtremes}
  21482. * function.
  21483. *
  21484. * @interface Highcharts.ExtremesObject
  21485. */ /**
  21486. * The maximum value of the axis' associated series.
  21487. * @name Highcharts.ExtremesObject#dataMax
  21488. * @type {number}
  21489. */ /**
  21490. * The minimum value of the axis' associated series.
  21491. * @name Highcharts.ExtremesObject#dataMin
  21492. * @type {number}
  21493. */ /**
  21494. * The maximum axis value, either automatic or set manually. If the `max` option
  21495. * is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
  21496. * the same as `dataMax`.
  21497. * @name Highcharts.ExtremesObject#max
  21498. * @type {number}
  21499. */ /**
  21500. * The minimum axis value, either automatic or set manually. If the `min` option
  21501. * is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
  21502. * the same as `dataMin`.
  21503. * @name Highcharts.ExtremesObject#min
  21504. * @type {number}
  21505. */ /**
  21506. * The user defined maximum, either from the `max` option or from a zoom or
  21507. * `setExtremes` action.
  21508. * @name Highcharts.ExtremesObject#userMax
  21509. * @type {number|undefined}
  21510. */ /**
  21511. * The user defined minimum, either from the `min` option or from a zoom or
  21512. * `setExtremes` action.
  21513. * @name Highcharts.ExtremesObject#userMin
  21514. * @type {number|undefined}
  21515. */
  21516. /**
  21517. * Formatter function for the text of a crosshair label.
  21518. *
  21519. * @callback Highcharts.XAxisCrosshairLabelFormatterCallbackFunction
  21520. *
  21521. * @param {Highcharts.Axis} this
  21522. * Axis context
  21523. *
  21524. * @param {number} value
  21525. * Y value of the data point
  21526. *
  21527. * @return {string}
  21528. */
  21529. ''; // keeps doclets above in JS file
  21530. return Axis;
  21531. });
  21532. _registerModule(_modules, 'Core/Axis/DateTimeAxis.js', [_modules['Core/Utilities.js']], function (U) {
  21533. /* *
  21534. *
  21535. * (c) 2010-2021 Torstein Honsi
  21536. *
  21537. * License: www.highcharts.com/license
  21538. *
  21539. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  21540. *
  21541. * */
  21542. const { addEvent, getMagnitude, normalizeTickInterval, timeUnits } = U;
  21543. /* *
  21544. *
  21545. * Composition
  21546. *
  21547. * */
  21548. /* eslint-disable valid-jsdoc */
  21549. var DateTimeAxis;
  21550. (function (DateTimeAxis) {
  21551. /* *
  21552. *
  21553. * Declarations
  21554. *
  21555. * */
  21556. /* *
  21557. *
  21558. * Constants
  21559. *
  21560. * */
  21561. const composedMembers = [];
  21562. /* *
  21563. *
  21564. * Functions
  21565. *
  21566. * */
  21567. /**
  21568. * Extends axis class with date and time support.
  21569. * @private
  21570. */
  21571. function compose(AxisClass) {
  21572. if (U.pushUnique(composedMembers, AxisClass)) {
  21573. AxisClass.keepProps.push('dateTime');
  21574. const axisProto = AxisClass.prototype;
  21575. axisProto.getTimeTicks = getTimeTicks;
  21576. addEvent(AxisClass, 'init', onInit);
  21577. }
  21578. return AxisClass;
  21579. }
  21580. DateTimeAxis.compose = compose;
  21581. /**
  21582. * Set the tick positions to a time unit that makes sense, for example
  21583. * on the first of each month or on every Monday. Return an array with
  21584. * the time positions. Used in datetime axes as well as for grouping
  21585. * data on a datetime axis.
  21586. *
  21587. * @private
  21588. * @function Highcharts.Axis#getTimeTicks
  21589. * @param {Highcharts.TimeNormalizeObject} normalizedInterval
  21590. * The interval in axis values (ms) and thecount.
  21591. * @param {number} min
  21592. * The minimum in axis values.
  21593. * @param {number} max
  21594. * The maximum in axis values.
  21595. */
  21596. function getTimeTicks() {
  21597. return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
  21598. }
  21599. /**
  21600. * @private
  21601. */
  21602. function onInit(e) {
  21603. const axis = this;
  21604. const options = e.userOptions;
  21605. if (options.type !== 'datetime') {
  21606. axis.dateTime = void 0;
  21607. return;
  21608. }
  21609. if (!axis.dateTime) {
  21610. axis.dateTime = new Additions(axis);
  21611. }
  21612. }
  21613. /* *
  21614. *
  21615. * Classes
  21616. *
  21617. * */
  21618. class Additions {
  21619. /* *
  21620. *
  21621. * Constructors
  21622. *
  21623. * */
  21624. constructor(axis) {
  21625. this.axis = axis;
  21626. }
  21627. /* *
  21628. *
  21629. * Functions
  21630. *
  21631. * */
  21632. /**
  21633. * Get a normalized tick interval for dates. Returns a configuration
  21634. * object with unit range (interval), count and name. Used to prepare
  21635. * data for `getTimeTicks`. Previously this logic was part of
  21636. * getTimeTicks, but as `getTimeTicks` now runs of segments in stock
  21637. * charts, the normalizing logic was extracted in order to prevent it
  21638. * for running over again for each segment having the same interval.
  21639. * #662, #697.
  21640. * @private
  21641. */
  21642. normalizeTimeTickInterval(tickInterval, unitsOption) {
  21643. const units = (unitsOption || [[
  21644. // unit name
  21645. 'millisecond',
  21646. // allowed multiples
  21647. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]
  21648. ], [
  21649. 'second',
  21650. [1, 2, 5, 10, 15, 30]
  21651. ], [
  21652. 'minute',
  21653. [1, 2, 5, 10, 15, 30]
  21654. ], [
  21655. 'hour',
  21656. [1, 2, 3, 4, 6, 8, 12]
  21657. ], [
  21658. 'day',
  21659. [1, 2]
  21660. ], [
  21661. 'week',
  21662. [1, 2]
  21663. ], [
  21664. 'month',
  21665. [1, 2, 3, 4, 6]
  21666. ], [
  21667. 'year',
  21668. null
  21669. ]]);
  21670. let unit = units[units.length - 1], // default unit is years
  21671. interval = timeUnits[unit[0]], multiples = unit[1], i;
  21672. // loop through the units to find the one that best fits the
  21673. // tickInterval
  21674. for (i = 0; i < units.length; i++) {
  21675. unit = units[i];
  21676. interval = timeUnits[unit[0]];
  21677. multiples = unit[1];
  21678. if (units[i + 1]) {
  21679. // lessThan is in the middle between the highest multiple
  21680. // and the next unit.
  21681. const lessThan = (interval *
  21682. multiples[multiples.length - 1] +
  21683. timeUnits[units[i + 1][0]]) / 2;
  21684. // break and keep the current unit
  21685. if (tickInterval <= lessThan) {
  21686. break;
  21687. }
  21688. }
  21689. }
  21690. // prevent 2.5 years intervals, though 25, 250 etc. are allowed
  21691. if (interval === timeUnits.year && tickInterval < 5 * interval) {
  21692. multiples = [1, 2, 5];
  21693. }
  21694. // get the count
  21695. const count = normalizeTickInterval(tickInterval / interval, multiples, unit[0] === 'year' ? // #1913, #2360
  21696. Math.max(getMagnitude(tickInterval / interval), 1) :
  21697. 1);
  21698. return {
  21699. unitRange: interval,
  21700. count: count,
  21701. unitName: unit[0]
  21702. };
  21703. }
  21704. /**
  21705. * Get the best date format for a specific X value based on the closest
  21706. * point range on the axis.
  21707. *
  21708. * @private
  21709. */
  21710. getXDateFormat(x, dateTimeLabelFormats) {
  21711. const { axis } = this, time = axis.chart.time;
  21712. return axis.closestPointRange ?
  21713. time.getDateFormat(axis.closestPointRange, x, axis.options.startOfWeek, dateTimeLabelFormats) ||
  21714. // #2546, 2581
  21715. time.resolveDTLFormat(dateTimeLabelFormats.year).main :
  21716. time.resolveDTLFormat(dateTimeLabelFormats.day).main;
  21717. }
  21718. }
  21719. DateTimeAxis.Additions = Additions;
  21720. })(DateTimeAxis || (DateTimeAxis = {}));
  21721. /* *
  21722. *
  21723. * Default Export
  21724. *
  21725. * */
  21726. return DateTimeAxis;
  21727. });
  21728. _registerModule(_modules, 'Core/Axis/LogarithmicAxis.js', [_modules['Core/Utilities.js']], function (U) {
  21729. /* *
  21730. *
  21731. * (c) 2010-2021 Torstein Honsi
  21732. *
  21733. * License: www.highcharts.com/license
  21734. *
  21735. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  21736. *
  21737. * */
  21738. const { addEvent, normalizeTickInterval, pick } = U;
  21739. /* *
  21740. *
  21741. * Class
  21742. *
  21743. * */
  21744. /**
  21745. * @private
  21746. */
  21747. var LogarithmicAxis;
  21748. (function (LogarithmicAxis) {
  21749. /* *
  21750. *
  21751. * Declarations
  21752. *
  21753. * */
  21754. /* *
  21755. *
  21756. * Constants
  21757. *
  21758. * */
  21759. const composedMembers = [];
  21760. /* *
  21761. *
  21762. * Functions
  21763. *
  21764. * */
  21765. /* eslint-disable valid-jsdoc */
  21766. /**
  21767. * Provides logarithmic support for axes.
  21768. * @private
  21769. */
  21770. function compose(AxisClass) {
  21771. if (U.pushUnique(composedMembers, AxisClass)) {
  21772. AxisClass.keepProps.push('logarithmic');
  21773. addEvent(AxisClass, 'init', onInit);
  21774. addEvent(AxisClass, 'afterInit', onAfterInit);
  21775. }
  21776. return AxisClass;
  21777. }
  21778. LogarithmicAxis.compose = compose;
  21779. /**
  21780. * @private
  21781. */
  21782. function onInit(e) {
  21783. const axis = this;
  21784. const options = e.userOptions;
  21785. let logarithmic = axis.logarithmic;
  21786. if (options.type !== 'logarithmic') {
  21787. axis.logarithmic = void 0;
  21788. }
  21789. else {
  21790. if (!logarithmic) {
  21791. logarithmic = axis.logarithmic = new Additions(axis);
  21792. }
  21793. }
  21794. }
  21795. /**
  21796. * @private
  21797. */
  21798. function onAfterInit() {
  21799. const axis = this;
  21800. const log = axis.logarithmic;
  21801. // extend logarithmic axis
  21802. if (log) {
  21803. axis.lin2val = function (num) {
  21804. return log.lin2log(num);
  21805. };
  21806. axis.val2lin = function (num) {
  21807. return log.log2lin(num);
  21808. };
  21809. }
  21810. }
  21811. /* *
  21812. *
  21813. * Class
  21814. *
  21815. * */
  21816. /**
  21817. * Provides logarithmic support for axes.
  21818. * @private
  21819. * @class
  21820. */
  21821. class Additions {
  21822. /* *
  21823. *
  21824. * Constructors
  21825. *
  21826. * */
  21827. constructor(axis) {
  21828. this.axis = axis;
  21829. }
  21830. /* *
  21831. *
  21832. * Functions
  21833. *
  21834. * */
  21835. /**
  21836. * Set the tick positions of a logarithmic axis.
  21837. */
  21838. getLogTickPositions(interval, min, max, minor) {
  21839. const log = this;
  21840. const axis = log.axis;
  21841. const axisLength = axis.len;
  21842. const options = axis.options;
  21843. // Since we use this method for both major and minor ticks,
  21844. // use a local variable and return the result
  21845. let positions = [];
  21846. // Reset
  21847. if (!minor) {
  21848. log.minorAutoInterval = void 0;
  21849. }
  21850. // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
  21851. if (interval >= 0.5) {
  21852. interval = Math.round(interval);
  21853. positions = axis.getLinearTickPositions(interval, min, max);
  21854. // Second case: We need intermediary ticks. For example
  21855. // 1, 2, 4, 6, 8, 10, 20, 40 etc.
  21856. }
  21857. else if (interval >= 0.08) {
  21858. const roundedMin = Math.floor(min);
  21859. let intermediate, i, j, len, pos, lastPos, break2;
  21860. if (interval > 0.3) {
  21861. intermediate = [1, 2, 4];
  21862. // 0.2 equals five minor ticks per 1, 10, 100 etc
  21863. }
  21864. else if (interval > 0.15) {
  21865. intermediate = [1, 2, 4, 6, 8];
  21866. }
  21867. else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
  21868. intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  21869. }
  21870. for (i = roundedMin; i < max + 1 && !break2; i++) {
  21871. len = intermediate.length;
  21872. for (j = 0; j < len && !break2; j++) {
  21873. pos = log.log2lin(log.lin2log(i) * intermediate[j]);
  21874. // #1670, lastPos is #3113
  21875. if (pos > min &&
  21876. (!minor || lastPos <= max) &&
  21877. typeof lastPos !== 'undefined') {
  21878. positions.push(lastPos);
  21879. }
  21880. if (lastPos > max) {
  21881. break2 = true;
  21882. }
  21883. lastPos = pos;
  21884. }
  21885. }
  21886. // Third case: We are so deep in between whole logarithmic values,
  21887. // that we might as well handle the tick positions like a linear
  21888. // axis. For example 1.01, 1.02, 1.03, 1.04.
  21889. }
  21890. else {
  21891. const realMin = log.lin2log(min), realMax = log.lin2log(max), tickIntervalOption = minor ?
  21892. axis.getMinorTickInterval() :
  21893. options.tickInterval, filteredTickIntervalOption = tickIntervalOption === 'auto' ?
  21894. null :
  21895. tickIntervalOption, tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1), totalPixelLength = minor ?
  21896. axisLength / axis.tickPositions.length :
  21897. axisLength;
  21898. interval = pick(filteredTickIntervalOption, log.minorAutoInterval, (realMax - realMin) *
  21899. tickPixelIntervalOption / (totalPixelLength || 1));
  21900. interval = normalizeTickInterval(interval);
  21901. positions = axis.getLinearTickPositions(interval, realMin, realMax).map(log.log2lin);
  21902. if (!minor) {
  21903. log.minorAutoInterval = interval / 5;
  21904. }
  21905. }
  21906. // Set the axis-level tickInterval variable
  21907. if (!minor) {
  21908. axis.tickInterval = interval;
  21909. }
  21910. return positions;
  21911. }
  21912. lin2log(num) {
  21913. return Math.pow(10, num);
  21914. }
  21915. log2lin(num) {
  21916. return Math.log(num) / Math.LN10;
  21917. }
  21918. }
  21919. LogarithmicAxis.Additions = Additions;
  21920. })(LogarithmicAxis || (LogarithmicAxis = {}));
  21921. /* *
  21922. *
  21923. * Default Export
  21924. *
  21925. * */
  21926. return LogarithmicAxis;
  21927. });
  21928. _registerModule(_modules, 'Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js', [_modules['Core/Utilities.js']], function (U) {
  21929. /* *
  21930. *
  21931. * (c) 2010-2021 Torstein Honsi
  21932. *
  21933. * License: www.highcharts.com/license
  21934. *
  21935. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  21936. *
  21937. * */
  21938. const { erase, extend, isNumber } = U;
  21939. /* *
  21940. *
  21941. * Composition
  21942. *
  21943. * */
  21944. var PlotLineOrBandAxis;
  21945. (function (PlotLineOrBandAxis) {
  21946. /* *
  21947. *
  21948. * Declarations
  21949. *
  21950. * */
  21951. /* *
  21952. *
  21953. * Constants
  21954. *
  21955. * */
  21956. const composedMembers = [];
  21957. /* *
  21958. *
  21959. * Variables
  21960. *
  21961. * */
  21962. let PlotLineOrBandClass;
  21963. /* *
  21964. *
  21965. * Functions
  21966. *
  21967. * */
  21968. /**
  21969. * Add a plot band after render time.
  21970. *
  21971. * @sample highcharts/members/axis-addplotband/
  21972. * Toggle the plot band from a button
  21973. *
  21974. * @function Highcharts.Axis#addPlotBand
  21975. *
  21976. * @param {Highcharts.AxisPlotBandsOptions} options
  21977. * A configuration object for the plot band, as defined in
  21978. * [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
  21979. *
  21980. * @return {Highcharts.PlotLineOrBand|undefined}
  21981. * The added plot band.
  21982. */
  21983. function addPlotBand(options) {
  21984. return this.addPlotBandOrLine(options, 'plotBands');
  21985. }
  21986. /**
  21987. * Add a plot band or plot line after render time. Called from
  21988. * addPlotBand and addPlotLine internally.
  21989. *
  21990. * @private
  21991. * @function Highcharts.Axis#addPlotBandOrLine
  21992. * @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
  21993. * The plotBand or plotLine configuration object.
  21994. */
  21995. function addPlotBandOrLine(options, coll) {
  21996. const userOptions = this.userOptions;
  21997. let obj = new PlotLineOrBandClass(this, options);
  21998. if (this.visible) {
  21999. obj = obj.render();
  22000. }
  22001. if (obj) { // #2189
  22002. if (!this._addedPlotLB) {
  22003. this._addedPlotLB = true;
  22004. (userOptions.plotLines || [])
  22005. .concat(userOptions.plotBands || [])
  22006. .forEach((plotLineOptions) => {
  22007. this.addPlotBandOrLine(plotLineOptions);
  22008. });
  22009. }
  22010. // Add it to the user options for exporting and Axis.update
  22011. if (coll) {
  22012. // Workaround Microsoft/TypeScript issue #32693
  22013. const updatedOptions = (userOptions[coll] || []);
  22014. updatedOptions.push(options);
  22015. userOptions[coll] = updatedOptions;
  22016. }
  22017. this.plotLinesAndBands.push(obj);
  22018. }
  22019. return obj;
  22020. }
  22021. /**
  22022. * Add a plot line after render time.
  22023. *
  22024. * @sample highcharts/members/axis-addplotline/
  22025. * Toggle the plot line from a button
  22026. *
  22027. * @function Highcharts.Axis#addPlotLine
  22028. *
  22029. * @param {Highcharts.AxisPlotLinesOptions} options
  22030. * A configuration object for the plot line, as defined in
  22031. * [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
  22032. *
  22033. * @return {Highcharts.PlotLineOrBand|undefined}
  22034. * The added plot line.
  22035. */
  22036. function addPlotLine(options) {
  22037. return this.addPlotBandOrLine(options, 'plotLines');
  22038. }
  22039. /**
  22040. * @private
  22041. */
  22042. function compose(PlotLineOrBandType, AxisClass) {
  22043. if (!PlotLineOrBandClass) {
  22044. PlotLineOrBandClass = PlotLineOrBandType;
  22045. }
  22046. if (U.pushUnique(composedMembers, AxisClass)) {
  22047. extend(AxisClass.prototype, {
  22048. addPlotBand,
  22049. addPlotLine,
  22050. addPlotBandOrLine,
  22051. getPlotBandPath,
  22052. removePlotBand,
  22053. removePlotLine,
  22054. removePlotBandOrLine
  22055. });
  22056. }
  22057. return AxisClass;
  22058. }
  22059. PlotLineOrBandAxis.compose = compose;
  22060. /**
  22061. * Internal function to create the SVG path definition for a plot band.
  22062. *
  22063. * @function Highcharts.Axis#getPlotBandPath
  22064. *
  22065. * @param {number} from
  22066. * The axis value to start from.
  22067. *
  22068. * @param {number} to
  22069. * The axis value to end on.
  22070. *
  22071. * @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
  22072. * The plotBand or plotLine configuration object.
  22073. *
  22074. * @return {Highcharts.SVGPathArray}
  22075. * The SVG path definition in array form.
  22076. */
  22077. function getPlotBandPath(from, to, options = this.options) {
  22078. const toPath = this.getPlotLinePath({
  22079. value: to,
  22080. force: true,
  22081. acrossPanes: options.acrossPanes
  22082. }), result = [], horiz = this.horiz, outside = !isNumber(this.min) ||
  22083. !isNumber(this.max) ||
  22084. (from < this.min && to < this.min) ||
  22085. (from > this.max && to > this.max);
  22086. let path = this.getPlotLinePath({
  22087. value: from,
  22088. force: true,
  22089. acrossPanes: options.acrossPanes
  22090. }), i,
  22091. // #4964 check if chart is inverted or plotband is on yAxis
  22092. plus = 1, isFlat;
  22093. if (path && toPath) {
  22094. // Flat paths don't need labels (#3836)
  22095. if (outside) {
  22096. isFlat = path.toString() === toPath.toString();
  22097. plus = 0;
  22098. }
  22099. // Go over each subpath - for panes in Highcharts Stock
  22100. for (i = 0; i < path.length; i += 2) {
  22101. const pathStart = path[i], pathEnd = path[i + 1], toPathStart = toPath[i], toPathEnd = toPath[i + 1];
  22102. // Type checking all affected path segments. Consider
  22103. // something smarter.
  22104. if ((pathStart[0] === 'M' || pathStart[0] === 'L') &&
  22105. (pathEnd[0] === 'M' || pathEnd[0] === 'L') &&
  22106. (toPathStart[0] === 'M' || toPathStart[0] === 'L') &&
  22107. (toPathEnd[0] === 'M' || toPathEnd[0] === 'L')) {
  22108. // Add 1 pixel when coordinates are the same
  22109. if (horiz && toPathStart[1] === pathStart[1]) {
  22110. toPathStart[1] += plus;
  22111. toPathEnd[1] += plus;
  22112. }
  22113. else if (!horiz && toPathStart[2] === pathStart[2]) {
  22114. toPathStart[2] += plus;
  22115. toPathEnd[2] += plus;
  22116. }
  22117. result.push(['M', pathStart[1], pathStart[2]], ['L', pathEnd[1], pathEnd[2]], ['L', toPathEnd[1], toPathEnd[2]], ['L', toPathStart[1], toPathStart[2]], ['Z']);
  22118. }
  22119. result.isFlat = isFlat;
  22120. }
  22121. }
  22122. else { // outside the axis area
  22123. path = null;
  22124. }
  22125. return result;
  22126. }
  22127. /**
  22128. * Remove a plot band by its id.
  22129. *
  22130. * @sample highcharts/members/axis-removeplotband/
  22131. * Remove plot band by id
  22132. * @sample highcharts/members/axis-addplotband/
  22133. * Toggle the plot band from a button
  22134. *
  22135. * @function Highcharts.Axis#removePlotBand
  22136. *
  22137. * @param {string} id
  22138. * The plot band's `id` as given in the original configuration
  22139. * object or in the `addPlotBand` option.
  22140. */
  22141. function removePlotBand(id) {
  22142. this.removePlotBandOrLine(id);
  22143. }
  22144. /**
  22145. * Remove a plot band or plot line from the chart by id. Called
  22146. * internally from `removePlotBand` and `removePlotLine`.
  22147. * @private
  22148. * @function Highcharts.Axis#removePlotBandOrLine
  22149. */
  22150. function removePlotBandOrLine(id) {
  22151. const plotLinesAndBands = this.plotLinesAndBands, options = this.options, userOptions = this.userOptions;
  22152. if (plotLinesAndBands) { // #15639
  22153. let i = plotLinesAndBands.length;
  22154. while (i--) {
  22155. if (plotLinesAndBands[i].id === id) {
  22156. plotLinesAndBands[i].destroy();
  22157. }
  22158. }
  22159. ([
  22160. options.plotLines || [],
  22161. userOptions.plotLines || [],
  22162. options.plotBands || [],
  22163. userOptions.plotBands || []
  22164. ]).forEach(function (arr) {
  22165. i = arr.length;
  22166. while (i--) {
  22167. if ((arr[i] || {}).id === id) {
  22168. erase(arr, arr[i]);
  22169. }
  22170. }
  22171. });
  22172. }
  22173. }
  22174. /**
  22175. * Remove a plot line by its id.
  22176. *
  22177. * @sample highcharts/xaxis/plotlines-id/
  22178. * Remove plot line by id
  22179. * @sample highcharts/members/axis-addplotline/
  22180. * Toggle the plot line from a button
  22181. *
  22182. * @function Highcharts.Axis#removePlotLine
  22183. *
  22184. * @param {string} id
  22185. * The plot line's `id` as given in the original configuration
  22186. * object or in the `addPlotLine` option.
  22187. */
  22188. function removePlotLine(id) {
  22189. this.removePlotBandOrLine(id);
  22190. }
  22191. })(PlotLineOrBandAxis || (PlotLineOrBandAxis = {}));
  22192. /* *
  22193. *
  22194. * Default Export
  22195. *
  22196. * */
  22197. return PlotLineOrBandAxis;
  22198. });
  22199. _registerModule(_modules, 'Core/Axis/PlotLineOrBand/PlotLineOrBand.js', [_modules['Core/Axis/PlotLineOrBand/PlotLineOrBandAxis.js'], _modules['Core/Utilities.js']], function (PlotLineOrBandAxis, U) {
  22200. /* *
  22201. *
  22202. * (c) 2010-2021 Torstein Honsi
  22203. *
  22204. * License: www.highcharts.com/license
  22205. *
  22206. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  22207. *
  22208. * */
  22209. const { arrayMax, arrayMin, defined, destroyObjectProperties, erase, fireEvent, merge, objectEach, pick } = U;
  22210. /* *
  22211. *
  22212. * Class
  22213. *
  22214. * */
  22215. /**
  22216. * The object wrapper for plot lines and plot bands
  22217. *
  22218. * @class
  22219. * @name Highcharts.PlotLineOrBand
  22220. *
  22221. * @param {Highcharts.Axis} axis
  22222. * Related axis.
  22223. *
  22224. * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} [options]
  22225. * Options to use.
  22226. */
  22227. class PlotLineOrBand {
  22228. /* *
  22229. *
  22230. * Static Functions
  22231. *
  22232. * */
  22233. static compose(AxisClass) {
  22234. return PlotLineOrBandAxis.compose(PlotLineOrBand, AxisClass);
  22235. }
  22236. /* *
  22237. *
  22238. * Constructor
  22239. *
  22240. * */
  22241. constructor(axis, options) {
  22242. this.axis = axis;
  22243. if (options) {
  22244. this.options = options;
  22245. this.id = options.id;
  22246. }
  22247. }
  22248. /* *
  22249. *
  22250. * Functions
  22251. *
  22252. * */
  22253. /* eslint-disable no-invalid-this, valid-jsdoc */
  22254. /**
  22255. * Render the plot line or plot band. If it is already existing,
  22256. * move it.
  22257. * @private
  22258. * @function Highcharts.PlotLineOrBand#render
  22259. */
  22260. render() {
  22261. fireEvent(this, 'render');
  22262. const plotLine = this, axis = plotLine.axis, horiz = axis.horiz, log = axis.logarithmic, options = plotLine.options, color = options.color, zIndex = pick(options.zIndex, 0), events = options.events, groupAttribs = {}, renderer = axis.chart.renderer;
  22263. let optionsLabel = options.label, label = plotLine.label, to = options.to, from = options.from, value = options.value, svgElem = plotLine.svgElem, path = [], group;
  22264. const isBand = defined(from) && defined(to), isLine = defined(value), isNew = !svgElem, attribs = {
  22265. 'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
  22266. (options.className || '')
  22267. };
  22268. let groupName = isBand ? 'bands' : 'lines';
  22269. // logarithmic conversion
  22270. if (log) {
  22271. from = log.log2lin(from);
  22272. to = log.log2lin(to);
  22273. value = log.log2lin(value);
  22274. }
  22275. // Set the presentational attributes
  22276. if (!axis.chart.styledMode) {
  22277. if (isLine) {
  22278. attribs.stroke = color || "#999999" /* Palette.neutralColor40 */;
  22279. attribs['stroke-width'] = pick(options.width, 1);
  22280. if (options.dashStyle) {
  22281. attribs.dashstyle =
  22282. options.dashStyle;
  22283. }
  22284. }
  22285. else if (isBand) { // plot band
  22286. attribs.fill = color || "#e6e9ff" /* Palette.highlightColor10 */;
  22287. if (options.borderWidth) {
  22288. attribs.stroke = options.borderColor;
  22289. attribs['stroke-width'] = options.borderWidth;
  22290. }
  22291. }
  22292. }
  22293. // Grouping and zIndex
  22294. groupAttribs.zIndex = zIndex;
  22295. groupName += '-' + zIndex;
  22296. group = axis.plotLinesAndBandsGroups[groupName];
  22297. if (!group) {
  22298. axis.plotLinesAndBandsGroups[groupName] = group =
  22299. renderer.g('plot-' + groupName)
  22300. .attr(groupAttribs).add();
  22301. }
  22302. // Create the path
  22303. if (isNew) {
  22304. /**
  22305. * SVG element of the plot line or band.
  22306. *
  22307. * @name Highcharts.PlotLineOrBand#svgElem
  22308. * @type {Highcharts.SVGElement}
  22309. */
  22310. plotLine.svgElem = svgElem = renderer
  22311. .path()
  22312. .attr(attribs)
  22313. .add(group);
  22314. }
  22315. // Set the path or return
  22316. if (isLine) {
  22317. path = axis.getPlotLinePath({
  22318. value: value,
  22319. lineWidth: svgElem.strokeWidth(),
  22320. acrossPanes: options.acrossPanes
  22321. });
  22322. }
  22323. else if (isBand) { // plot band
  22324. path = axis.getPlotBandPath(from, to, options);
  22325. }
  22326. else {
  22327. return;
  22328. }
  22329. // common for lines and bands
  22330. // Add events only if they were not added before.
  22331. if (!plotLine.eventsAdded && events) {
  22332. objectEach(events, function (event, eventType) {
  22333. svgElem.on(eventType, function (e) {
  22334. events[eventType].apply(plotLine, [e]);
  22335. });
  22336. });
  22337. plotLine.eventsAdded = true;
  22338. }
  22339. if ((isNew || !svgElem.d) && path && path.length) {
  22340. svgElem.attr({ d: path });
  22341. }
  22342. else if (svgElem) {
  22343. if (path) {
  22344. svgElem.show();
  22345. svgElem.animate({ d: path });
  22346. }
  22347. else if (svgElem.d) {
  22348. svgElem.hide();
  22349. if (label) {
  22350. plotLine.label = label = label.destroy();
  22351. }
  22352. }
  22353. }
  22354. // the plot band/line label
  22355. if (optionsLabel &&
  22356. (defined(optionsLabel.text) || defined(optionsLabel.formatter)) &&
  22357. path &&
  22358. path.length &&
  22359. axis.width > 0 &&
  22360. axis.height > 0 &&
  22361. !path.isFlat) {
  22362. // apply defaults
  22363. optionsLabel = merge({
  22364. align: horiz && isBand && 'center',
  22365. x: horiz ? !isBand && 4 : 10,
  22366. verticalAlign: !horiz && isBand && 'middle',
  22367. y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
  22368. rotation: horiz && !isBand && 90
  22369. }, optionsLabel);
  22370. this.renderLabel(optionsLabel, path, isBand, zIndex);
  22371. }
  22372. else if (label) { // move out of sight
  22373. label.hide();
  22374. }
  22375. // chainable
  22376. return plotLine;
  22377. }
  22378. /**
  22379. * Render and align label for plot line or band.
  22380. * @private
  22381. * @function Highcharts.PlotLineOrBand#renderLabel
  22382. */
  22383. renderLabel(optionsLabel, path, isBand, zIndex) {
  22384. const plotLine = this, axis = plotLine.axis, renderer = axis.chart.renderer;
  22385. let label = plotLine.label;
  22386. // add the SVG element
  22387. if (!label) {
  22388. /**
  22389. * SVG element of the label.
  22390. *
  22391. * @name Highcharts.PlotLineOrBand#label
  22392. * @type {Highcharts.SVGElement}
  22393. */
  22394. plotLine.label = label = renderer
  22395. .text(this.getLabelText(optionsLabel), 0, 0, optionsLabel.useHTML)
  22396. .attr({
  22397. align: optionsLabel.textAlign || optionsLabel.align,
  22398. rotation: optionsLabel.rotation,
  22399. 'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
  22400. '-label ' + (optionsLabel.className || ''),
  22401. zIndex
  22402. })
  22403. .add();
  22404. if (!axis.chart.styledMode) {
  22405. label.css(merge({
  22406. fontSize: '0.8em',
  22407. textOverflow: 'ellipsis'
  22408. }, optionsLabel.style));
  22409. }
  22410. }
  22411. // get the bounding box and align the label
  22412. // #3000 changed to better handle choice between plotband or plotline
  22413. const xBounds = path.xBounds ||
  22414. [path[0][1], path[1][1], (isBand ? path[2][1] : path[0][1])];
  22415. const yBounds = path.yBounds ||
  22416. [path[0][2], path[1][2], (isBand ? path[2][2] : path[0][2])];
  22417. const x = arrayMin(xBounds);
  22418. const y = arrayMin(yBounds);
  22419. label.align(optionsLabel, false, {
  22420. x,
  22421. y,
  22422. width: arrayMax(xBounds) - x,
  22423. height: arrayMax(yBounds) - y
  22424. });
  22425. if (!label.alignValue || label.alignValue === 'left') {
  22426. const width = optionsLabel.clip ?
  22427. axis.width : axis.chart.chartWidth;
  22428. label.css({
  22429. width: (label.rotation === 90 ?
  22430. axis.height - (label.alignAttr.y - axis.top) :
  22431. width - (label.alignAttr.x - axis.left)) + 'px'
  22432. });
  22433. }
  22434. label.show(true);
  22435. }
  22436. /**
  22437. * Get label's text content.
  22438. * @private
  22439. * @function Highcharts.PlotLineOrBand#getLabelText
  22440. */
  22441. getLabelText(optionsLabel) {
  22442. return defined(optionsLabel.formatter) ?
  22443. optionsLabel.formatter
  22444. .call(this) :
  22445. optionsLabel.text;
  22446. }
  22447. /**
  22448. * Remove the plot line or band.
  22449. *
  22450. * @function Highcharts.PlotLineOrBand#destroy
  22451. */
  22452. destroy() {
  22453. // remove it from the lookup
  22454. erase(this.axis.plotLinesAndBands, this);
  22455. delete this.axis;
  22456. destroyObjectProperties(this);
  22457. }
  22458. }
  22459. /* *
  22460. *
  22461. * Default Export
  22462. *
  22463. * */
  22464. /* *
  22465. *
  22466. * API Options
  22467. *
  22468. * */
  22469. /**
  22470. * Options for plot bands on axes.
  22471. *
  22472. * @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
  22473. */
  22474. /**
  22475. * Options for plot band labels on axes.
  22476. *
  22477. * @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
  22478. */
  22479. /**
  22480. * Options for plot lines on axes.
  22481. *
  22482. * @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
  22483. */
  22484. /**
  22485. * Options for plot line labels on axes.
  22486. *
  22487. * @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
  22488. */
  22489. ('');
  22490. /* *
  22491. *
  22492. * API Options
  22493. *
  22494. * */
  22495. /**
  22496. * An array of colored bands stretching across the plot area marking an
  22497. * interval on the axis.
  22498. *
  22499. * In styled mode, the plot bands are styled by the `.highcharts-plot-band`
  22500. * class in addition to the `className` option.
  22501. *
  22502. * @productdesc {highcharts}
  22503. * In a gauge, a plot band on the Y axis (value axis) will stretch along the
  22504. * perimeter of the gauge.
  22505. *
  22506. * @type {Array<*>}
  22507. * @product highcharts highstock gantt
  22508. * @apioption xAxis.plotBands
  22509. */
  22510. /**
  22511. * Flag to decide if plotBand should be rendered across all panes.
  22512. *
  22513. * @since 7.1.2
  22514. * @product highstock
  22515. * @type {boolean}
  22516. * @default true
  22517. * @apioption xAxis.plotBands.acrossPanes
  22518. */
  22519. /**
  22520. * Border color for the plot band. Also requires `borderWidth` to be set.
  22521. *
  22522. * @type {Highcharts.ColorString}
  22523. * @apioption xAxis.plotBands.borderColor
  22524. */
  22525. /**
  22526. * Border width for the plot band. Also requires `borderColor` to be set.
  22527. *
  22528. * @type {number}
  22529. * @default 0
  22530. * @apioption xAxis.plotBands.borderWidth
  22531. */
  22532. /**
  22533. * A custom class name, in addition to the default `highcharts-plot-band`,
  22534. * to apply to each individual band.
  22535. *
  22536. * @type {string}
  22537. * @since 5.0.0
  22538. * @apioption xAxis.plotBands.className
  22539. */
  22540. /**
  22541. * The color of the plot band.
  22542. *
  22543. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22544. * Color band
  22545. * @sample {highstock} stock/xaxis/plotbands/
  22546. * Plot band on Y axis
  22547. *
  22548. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  22549. * @default #e6e9ff
  22550. * @apioption xAxis.plotBands.color
  22551. */
  22552. /**
  22553. * An object defining mouse events for the plot band. Supported properties
  22554. * are `click`, `mouseover`, `mouseout`, `mousemove`.
  22555. *
  22556. * @sample {highcharts} highcharts/xaxis/plotbands-events/
  22557. * Mouse events demonstrated
  22558. *
  22559. * @since 1.2
  22560. * @apioption xAxis.plotBands.events
  22561. */
  22562. /**
  22563. * Click event on a plot band.
  22564. *
  22565. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22566. * @apioption xAxis.plotBands.events.click
  22567. */
  22568. /**
  22569. * Mouse move event on a plot band.
  22570. *
  22571. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22572. * @apioption xAxis.plotBands.events.mousemove
  22573. */
  22574. /**
  22575. * Mouse out event on the corner of a plot band.
  22576. *
  22577. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22578. * @apioption xAxis.plotBands.events.mouseout
  22579. */
  22580. /**
  22581. * Mouse over event on a plot band.
  22582. *
  22583. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22584. * @apioption xAxis.plotBands.events.mouseover
  22585. */
  22586. /**
  22587. * The start position of the plot band in axis units.
  22588. *
  22589. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22590. * Datetime axis
  22591. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  22592. * Categorized axis
  22593. * @sample {highstock} stock/xaxis/plotbands/
  22594. * Plot band on Y axis
  22595. *
  22596. * @type {number}
  22597. * @apioption xAxis.plotBands.from
  22598. */
  22599. /**
  22600. * An id used for identifying the plot band in Axis.removePlotBand.
  22601. *
  22602. * @sample {highcharts} highcharts/xaxis/plotbands-id/
  22603. * Remove plot band by id
  22604. * @sample {highstock} highcharts/xaxis/plotbands-id/
  22605. * Remove plot band by id
  22606. *
  22607. * @type {string}
  22608. * @apioption xAxis.plotBands.id
  22609. */
  22610. /**
  22611. * The end position of the plot band in axis units.
  22612. *
  22613. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22614. * Datetime axis
  22615. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  22616. * Categorized axis
  22617. * @sample {highstock} stock/xaxis/plotbands/
  22618. * Plot band on Y axis
  22619. *
  22620. * @type {number}
  22621. * @apioption xAxis.plotBands.to
  22622. */
  22623. /**
  22624. * The z index of the plot band within the chart, relative to other
  22625. * elements. Using the same z index as another element may give
  22626. * unpredictable results, as the last rendered element will be on top.
  22627. * Values from 0 to 20 make sense.
  22628. *
  22629. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  22630. * Behind plot lines by default
  22631. * @sample {highcharts} highcharts/xaxis/plotbands-zindex/
  22632. * Above plot lines
  22633. * @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
  22634. * Above plot lines and series
  22635. *
  22636. * @type {number}
  22637. * @since 1.2
  22638. * @apioption xAxis.plotBands.zIndex
  22639. */
  22640. /**
  22641. * Text labels for the plot bands
  22642. *
  22643. * @product highcharts highstock gantt
  22644. * @apioption xAxis.plotBands.label
  22645. */
  22646. /**
  22647. * Horizontal alignment of the label. Can be one of "left", "center" or
  22648. * "right".
  22649. *
  22650. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  22651. * Aligned to the right
  22652. * @sample {highstock} stock/xaxis/plotbands-label/
  22653. * Plot band with labels
  22654. *
  22655. * @type {Highcharts.AlignValue}
  22656. * @default center
  22657. * @since 2.1
  22658. * @apioption xAxis.plotBands.label.align
  22659. */
  22660. /**
  22661. * Rotation of the text label in degrees .
  22662. *
  22663. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  22664. * Vertical text
  22665. *
  22666. * @type {number}
  22667. * @default 0
  22668. * @since 2.1
  22669. * @apioption xAxis.plotBands.label.rotation
  22670. */
  22671. /**
  22672. * CSS styles for the text label.
  22673. *
  22674. * In styled mode, the labels are styled by the
  22675. * `.highcharts-plot-band-label` class.
  22676. *
  22677. * @sample {highcharts} highcharts/xaxis/plotbands-label-style/
  22678. * Blue and bold label
  22679. *
  22680. * @type {Highcharts.CSSObject}
  22681. * @since 2.1
  22682. * @apioption xAxis.plotBands.label.style
  22683. */
  22684. /**
  22685. * The string text itself. A subset of HTML is supported.
  22686. *
  22687. * @type {string}
  22688. * @since 2.1
  22689. * @apioption xAxis.plotBands.label.text
  22690. */
  22691. /**
  22692. * The text alignment for the label. While `align` determines where the
  22693. * texts anchor point is placed within the plot band, `textAlign` determines
  22694. * how the text is aligned against its anchor point. Possible values are
  22695. * "left", "center" and "right". Defaults to the same as the `align` option.
  22696. *
  22697. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  22698. * Vertical text in center position but text-aligned left
  22699. *
  22700. * @type {Highcharts.AlignValue}
  22701. * @since 2.1
  22702. * @apioption xAxis.plotBands.label.textAlign
  22703. */
  22704. /**
  22705. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  22706. * to render the labels.
  22707. *
  22708. * @type {boolean}
  22709. * @default false
  22710. * @since 3.0.3
  22711. * @apioption xAxis.plotBands.label.useHTML
  22712. */
  22713. /**
  22714. * Vertical alignment of the label relative to the plot band. Can be one of
  22715. * "top", "middle" or "bottom".
  22716. *
  22717. * @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
  22718. * Vertically centered label
  22719. * @sample {highstock} stock/xaxis/plotbands-label/
  22720. * Plot band with labels
  22721. *
  22722. * @type {Highcharts.VerticalAlignValue}
  22723. * @default top
  22724. * @since 2.1
  22725. * @apioption xAxis.plotBands.label.verticalAlign
  22726. */
  22727. /**
  22728. * Horizontal position relative the alignment. Default varies by
  22729. * orientation.
  22730. *
  22731. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  22732. * Aligned 10px from the right edge
  22733. * @sample {highstock} stock/xaxis/plotbands-label/
  22734. * Plot band with labels
  22735. *
  22736. * @type {number}
  22737. * @since 2.1
  22738. * @apioption xAxis.plotBands.label.x
  22739. */
  22740. /**
  22741. * Vertical position of the text baseline relative to the alignment. Default
  22742. * varies by orientation.
  22743. *
  22744. * @sample {highcharts} highcharts/xaxis/plotbands-label-y/
  22745. * Label on x axis
  22746. * @sample {highstock} stock/xaxis/plotbands-label/
  22747. * Plot band with labels
  22748. *
  22749. * @type {number}
  22750. * @since 2.1
  22751. * @apioption xAxis.plotBands.label.y
  22752. */
  22753. /**
  22754. * An array of lines stretching across the plot area, marking a specific
  22755. * value on one of the axes.
  22756. *
  22757. * In styled mode, the plot lines are styled by the
  22758. * `.highcharts-plot-line` class in addition to the `className` option.
  22759. *
  22760. * @type {Array<*>}
  22761. * @product highcharts highstock gantt
  22762. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22763. * Basic plot line
  22764. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  22765. * Solid gauge plot line
  22766. * @apioption xAxis.plotLines
  22767. */
  22768. /**
  22769. * Flag to decide if plotLine should be rendered across all panes.
  22770. *
  22771. * @sample {highstock} stock/xaxis/plotlines-acrosspanes/
  22772. * Plot lines on different panes
  22773. *
  22774. * @since 7.1.2
  22775. * @product highstock
  22776. * @type {boolean}
  22777. * @default true
  22778. * @apioption xAxis.plotLines.acrossPanes
  22779. */
  22780. /**
  22781. * A custom class name, in addition to the default `highcharts-plot-line`,
  22782. * to apply to each individual line.
  22783. *
  22784. * @type {string}
  22785. * @since 5.0.0
  22786. * @apioption xAxis.plotLines.className
  22787. */
  22788. /**
  22789. * The color of the line.
  22790. *
  22791. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22792. * A red line from X axis
  22793. * @sample {highstock} stock/xaxis/plotlines/
  22794. * Plot line on Y axis
  22795. *
  22796. * @type {Highcharts.ColorString}
  22797. * @default #999999
  22798. * @apioption xAxis.plotLines.color
  22799. */
  22800. /**
  22801. * The dashing or dot style for the plot line. For possible values see
  22802. * [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  22803. *
  22804. * @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
  22805. * Dash and dot pattern
  22806. * @sample {highstock} stock/xaxis/plotlines/
  22807. * Plot line on Y axis
  22808. *
  22809. * @type {Highcharts.DashStyleValue}
  22810. * @default Solid
  22811. * @since 1.2
  22812. * @apioption xAxis.plotLines.dashStyle
  22813. */
  22814. /**
  22815. * An object defining mouse events for the plot line. Supported
  22816. * properties are `click`, `mouseover`, `mouseout`, `mousemove`.
  22817. *
  22818. * @sample {highcharts} highcharts/xaxis/plotlines-events/
  22819. * Mouse events demonstrated
  22820. *
  22821. * @since 1.2
  22822. * @apioption xAxis.plotLines.events
  22823. */
  22824. /**
  22825. * Click event on a plot band.
  22826. *
  22827. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22828. * @apioption xAxis.plotLines.events.click
  22829. */
  22830. /**
  22831. * Mouse move event on a plot band.
  22832. *
  22833. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22834. * @apioption xAxis.plotLines.events.mousemove
  22835. */
  22836. /**
  22837. * Mouse out event on the corner of a plot band.
  22838. *
  22839. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22840. * @apioption xAxis.plotLines.events.mouseout
  22841. */
  22842. /**
  22843. * Mouse over event on a plot band.
  22844. *
  22845. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  22846. * @apioption xAxis.plotLines.events.mouseover
  22847. */
  22848. /**
  22849. * An id used for identifying the plot line in Axis.removePlotLine.
  22850. *
  22851. * @sample {highcharts} highcharts/xaxis/plotlines-id/
  22852. * Remove plot line by id
  22853. *
  22854. * @type {string}
  22855. * @apioption xAxis.plotLines.id
  22856. */
  22857. /**
  22858. * The position of the line in axis units.
  22859. *
  22860. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22861. * Between two categories on X axis
  22862. * @sample {highstock} stock/xaxis/plotlines/
  22863. * Plot line on Y axis
  22864. *
  22865. * @type {number}
  22866. * @apioption xAxis.plotLines.value
  22867. */
  22868. /**
  22869. * The width or thickness of the plot line.
  22870. *
  22871. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  22872. * 2px wide line from X axis
  22873. * @sample {highstock} stock/xaxis/plotlines/
  22874. * Plot line on Y axis
  22875. *
  22876. * @type {number}
  22877. * @default 2
  22878. * @apioption xAxis.plotLines.width
  22879. */
  22880. /**
  22881. * The z index of the plot line within the chart.
  22882. *
  22883. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
  22884. * Behind plot lines by default
  22885. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
  22886. * Above plot lines
  22887. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
  22888. * Above plot lines and series
  22889. *
  22890. * @type {number}
  22891. * @since 1.2
  22892. * @apioption xAxis.plotLines.zIndex
  22893. */
  22894. /**
  22895. * Text labels for the plot bands
  22896. *
  22897. * @apioption xAxis.plotLines.label
  22898. */
  22899. /**
  22900. * Horizontal alignment of the label. Can be one of "left", "center" or
  22901. * "right".
  22902. *
  22903. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  22904. * Aligned to the right
  22905. * @sample {highstock} stock/xaxis/plotlines/
  22906. * Plot line on Y axis
  22907. *
  22908. * @type {Highcharts.AlignValue}
  22909. * @default left
  22910. * @since 2.1
  22911. * @apioption xAxis.plotLines.label.align
  22912. */
  22913. /**
  22914. * Whether to hide labels that are outside the plot area.
  22915. *
  22916. * @type {boolean}
  22917. * @default false
  22918. * @since 10.3.3
  22919. * @apioption xAxis.plotLines.labels.clip
  22920. */
  22921. /**
  22922. * Callback JavaScript function to format the label. Useful properties like
  22923. * the value of plot line or the range of plot band (`from` & `to`
  22924. * properties) can be found in `this.options` object.
  22925. *
  22926. * @sample {highcharts} highcharts/xaxis/plotlines-plotbands-label-formatter
  22927. * Label formatters for plot line and plot band.
  22928. * @type {Highcharts.FormatterCallbackFunction<Highcharts.PlotLineOrBand>}
  22929. * @apioption xAxis.plotLines.label.formatter
  22930. */
  22931. /**
  22932. * Rotation of the text label in degrees. Defaults to 0 for horizontal plot
  22933. * lines and 90 for vertical lines.
  22934. *
  22935. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  22936. * Slanted text
  22937. *
  22938. * @type {number}
  22939. * @since 2.1
  22940. * @apioption xAxis.plotLines.label.rotation
  22941. */
  22942. /**
  22943. * CSS styles for the text label.
  22944. *
  22945. * In styled mode, the labels are styled by the
  22946. * `.highcharts-plot-line-label` class.
  22947. *
  22948. * @sample {highcharts} highcharts/xaxis/plotlines-label-style/
  22949. * Blue and bold label
  22950. *
  22951. * @type {Highcharts.CSSObject}
  22952. * @since 2.1
  22953. * @apioption xAxis.plotLines.label.style
  22954. */
  22955. /**
  22956. * The text itself. A subset of HTML is supported.
  22957. *
  22958. * @type {string}
  22959. * @since 2.1
  22960. * @apioption xAxis.plotLines.label.text
  22961. */
  22962. /**
  22963. * The text alignment for the label. While `align` determines where the
  22964. * texts anchor point is placed within the plot band, `textAlign` determines
  22965. * how the text is aligned against its anchor point. Possible values are
  22966. * "left", "center" and "right". Defaults to the same as the `align` option.
  22967. *
  22968. * @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
  22969. * Text label in bottom position
  22970. *
  22971. * @type {Highcharts.AlignValue}
  22972. * @since 2.1
  22973. * @apioption xAxis.plotLines.label.textAlign
  22974. */
  22975. /**
  22976. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  22977. * to render the labels.
  22978. *
  22979. * @type {boolean}
  22980. * @default false
  22981. * @since 3.0.3
  22982. * @apioption xAxis.plotLines.label.useHTML
  22983. */
  22984. /**
  22985. * Vertical alignment of the label relative to the plot line. Can be
  22986. * one of "top", "middle" or "bottom".
  22987. *
  22988. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  22989. * Vertically centered label
  22990. *
  22991. * @type {Highcharts.VerticalAlignValue}
  22992. * @default {highcharts} top
  22993. * @default {highstock} top
  22994. * @since 2.1
  22995. * @apioption xAxis.plotLines.label.verticalAlign
  22996. */
  22997. /**
  22998. * Horizontal position relative the alignment. Default varies by
  22999. * orientation.
  23000. *
  23001. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  23002. * Aligned 10px from the right edge
  23003. * @sample {highstock} stock/xaxis/plotlines/
  23004. * Plot line on Y axis
  23005. *
  23006. * @type {number}
  23007. * @since 2.1
  23008. * @apioption xAxis.plotLines.label.x
  23009. */
  23010. /**
  23011. * Vertical position of the text baseline relative to the alignment. Default
  23012. * varies by orientation.
  23013. *
  23014. * @sample {highcharts} highcharts/xaxis/plotlines-label-y/
  23015. * Label below the plot line
  23016. * @sample {highstock} stock/xaxis/plotlines/
  23017. * Plot line on Y axis
  23018. *
  23019. * @type {number}
  23020. * @since 2.1
  23021. * @apioption xAxis.plotLines.label.y
  23022. */
  23023. /**
  23024. * @type {Array<*>}
  23025. * @extends xAxis.plotBands
  23026. * @apioption yAxis.plotBands
  23027. */
  23028. /**
  23029. * In a gauge chart, this option determines the inner radius of the
  23030. * plot band that stretches along the perimeter. It can be given as
  23031. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  23032. * By default, the inner radius is controlled by the [thickness](
  23033. * #yAxis.plotBands.thickness) option.
  23034. *
  23035. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  23036. * Gauge plot band
  23037. *
  23038. * @type {number|string}
  23039. * @since 2.3
  23040. * @product highcharts
  23041. * @apioption yAxis.plotBands.innerRadius
  23042. */
  23043. /**
  23044. * In a gauge chart, this option determines the outer radius of the
  23045. * plot band that stretches along the perimeter. It can be given as
  23046. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  23047. *
  23048. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  23049. * Gauge plot band
  23050. *
  23051. * @type {number|string}
  23052. * @default 100%
  23053. * @since 2.3
  23054. * @product highcharts
  23055. * @apioption yAxis.plotBands.outerRadius
  23056. */
  23057. /**
  23058. * In a gauge chart, this option sets the width of the plot band
  23059. * stretching along the perimeter. It can be given as a percentage
  23060. * string, like `"10%"`, or as a pixel number, like `10`. The default
  23061. * value 10 is the same as the default [tickLength](#yAxis.tickLength),
  23062. * thus making the plot band act as a background for the tick markers.
  23063. *
  23064. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  23065. * Gauge plot band
  23066. *
  23067. * @type {number|string}
  23068. * @default 10
  23069. * @since 2.3
  23070. * @product highcharts
  23071. * @apioption yAxis.plotBands.thickness
  23072. */
  23073. /**
  23074. * @type {Array<*>}
  23075. * @extends xAxis.plotLines
  23076. * @apioption yAxis.plotLines
  23077. */
  23078. (''); // keeps doclets above in JS file
  23079. return PlotLineOrBand;
  23080. });
  23081. _registerModule(_modules, 'Core/Tooltip.js', [_modules['Core/Templating.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Utilities.js']], function (F, H, R, RendererRegistry, U) {
  23082. /* *
  23083. *
  23084. * (c) 2010-2021 Torstein Honsi
  23085. *
  23086. * License: www.highcharts.com/license
  23087. *
  23088. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  23089. *
  23090. * */
  23091. const { format } = F;
  23092. const { doc, isSafari } = H;
  23093. const { distribute } = R;
  23094. const { addEvent, clamp, css, discardElement, extend, fireEvent, isArray, isNumber, isString, merge, pick, splat, syncTimeout } = U;
  23095. /* *
  23096. *
  23097. * Class
  23098. *
  23099. * */
  23100. /* eslint-disable no-invalid-this, valid-jsdoc */
  23101. /**
  23102. * Tooltip of a chart.
  23103. *
  23104. * @class
  23105. * @name Highcharts.Tooltip
  23106. *
  23107. * @param {Highcharts.Chart} chart
  23108. * The chart instance.
  23109. *
  23110. * @param {Highcharts.TooltipOptions} options
  23111. * Tooltip options.
  23112. */
  23113. class Tooltip {
  23114. /* *
  23115. *
  23116. * Constructors
  23117. *
  23118. * */
  23119. constructor(chart, options) {
  23120. /* *
  23121. *
  23122. * Properties
  23123. *
  23124. * */
  23125. this.allowShared = true;
  23126. this.container = void 0;
  23127. this.crosshairs = [];
  23128. this.distance = 0;
  23129. this.isHidden = true;
  23130. this.isSticky = false;
  23131. this.now = {};
  23132. this.options = {};
  23133. this.outside = false;
  23134. this.chart = chart;
  23135. this.init(chart, options);
  23136. }
  23137. /* *
  23138. *
  23139. * Functions
  23140. *
  23141. * */
  23142. /**
  23143. * Build the body (lines) of the tooltip by iterating over the items and
  23144. * returning one entry for each item, abstracting this functionality allows
  23145. * to easily overwrite and extend it.
  23146. *
  23147. * @private
  23148. * @function Highcharts.Tooltip#bodyFormatter
  23149. */
  23150. bodyFormatter(items) {
  23151. return items.map(function (item) {
  23152. const tooltipOptions = item.series.tooltipOptions;
  23153. return (tooltipOptions[(item.point.formatPrefix || 'point') + 'Formatter'] ||
  23154. item.point.tooltipFormatter).call(item.point, tooltipOptions[(item.point.formatPrefix || 'point') + 'Format'] || '');
  23155. });
  23156. }
  23157. /**
  23158. * Destroy the single tooltips in a split tooltip.
  23159. * If the tooltip is active then it is not destroyed, unless forced to.
  23160. *
  23161. * @private
  23162. * @function Highcharts.Tooltip#cleanSplit
  23163. *
  23164. * @param {boolean} [force]
  23165. * Force destroy all tooltips.
  23166. */
  23167. cleanSplit(force) {
  23168. this.chart.series.forEach(function (series) {
  23169. const tt = series && series.tt;
  23170. if (tt) {
  23171. if (!tt.isActive || force) {
  23172. series.tt = tt.destroy();
  23173. }
  23174. else {
  23175. tt.isActive = false;
  23176. }
  23177. }
  23178. });
  23179. }
  23180. /**
  23181. * In case no user defined formatter is given, this will be used. Note that
  23182. * the context here is an object holding point, series, x, y etc.
  23183. *
  23184. * @function Highcharts.Tooltip#defaultFormatter
  23185. *
  23186. * @param {Highcharts.Tooltip} tooltip
  23187. *
  23188. * @return {string|Array<string>}
  23189. * Returns a string (single tooltip and shared)
  23190. * or an array of strings (split tooltip)
  23191. */
  23192. defaultFormatter(tooltip) {
  23193. const items = this.points || splat(this);
  23194. let s;
  23195. // Build the header
  23196. s = [tooltip.tooltipFooterHeaderFormatter(items[0])];
  23197. // build the values
  23198. s = s.concat(tooltip.bodyFormatter(items));
  23199. // footer
  23200. s.push(tooltip.tooltipFooterHeaderFormatter(items[0], true));
  23201. return s;
  23202. }
  23203. /**
  23204. * Removes and destroys the tooltip and its elements.
  23205. *
  23206. * @function Highcharts.Tooltip#destroy
  23207. */
  23208. destroy() {
  23209. // Destroy and clear local variables
  23210. if (this.label) {
  23211. this.label = this.label.destroy();
  23212. }
  23213. if (this.split) {
  23214. this.cleanSplit(true);
  23215. if (this.tt) {
  23216. this.tt = this.tt.destroy();
  23217. }
  23218. }
  23219. if (this.renderer) {
  23220. this.renderer = this.renderer.destroy();
  23221. discardElement(this.container);
  23222. }
  23223. U.clearTimeout(this.hideTimer);
  23224. U.clearTimeout(this.tooltipTimeout);
  23225. }
  23226. /**
  23227. * Extendable method to get the anchor position of the tooltip
  23228. * from a point or set of points
  23229. *
  23230. * @private
  23231. * @function Highcharts.Tooltip#getAnchor
  23232. */
  23233. getAnchor(points, mouseEvent) {
  23234. const chart = this.chart, pointer = chart.pointer, inverted = chart.inverted, plotTop = chart.plotTop, plotLeft = chart.plotLeft;
  23235. let ret;
  23236. points = splat(points);
  23237. // If reversedStacks are false the tooltip position should be taken from
  23238. // the last point (#17948)
  23239. if (points[0].series &&
  23240. points[0].series.yAxis &&
  23241. !points[0].series.yAxis.options.reversedStacks) {
  23242. points = points.slice().reverse();
  23243. }
  23244. // When tooltip follows mouse, relate the position to the mouse
  23245. if (this.followPointer && mouseEvent) {
  23246. if (typeof mouseEvent.chartX === 'undefined') {
  23247. mouseEvent = pointer.normalize(mouseEvent);
  23248. }
  23249. ret = [
  23250. mouseEvent.chartX - plotLeft,
  23251. mouseEvent.chartY - plotTop
  23252. ];
  23253. // Some series types use a specificly calculated tooltip position for
  23254. // each point
  23255. }
  23256. else if (points[0].tooltipPos) {
  23257. ret = points[0].tooltipPos;
  23258. // Calculate the average position and adjust for axis positions
  23259. }
  23260. else {
  23261. let chartX = 0, chartY = 0;
  23262. points.forEach(function (point) {
  23263. const pos = point.pos(true);
  23264. if (pos) {
  23265. chartX += pos[0];
  23266. chartY += pos[1];
  23267. }
  23268. });
  23269. chartX /= points.length;
  23270. chartY /= points.length;
  23271. // When shared, place the tooltip next to the mouse (#424)
  23272. if (this.shared && points.length > 1 && mouseEvent) {
  23273. if (inverted) {
  23274. chartX = mouseEvent.chartX;
  23275. }
  23276. else {
  23277. chartY = mouseEvent.chartY;
  23278. }
  23279. }
  23280. // Use the average position for multiple points
  23281. ret = [chartX - plotLeft, chartY - plotTop];
  23282. }
  23283. return ret.map(Math.round);
  23284. }
  23285. /**
  23286. * Get the CSS class names for the tooltip's label. Styles the label
  23287. * by `colorIndex` or user-defined CSS.
  23288. *
  23289. * @function Highcharts.Tooltip#getClassName
  23290. *
  23291. * @return {string}
  23292. * The class names.
  23293. */
  23294. getClassName(point, isSplit, isHeader) {
  23295. const options = this.options, series = point.series, seriesOptions = series.options;
  23296. return [
  23297. options.className,
  23298. 'highcharts-label',
  23299. isHeader && 'highcharts-tooltip-header',
  23300. isSplit ? 'highcharts-tooltip-box' : 'highcharts-tooltip',
  23301. !isHeader && 'highcharts-color-' + pick(point.colorIndex, series.colorIndex),
  23302. (seriesOptions && seriesOptions.className)
  23303. ].filter(isString).join(' ');
  23304. }
  23305. /**
  23306. * Creates the Tooltip label element if it does not exist, then returns it.
  23307. *
  23308. * @function Highcharts.Tooltip#getLabel
  23309. *
  23310. * @return {Highcharts.SVGElement}
  23311. * Tooltip label
  23312. */
  23313. getLabel() {
  23314. const tooltip = this, styledMode = this.chart.styledMode, options = this.options, doSplit = this.split && this.allowShared, pointerEvents = (options.style.pointerEvents ||
  23315. (this.shouldStickOnContact() ? 'auto' : 'none'));
  23316. let container, renderer = this.chart.renderer;
  23317. // If changing from a split tooltip to a non-split tooltip, we must
  23318. // destroy it in order to get the SVG right. #13868.
  23319. if (this.label) {
  23320. const wasSplit = !this.label.hasClass('highcharts-label');
  23321. if ((!doSplit && wasSplit) || (doSplit && !wasSplit)) {
  23322. this.destroy();
  23323. }
  23324. }
  23325. if (!this.label) {
  23326. if (this.outside) {
  23327. const chartStyle = this.chart.options.chart.style, Renderer = RendererRegistry.getRendererType();
  23328. /**
  23329. * Reference to the tooltip's container, when
  23330. * [Highcharts.Tooltip#outside] is set to true, otherwise
  23331. * it's undefined.
  23332. *
  23333. * @name Highcharts.Tooltip#container
  23334. * @type {Highcharts.HTMLDOMElement|undefined}
  23335. */
  23336. this.container = container = H.doc.createElement('div');
  23337. container.className = 'highcharts-tooltip-container';
  23338. css(container, {
  23339. position: 'absolute',
  23340. top: '1px',
  23341. pointerEvents,
  23342. zIndex: Math.max(this.options.style.zIndex || 0, (chartStyle && chartStyle.zIndex || 0) + 3)
  23343. });
  23344. H.doc.body.appendChild(container);
  23345. /**
  23346. * Reference to the tooltip's renderer, when
  23347. * [Highcharts.Tooltip#outside] is set to true, otherwise
  23348. * it's undefined.
  23349. *
  23350. * @name Highcharts.Tooltip#renderer
  23351. * @type {Highcharts.SVGRenderer|undefined}
  23352. */
  23353. this.renderer = renderer = new Renderer(container, 0, 0, chartStyle, void 0, void 0, renderer.styledMode);
  23354. }
  23355. // Create the label
  23356. if (doSplit) {
  23357. this.label = renderer.g('tooltip');
  23358. }
  23359. else {
  23360. this.label = renderer
  23361. .label('', 0, 0, options.shape, void 0, void 0, options.useHTML, void 0, 'tooltip')
  23362. .attr({
  23363. padding: options.padding,
  23364. r: options.borderRadius
  23365. });
  23366. if (!styledMode) {
  23367. this.label
  23368. .attr({
  23369. fill: options.backgroundColor,
  23370. 'stroke-width': options.borderWidth || 0
  23371. })
  23372. // #2301, #2657
  23373. .css(options.style)
  23374. .css({ pointerEvents });
  23375. }
  23376. }
  23377. // Split tooltip use updateTooltipContainer to position the tooltip
  23378. // container.
  23379. if (tooltip.outside) {
  23380. const label = this.label;
  23381. const { xSetter, ySetter } = label;
  23382. label.xSetter = function (value) {
  23383. xSetter.call(label, tooltip.distance);
  23384. container.style.left = value + 'px';
  23385. };
  23386. label.ySetter = function (value) {
  23387. ySetter.call(label, tooltip.distance);
  23388. container.style.top = value + 'px';
  23389. };
  23390. }
  23391. this.label
  23392. .attr({ zIndex: 8 })
  23393. .shadow(options.shadow)
  23394. .add();
  23395. }
  23396. return this.label;
  23397. }
  23398. /**
  23399. * Get the total area available area to place the tooltip
  23400. *
  23401. * @private
  23402. */
  23403. getPlayingField() {
  23404. const { body, documentElement } = doc, { chart, distance, outside } = this;
  23405. return {
  23406. width: outside ?
  23407. // Substract distance to prevent scrollbars
  23408. Math.max(body.scrollWidth, documentElement.scrollWidth, body.offsetWidth, documentElement.offsetWidth, documentElement.clientWidth) - 2 * distance :
  23409. chart.chartWidth,
  23410. height: outside ?
  23411. Math.max(body.scrollHeight, documentElement.scrollHeight, body.offsetHeight, documentElement.offsetHeight, documentElement.clientHeight) :
  23412. chart.chartHeight
  23413. };
  23414. }
  23415. /**
  23416. * Place the tooltip in a chart without spilling over and not covering the
  23417. * point itself.
  23418. *
  23419. * @function Highcharts.Tooltip#getPosition
  23420. *
  23421. * @param {number} boxWidth
  23422. * Width of the tooltip box.
  23423. *
  23424. * @param {number} boxHeight
  23425. * Height of the tooltip box.
  23426. *
  23427. * @param {Highcharts.Point} point
  23428. * Tooltip related point.
  23429. *
  23430. * @return {Highcharts.PositionObject}
  23431. * Recommended position of the tooltip.
  23432. */
  23433. getPosition(boxWidth, boxHeight, point) {
  23434. const chart = this.chart, distance = this.distance, ret = {},
  23435. // Don't use h if chart isn't inverted (#7242) ???
  23436. h = (chart.inverted && point.h) || 0, // #4117 ???
  23437. outside = this.outside, playingField = this.getPlayingField(), outerWidth = playingField.width, outerHeight = playingField.height, chartPosition = chart.pointer.getChartPosition(), scaleX = (val) => ( // eslint-disable-line no-confusing-arrow
  23438. val * chartPosition.scaleX), scaleY = (val) => ( // eslint-disable-line no-confusing-arrow
  23439. val * chartPosition.scaleY),
  23440. // Build parameter arrays for firstDimension()/secondDimension()
  23441. buildDimensionArray = (dim) => {
  23442. const isX = dim === 'x';
  23443. return [
  23444. dim,
  23445. isX ? outerWidth : outerHeight,
  23446. isX ? boxWidth : boxHeight
  23447. ].concat(outside ? [
  23448. // If we are using tooltip.outside, we need to scale the
  23449. // position to match scaling of the container in case there
  23450. // is a transform/zoom on the container. #11329
  23451. isX ? scaleX(boxWidth) : scaleY(boxHeight),
  23452. isX ? chartPosition.left - distance +
  23453. scaleX(point.plotX + chart.plotLeft) :
  23454. chartPosition.top - distance +
  23455. scaleY(point.plotY + chart.plotTop),
  23456. 0,
  23457. isX ? outerWidth : outerHeight
  23458. ] : [
  23459. // Not outside, no scaling is needed
  23460. isX ? boxWidth : boxHeight,
  23461. isX ? point.plotX + chart.plotLeft :
  23462. point.plotY + chart.plotTop,
  23463. isX ? chart.plotLeft : chart.plotTop,
  23464. isX ? chart.plotLeft + chart.plotWidth :
  23465. chart.plotTop + chart.plotHeight
  23466. ]);
  23467. };
  23468. let first = buildDimensionArray('y'), second = buildDimensionArray('x'), swapped;
  23469. // Handle negative points or reversed axis (#13780)
  23470. let flipped = !!point.negative;
  23471. if (!chart.polar &&
  23472. chart.hoverSeries &&
  23473. chart.hoverSeries.yAxis &&
  23474. chart.hoverSeries.yAxis.reversed) {
  23475. flipped = !flipped;
  23476. }
  23477. // The far side is right or bottom
  23478. const preferFarSide = !this.followPointer &&
  23479. pick(point.ttBelow, !chart.inverted === flipped), // #4984
  23480. /*
  23481. * Handle the preferred dimension. When the preferred dimension is
  23482. * tooltip on top or bottom of the point, it will look for space
  23483. * there.
  23484. *
  23485. * @private
  23486. */
  23487. firstDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  23488. point, min, max) {
  23489. const scaledDist = outside ?
  23490. (dim === 'y' ? scaleY(distance) : scaleX(distance)) :
  23491. distance, scaleDiff = (innerSize - scaledInnerSize) / 2, roomLeft = scaledInnerSize < point - distance, roomRight = point + distance + scaledInnerSize < outerSize, alignedLeft = point - scaledDist - innerSize + scaleDiff, alignedRight = point + scaledDist - scaleDiff;
  23492. if (preferFarSide && roomRight) {
  23493. ret[dim] = alignedRight;
  23494. }
  23495. else if (!preferFarSide && roomLeft) {
  23496. ret[dim] = alignedLeft;
  23497. }
  23498. else if (roomLeft) {
  23499. ret[dim] = Math.min(max - scaledInnerSize, alignedLeft - h < 0 ? alignedLeft : alignedLeft - h);
  23500. }
  23501. else if (roomRight) {
  23502. ret[dim] = Math.max(min, alignedRight + h + innerSize > outerSize ?
  23503. alignedRight :
  23504. alignedRight + h);
  23505. }
  23506. else {
  23507. return false;
  23508. }
  23509. },
  23510. /*
  23511. * Handle the secondary dimension. If the preferred dimension is
  23512. * tooltip on top or bottom of the point, the second dimension is to
  23513. * align the tooltip above the point, trying to align center but
  23514. * allowing left or right align within the chart box.
  23515. *
  23516. * @private
  23517. */
  23518. secondDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  23519. point) {
  23520. let retVal;
  23521. // Too close to the edge, return false and swap dimensions
  23522. if (point < distance || point > outerSize - distance) {
  23523. retVal = false;
  23524. // Align left/top
  23525. }
  23526. else if (point < innerSize / 2) {
  23527. ret[dim] = 1;
  23528. // Align right/bottom
  23529. }
  23530. else if (point > outerSize - scaledInnerSize / 2) {
  23531. ret[dim] = outerSize - scaledInnerSize - 2;
  23532. // Align center
  23533. }
  23534. else {
  23535. ret[dim] = point - innerSize / 2;
  23536. }
  23537. return retVal;
  23538. },
  23539. /*
  23540. * Swap the dimensions
  23541. */
  23542. swap = function (count) {
  23543. const temp = first;
  23544. first = second;
  23545. second = temp;
  23546. swapped = count;
  23547. }, run = function () {
  23548. if (firstDimension.apply(0, first) !== false) {
  23549. if (secondDimension.apply(0, second) === false &&
  23550. !swapped) {
  23551. swap(true);
  23552. run();
  23553. }
  23554. }
  23555. else if (!swapped) {
  23556. swap(true);
  23557. run();
  23558. }
  23559. else {
  23560. ret.x = ret.y = 0;
  23561. }
  23562. };
  23563. // Under these conditions, prefer the tooltip on the side of the point
  23564. if (chart.inverted || this.len > 1) {
  23565. swap();
  23566. }
  23567. run();
  23568. return ret;
  23569. }
  23570. /**
  23571. * Hides the tooltip with a fade out animation.
  23572. *
  23573. * @function Highcharts.Tooltip#hide
  23574. *
  23575. * @param {number} [delay]
  23576. * The fade out in milliseconds. If no value is provided the value
  23577. * of the tooltip.hideDelay option is used. A value of 0 disables
  23578. * the fade out animation.
  23579. */
  23580. hide(delay) {
  23581. const tooltip = this;
  23582. // disallow duplicate timers (#1728, #1766)
  23583. U.clearTimeout(this.hideTimer);
  23584. delay = pick(delay, this.options.hideDelay);
  23585. if (!this.isHidden) {
  23586. this.hideTimer = syncTimeout(function () {
  23587. // If there is a delay, do fadeOut with the default duration. If
  23588. // the hideDelay is 0, we assume no animation is wanted, so we
  23589. // pass 0 duration. #12994.
  23590. tooltip.getLabel().fadeOut(delay ? void 0 : delay);
  23591. tooltip.isHidden = true;
  23592. }, delay);
  23593. }
  23594. }
  23595. /**
  23596. * Initialize tooltip.
  23597. *
  23598. * @private
  23599. * @function Highcharts.Tooltip#init
  23600. *
  23601. * @param {Highcharts.Chart} chart
  23602. * The chart instance.
  23603. *
  23604. * @param {Highcharts.TooltipOptions} options
  23605. * Tooltip options.
  23606. */
  23607. init(chart, options) {
  23608. /**
  23609. * Chart of the tooltip.
  23610. *
  23611. * @readonly
  23612. * @name Highcharts.Tooltip#chart
  23613. * @type {Highcharts.Chart}
  23614. */
  23615. this.chart = chart;
  23616. /**
  23617. * Used tooltip options.
  23618. *
  23619. * @readonly
  23620. * @name Highcharts.Tooltip#options
  23621. * @type {Highcharts.TooltipOptions}
  23622. */
  23623. this.options = options;
  23624. /**
  23625. * List of crosshairs.
  23626. *
  23627. * @private
  23628. * @readonly
  23629. * @name Highcharts.Tooltip#crosshairs
  23630. * @type {Array<null>}
  23631. */
  23632. this.crosshairs = [];
  23633. /**
  23634. * Current values of x and y when animating.
  23635. *
  23636. * @private
  23637. * @readonly
  23638. * @name Highcharts.Tooltip#now
  23639. * @type {Highcharts.PositionObject}
  23640. */
  23641. this.now = { x: 0, y: 0 };
  23642. /**
  23643. * Tooltips are initially hidden.
  23644. *
  23645. * @private
  23646. * @readonly
  23647. * @name Highcharts.Tooltip#isHidden
  23648. * @type {boolean}
  23649. */
  23650. this.isHidden = true;
  23651. /**
  23652. * True, if the tooltip is split into one label per series, with the
  23653. * header close to the axis.
  23654. *
  23655. * @readonly
  23656. * @name Highcharts.Tooltip#split
  23657. * @type {boolean|undefined}
  23658. */
  23659. this.split = options.split && !chart.inverted && !chart.polar;
  23660. /**
  23661. * When the tooltip is shared, the entire plot area will capture mouse
  23662. * movement or touch events.
  23663. *
  23664. * @readonly
  23665. * @name Highcharts.Tooltip#shared
  23666. * @type {boolean|undefined}
  23667. */
  23668. this.shared = options.shared || this.split;
  23669. /**
  23670. * Whether to allow the tooltip to render outside the chart's SVG
  23671. * element box. By default (false), the tooltip is rendered within the
  23672. * chart's SVG element, which results in the tooltip being aligned
  23673. * inside the chart area.
  23674. *
  23675. * @readonly
  23676. * @name Highcharts.Tooltip#outside
  23677. * @type {boolean}
  23678. *
  23679. * @todo
  23680. * Split tooltip does not support outside in the first iteration. Should
  23681. * not be too complicated to implement.
  23682. */
  23683. this.outside = pick(options.outside, Boolean(chart.scrollablePixelsX || chart.scrollablePixelsY));
  23684. }
  23685. shouldStickOnContact(pointerEvent) {
  23686. return !!(!this.followPointer &&
  23687. this.options.stickOnContact &&
  23688. (!pointerEvent || this.chart.pointer.inClass(pointerEvent.target, 'highcharts-tooltip')));
  23689. }
  23690. /**
  23691. * Moves the tooltip with a soft animation to a new position.
  23692. *
  23693. * @private
  23694. * @function Highcharts.Tooltip#move
  23695. *
  23696. * @param {number} x
  23697. *
  23698. * @param {number} y
  23699. *
  23700. * @param {number} anchorX
  23701. *
  23702. * @param {number} anchorY
  23703. */
  23704. move(x, y, anchorX, anchorY) {
  23705. const tooltip = this, now = tooltip.now, animate = tooltip.options.animation !== false &&
  23706. !tooltip.isHidden &&
  23707. // When we get close to the target position, abort animation and
  23708. // land on the right place (#3056)
  23709. (Math.abs(x - now.x) > 1 || Math.abs(y - now.y) > 1), skipAnchor = tooltip.followPointer || tooltip.len > 1;
  23710. // Get intermediate values for animation
  23711. extend(now, {
  23712. x: animate ? (2 * now.x + x) / 3 : x,
  23713. y: animate ? (now.y + y) / 2 : y,
  23714. anchorX: skipAnchor ?
  23715. void 0 :
  23716. animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
  23717. anchorY: skipAnchor ?
  23718. void 0 :
  23719. animate ? (now.anchorY + anchorY) / 2 : anchorY
  23720. });
  23721. // Move to the intermediate value
  23722. tooltip.getLabel().attr(now);
  23723. tooltip.drawTracker();
  23724. // Run on next tick of the mouse tracker
  23725. if (animate) {
  23726. // Never allow two timeouts
  23727. U.clearTimeout(this.tooltipTimeout);
  23728. // Set the fixed interval ticking for the smooth tooltip
  23729. this.tooltipTimeout = setTimeout(function () {
  23730. // The interval function may still be running during destroy,
  23731. // so check that the chart is really there before calling.
  23732. if (tooltip) {
  23733. tooltip.move(x, y, anchorX, anchorY);
  23734. }
  23735. }, 32);
  23736. }
  23737. }
  23738. /**
  23739. * Refresh the tooltip's text and position.
  23740. *
  23741. * @function Highcharts.Tooltip#refresh
  23742. *
  23743. * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
  23744. * Either a point or an array of points.
  23745. *
  23746. * @param {Highcharts.PointerEventObject} [mouseEvent]
  23747. * Mouse event, that is responsible for the refresh and should be
  23748. * used for the tooltip update.
  23749. */
  23750. refresh(pointOrPoints, mouseEvent) {
  23751. const tooltip = this, chart = this.chart, options = tooltip.options, pointer = chart.pointer, points = splat(pointOrPoints), point = points[0], pointConfig = [], formatString = options.format, formatter = options.formatter || tooltip.defaultFormatter, shared = tooltip.shared, styledMode = chart.styledMode;
  23752. let formatterContext = {};
  23753. if (!options.enabled || !point.series) { // #16820
  23754. return;
  23755. }
  23756. U.clearTimeout(this.hideTimer);
  23757. // A switch saying if this specific tooltip configuration allows shared
  23758. // or split modes
  23759. tooltip.allowShared = !(!isArray(pointOrPoints) &&
  23760. pointOrPoints.series &&
  23761. pointOrPoints.series.noSharedTooltip);
  23762. // get the reference point coordinates (pie charts use tooltipPos)
  23763. tooltip.followPointer = (!tooltip.split && point.series.tooltipOptions.followPointer);
  23764. const anchor = tooltip.getAnchor(pointOrPoints, mouseEvent), x = anchor[0], y = anchor[1];
  23765. // shared tooltip, array is sent over
  23766. if (shared && tooltip.allowShared) {
  23767. pointer.applyInactiveState(points);
  23768. // Now set hover state for the choosen ones:
  23769. points.forEach(function (item) {
  23770. item.setState('hover');
  23771. pointConfig.push(item.getLabelConfig());
  23772. });
  23773. formatterContext = point.getLabelConfig();
  23774. formatterContext.points = pointConfig;
  23775. // single point tooltip
  23776. }
  23777. else {
  23778. formatterContext = point.getLabelConfig();
  23779. }
  23780. this.len = pointConfig.length; // #6128
  23781. const text = isString(formatString) ?
  23782. format(formatString, formatterContext, chart) :
  23783. formatter.call(formatterContext, tooltip);
  23784. // register the current series
  23785. const currentSeries = point.series;
  23786. this.distance = pick(currentSeries.tooltipOptions.distance, 16);
  23787. // update the inner HTML
  23788. if (text === false) {
  23789. this.hide();
  23790. }
  23791. else {
  23792. // update text
  23793. if (tooltip.split && tooltip.allowShared) { // #13868
  23794. this.renderSplit(text, points);
  23795. }
  23796. else {
  23797. let checkX = x;
  23798. let checkY = y;
  23799. if (mouseEvent && pointer.isDirectTouch) {
  23800. checkX = mouseEvent.chartX - chart.plotLeft;
  23801. checkY = mouseEvent.chartY - chart.plotTop;
  23802. }
  23803. // #11493, #13095
  23804. if (chart.polar ||
  23805. currentSeries.options.clip === false ||
  23806. points.some((p) => // #16004
  23807. pointer.isDirectTouch || // ##17929
  23808. p.series.shouldShowTooltip(checkX, checkY))) {
  23809. const label = tooltip.getLabel();
  23810. // Prevent the tooltip from flowing over the chart box
  23811. // (#6659)
  23812. if (!options.style.width || styledMode) {
  23813. label.css({
  23814. width: (this.outside ?
  23815. this.getPlayingField() :
  23816. chart.spacingBox).width + 'px'
  23817. });
  23818. }
  23819. label.attr({
  23820. text: text && text.join ?
  23821. text.join('') :
  23822. text
  23823. });
  23824. // Set the stroke color of the box to reflect the point
  23825. label.addClass(tooltip.getClassName(point), true);
  23826. if (!styledMode) {
  23827. label.attr({
  23828. stroke: (options.borderColor ||
  23829. point.color ||
  23830. currentSeries.color ||
  23831. "#666666" /* Palette.neutralColor60 */)
  23832. });
  23833. }
  23834. tooltip.updatePosition({
  23835. plotX: x,
  23836. plotY: y,
  23837. negative: point.negative,
  23838. ttBelow: point.ttBelow,
  23839. h: anchor[2] || 0
  23840. });
  23841. }
  23842. else {
  23843. tooltip.hide();
  23844. return;
  23845. }
  23846. }
  23847. // show it
  23848. if (tooltip.isHidden && tooltip.label) {
  23849. tooltip.label.attr({
  23850. opacity: 1
  23851. }).show();
  23852. }
  23853. tooltip.isHidden = false;
  23854. }
  23855. fireEvent(this, 'refresh');
  23856. }
  23857. /**
  23858. * Render the split tooltip. Loops over each point's text and adds
  23859. * a label next to the point, then uses the distribute function to
  23860. * find best non-overlapping positions.
  23861. *
  23862. * @private
  23863. * @function Highcharts.Tooltip#renderSplit
  23864. *
  23865. * @param {string|Array<(boolean|string)>} labels
  23866. *
  23867. * @param {Array<Highcharts.Point>} points
  23868. */
  23869. renderSplit(labels, points) {
  23870. const tooltip = this;
  23871. const { chart, chart: { chartWidth, chartHeight, plotHeight, plotLeft, plotTop, pointer, scrollablePixelsY = 0, scrollablePixelsX, scrollingContainer: { scrollLeft, scrollTop } = { scrollLeft: 0, scrollTop: 0 }, styledMode }, distance, options, options: { positioner } } = tooltip;
  23872. // The area which the tooltip should be limited to. Limit to scrollable
  23873. // plot area if enabled, otherwise limit to the chart container. If
  23874. // outside is true it should be the whole viewport
  23875. const bounds = (tooltip.outside &&
  23876. typeof scrollablePixelsX !== 'number') ?
  23877. doc.documentElement.getBoundingClientRect() : {
  23878. left: scrollLeft,
  23879. right: scrollLeft + chartWidth,
  23880. top: scrollTop,
  23881. bottom: scrollTop + chartHeight
  23882. };
  23883. const tooltipLabel = tooltip.getLabel();
  23884. const ren = this.renderer || chart.renderer;
  23885. const headerTop = Boolean(chart.xAxis[0] && chart.xAxis[0].opposite);
  23886. const { left: chartLeft, top: chartTop } = pointer.getChartPosition();
  23887. let distributionBoxTop = plotTop + scrollTop;
  23888. let headerHeight = 0;
  23889. let adjustedPlotHeight = plotHeight - scrollablePixelsY;
  23890. /**
  23891. * Calculates the anchor position for the partial tooltip
  23892. *
  23893. * @private
  23894. * @param {Highcharts.Point} point The point related to the tooltip
  23895. * @return {Object} Returns an object with anchorX and anchorY
  23896. */
  23897. function getAnchor(point) {
  23898. const { isHeader, plotX = 0, plotY = 0, series } = point;
  23899. let anchorX;
  23900. let anchorY;
  23901. if (isHeader) {
  23902. // Set anchorX to plotX
  23903. anchorX = plotLeft + plotX;
  23904. // Set anchorY to center of visible plot area.
  23905. anchorY = plotTop + plotHeight / 2;
  23906. }
  23907. else {
  23908. const { xAxis, yAxis } = series;
  23909. // Set anchorX to plotX. Limit to within xAxis.
  23910. anchorX = xAxis.pos + clamp(plotX, -distance, xAxis.len + distance);
  23911. // Set anchorY, limit to the scrollable plot area
  23912. if (series.shouldShowTooltip(0, yAxis.pos - plotTop + plotY, {
  23913. ignoreX: true
  23914. })) {
  23915. anchorY = yAxis.pos + plotY;
  23916. }
  23917. }
  23918. // Limit values to plot area
  23919. anchorX = clamp(anchorX, bounds.left - distance, bounds.right + distance);
  23920. return { anchorX, anchorY };
  23921. }
  23922. /**
  23923. * Calculates the position of the partial tooltip
  23924. *
  23925. * @private
  23926. * @param {number} anchorX
  23927. * The partial tooltip anchor x position
  23928. *
  23929. * @param {number} anchorY
  23930. * The partial tooltip anchor y position
  23931. *
  23932. * @param {boolean|undefined} isHeader
  23933. * Whether the partial tooltip is a header
  23934. *
  23935. * @param {number} boxWidth
  23936. * Width of the partial tooltip
  23937. *
  23938. * @return {Highcharts.PositionObject}
  23939. * Returns the partial tooltip x and y position
  23940. */
  23941. function defaultPositioner(anchorX, anchorY, isHeader, boxWidth, alignedLeft = true) {
  23942. let y;
  23943. let x;
  23944. if (isHeader) {
  23945. y = headerTop ? 0 : adjustedPlotHeight;
  23946. x = clamp(anchorX - (boxWidth / 2), bounds.left, bounds.right - boxWidth - (tooltip.outside ? chartLeft : 0));
  23947. }
  23948. else {
  23949. y = anchorY - distributionBoxTop;
  23950. x = alignedLeft ?
  23951. anchorX - boxWidth - distance :
  23952. anchorX + distance;
  23953. x = clamp(x, alignedLeft ? x : bounds.left, bounds.right);
  23954. }
  23955. // NOTE: y is relative to distributionBoxTop
  23956. return { x, y };
  23957. }
  23958. /**
  23959. * Updates the attributes and styling of the partial tooltip. Creates a
  23960. * new partial tooltip if it does not exists.
  23961. *
  23962. * @private
  23963. * @param {Highcharts.SVGElement|undefined} partialTooltip
  23964. * The partial tooltip to update
  23965. * @param {Highcharts.Point} point
  23966. * The point related to the partial tooltip
  23967. * @param {boolean|string} str The text for the partial tooltip
  23968. * @return {Highcharts.SVGElement} Returns the updated partial tooltip
  23969. */
  23970. function updatePartialTooltip(partialTooltip, point, str) {
  23971. var _a;
  23972. let tt = partialTooltip;
  23973. const { isHeader, series } = point;
  23974. if (!tt) {
  23975. const attribs = {
  23976. padding: options.padding,
  23977. r: options.borderRadius
  23978. };
  23979. if (!styledMode) {
  23980. attribs.fill = options.backgroundColor;
  23981. attribs['stroke-width'] = (_a = options.borderWidth) !== null && _a !== void 0 ? _a : 1;
  23982. }
  23983. tt = ren
  23984. .label('', 0, 0, (options[isHeader ? 'headerShape' : 'shape']), void 0, void 0, options.useHTML)
  23985. .addClass(tooltip.getClassName(point, true, isHeader))
  23986. .attr(attribs)
  23987. .add(tooltipLabel);
  23988. }
  23989. tt.isActive = true;
  23990. tt.attr({
  23991. text: str
  23992. });
  23993. if (!styledMode) {
  23994. tt.css(options.style)
  23995. .attr({
  23996. stroke: (options.borderColor ||
  23997. point.color ||
  23998. series.color ||
  23999. "#333333" /* Palette.neutralColor80 */)
  24000. });
  24001. }
  24002. return tt;
  24003. }
  24004. // Graceful degradation for legacy formatters
  24005. if (isString(labels)) {
  24006. labels = [false, labels];
  24007. }
  24008. // Create the individual labels for header and points, ignore footer
  24009. let boxes = labels.slice(0, points.length + 1).reduce(function (boxes, str, i) {
  24010. if (str !== false && str !== '') {
  24011. const point = (points[i - 1] ||
  24012. {
  24013. // Item 0 is the header. Instead of this, we could also
  24014. // use the crosshair label
  24015. isHeader: true,
  24016. plotX: points[0].plotX,
  24017. plotY: plotHeight,
  24018. series: {}
  24019. });
  24020. const isHeader = point.isHeader;
  24021. // Store the tooltip label referance on the series
  24022. const owner = isHeader ? tooltip : point.series;
  24023. const tt = owner.tt = updatePartialTooltip(owner.tt, point, str.toString());
  24024. // Get X position now, so we can move all to the other side in
  24025. // case of overflow
  24026. const bBox = tt.getBBox();
  24027. const boxWidth = bBox.width + tt.strokeWidth();
  24028. if (isHeader) {
  24029. headerHeight = bBox.height;
  24030. adjustedPlotHeight += headerHeight;
  24031. if (headerTop) {
  24032. distributionBoxTop -= headerHeight;
  24033. }
  24034. }
  24035. const { anchorX, anchorY } = getAnchor(point);
  24036. if (typeof anchorY === 'number') {
  24037. const size = bBox.height + 1;
  24038. const boxPosition = (positioner ?
  24039. positioner.call(tooltip, boxWidth, size, point) :
  24040. defaultPositioner(anchorX, anchorY, isHeader, boxWidth));
  24041. boxes.push({
  24042. // 0-align to the top, 1-align to the bottom
  24043. align: positioner ? 0 : void 0,
  24044. anchorX,
  24045. anchorY,
  24046. boxWidth,
  24047. point,
  24048. rank: pick(boxPosition.rank, isHeader ? 1 : 0),
  24049. size,
  24050. target: boxPosition.y,
  24051. tt,
  24052. x: boxPosition.x
  24053. });
  24054. }
  24055. else {
  24056. // Hide tooltips which anchorY is outside the visible plot
  24057. // area
  24058. tt.isActive = false;
  24059. }
  24060. }
  24061. return boxes;
  24062. }, []);
  24063. // Realign the tooltips towards the right if there is not enough space
  24064. // to the left and there is space to to the right
  24065. if (!positioner && boxes.some((box) => {
  24066. // Always realign if the beginning of a label is outside bounds
  24067. const { outside } = tooltip;
  24068. const boxStart = (outside ? chartLeft : 0) + box.anchorX;
  24069. if (boxStart < bounds.left &&
  24070. boxStart + box.boxWidth < bounds.right) {
  24071. return true;
  24072. }
  24073. // Otherwise, check if there is more space available to the right
  24074. return boxStart < (chartLeft - bounds.left) + box.boxWidth &&
  24075. bounds.right - boxStart > boxStart;
  24076. })) {
  24077. boxes = boxes.map((box) => {
  24078. const { x, y } = defaultPositioner(box.anchorX, box.anchorY, box.point.isHeader, box.boxWidth, false);
  24079. return extend(box, {
  24080. target: y,
  24081. x
  24082. });
  24083. });
  24084. }
  24085. // Clean previous run (for missing points)
  24086. tooltip.cleanSplit();
  24087. // Distribute and put in place
  24088. distribute(boxes, adjustedPlotHeight);
  24089. const boxExtremes = {
  24090. left: chartLeft,
  24091. right: chartLeft
  24092. };
  24093. // Get the extremes from series tooltips
  24094. boxes.forEach(function (box) {
  24095. const { x, boxWidth, isHeader } = box;
  24096. if (!isHeader) {
  24097. if (tooltip.outside && chartLeft + x < boxExtremes.left) {
  24098. boxExtremes.left = chartLeft + x;
  24099. }
  24100. if (!isHeader &&
  24101. tooltip.outside &&
  24102. boxExtremes.left + boxWidth > boxExtremes.right) {
  24103. boxExtremes.right = chartLeft + x;
  24104. }
  24105. }
  24106. });
  24107. boxes.forEach(function (box) {
  24108. const { x, anchorX, anchorY, pos, point: { isHeader } } = box;
  24109. const attributes = {
  24110. visibility: typeof pos === 'undefined' ? 'hidden' : 'inherit',
  24111. x,
  24112. /* NOTE: y should equal pos to be consistent with !split
  24113. * tooltip, but is currently relative to plotTop. Is left as is
  24114. * to avoid breaking change. Remove distributionBoxTop to make
  24115. * it consistent.
  24116. */
  24117. y: (pos || 0) + distributionBoxTop,
  24118. anchorX,
  24119. anchorY
  24120. };
  24121. // Handle left-aligned tooltips overflowing the chart area
  24122. if (tooltip.outside && x < anchorX) {
  24123. const offset = chartLeft - boxExtremes.left;
  24124. // Skip this if there is no overflow
  24125. if (offset > 0) {
  24126. if (!isHeader) {
  24127. attributes.x = x + offset;
  24128. attributes.anchorX = anchorX + offset;
  24129. }
  24130. if (isHeader) {
  24131. attributes.x = (boxExtremes.right - boxExtremes.left) / 2;
  24132. attributes.anchorX = anchorX + offset;
  24133. }
  24134. }
  24135. }
  24136. // Put the label in place
  24137. box.tt.attr(attributes);
  24138. });
  24139. /* If we have a seperate tooltip container, then update the necessary
  24140. * container properties.
  24141. * Test that tooltip has its own container and renderer before executing
  24142. * the operation.
  24143. */
  24144. const { container, outside, renderer } = tooltip;
  24145. if (outside && container && renderer) {
  24146. // Set container size to fit the bounds
  24147. const { width, height, x, y } = tooltipLabel.getBBox();
  24148. renderer.setSize(width + x, height + y, false);
  24149. // Position the tooltip container to the chart container
  24150. container.style.left = boxExtremes.left + 'px';
  24151. container.style.top = chartTop + 'px';
  24152. }
  24153. // Workaround for #18927, artefacts left by the shadows of split
  24154. // tooltips in Safari v16 (2023). Check again with later versions if we
  24155. // can remove this.
  24156. if (isSafari) {
  24157. tooltipLabel.attr({
  24158. // Force a redraw of the whole group by chaning the opacity
  24159. // slightly
  24160. opacity: tooltipLabel.opacity === 1 ? 0.999 : 1
  24161. });
  24162. }
  24163. }
  24164. /**
  24165. * If the `stickOnContact` option is active, this will add a tracker shape.
  24166. *
  24167. * @private
  24168. * @function Highcharts.Tooltip#drawTracker
  24169. */
  24170. drawTracker() {
  24171. const tooltip = this;
  24172. if (!this.shouldStickOnContact()) {
  24173. if (tooltip.tracker) {
  24174. tooltip.tracker = tooltip.tracker.destroy();
  24175. }
  24176. return;
  24177. }
  24178. const chart = tooltip.chart;
  24179. const label = tooltip.label;
  24180. const points = tooltip.shared ? chart.hoverPoints : chart.hoverPoint;
  24181. if (!label || !points) {
  24182. return;
  24183. }
  24184. const box = {
  24185. x: 0,
  24186. y: 0,
  24187. width: 0,
  24188. height: 0
  24189. };
  24190. // Combine anchor and tooltip
  24191. const anchorPos = this.getAnchor(points);
  24192. const labelBBox = label.getBBox();
  24193. anchorPos[0] += chart.plotLeft - label.translateX;
  24194. anchorPos[1] += chart.plotTop - label.translateY;
  24195. // When the mouse pointer is between the anchor point and the label,
  24196. // the label should stick.
  24197. box.x = Math.min(0, anchorPos[0]);
  24198. box.y = Math.min(0, anchorPos[1]);
  24199. box.width = (anchorPos[0] < 0 ?
  24200. Math.max(Math.abs(anchorPos[0]), (labelBBox.width - anchorPos[0])) :
  24201. Math.max(Math.abs(anchorPos[0]), labelBBox.width));
  24202. box.height = (anchorPos[1] < 0 ?
  24203. Math.max(Math.abs(anchorPos[1]), (labelBBox.height - Math.abs(anchorPos[1]))) :
  24204. Math.max(Math.abs(anchorPos[1]), labelBBox.height));
  24205. if (tooltip.tracker) {
  24206. tooltip.tracker.attr(box);
  24207. }
  24208. else {
  24209. tooltip.tracker = label.renderer
  24210. .rect(box)
  24211. .addClass('highcharts-tracker')
  24212. .add(label);
  24213. if (!chart.styledMode) {
  24214. tooltip.tracker.attr({
  24215. fill: 'rgba(0,0,0,0)'
  24216. });
  24217. }
  24218. }
  24219. }
  24220. /**
  24221. * @private
  24222. */
  24223. styledModeFormat(formatString) {
  24224. return formatString
  24225. .replace('style="font-size: 0.8em"', 'class="highcharts-header"')
  24226. .replace(/style="color:{(point|series)\.color}"/g, 'class="highcharts-color-{$1.colorIndex} ' +
  24227. '{series.options.className} ' +
  24228. '{point.options.className}"');
  24229. }
  24230. /**
  24231. * Format the footer/header of the tooltip
  24232. * #3397: abstraction to enable formatting of footer and header
  24233. *
  24234. * @private
  24235. * @function Highcharts.Tooltip#tooltipFooterHeaderFormatter
  24236. */
  24237. tooltipFooterHeaderFormatter(labelConfig, isFooter) {
  24238. const series = labelConfig.series, tooltipOptions = series.tooltipOptions, xAxis = series.xAxis, dateTime = xAxis && xAxis.dateTime, e = {
  24239. isFooter: isFooter,
  24240. labelConfig: labelConfig
  24241. };
  24242. let xDateFormat = tooltipOptions.xDateFormat, formatString = tooltipOptions[isFooter ? 'footerFormat' : 'headerFormat'];
  24243. fireEvent(this, 'headerFormatter', e, function (e) {
  24244. // Guess the best date format based on the closest point distance
  24245. // (#568, #3418)
  24246. if (dateTime && !xDateFormat && isNumber(labelConfig.key)) {
  24247. xDateFormat = dateTime.getXDateFormat(labelConfig.key, tooltipOptions.dateTimeLabelFormats);
  24248. }
  24249. // Insert the footer date format if any
  24250. if (dateTime && xDateFormat) {
  24251. ((labelConfig.point && labelConfig.point.tooltipDateKeys) ||
  24252. ['key']).forEach(function (key) {
  24253. formatString = formatString.replace('{point.' + key + '}', '{point.' + key + ':' + xDateFormat + '}');
  24254. });
  24255. }
  24256. // Replace default header style with class name
  24257. if (series.chart.styledMode) {
  24258. formatString = this.styledModeFormat(formatString);
  24259. }
  24260. e.text = format(formatString, {
  24261. point: labelConfig,
  24262. series: series
  24263. }, this.chart);
  24264. });
  24265. return e.text;
  24266. }
  24267. /**
  24268. * Updates the tooltip with the provided tooltip options.
  24269. *
  24270. * @function Highcharts.Tooltip#update
  24271. *
  24272. * @param {Highcharts.TooltipOptions} options
  24273. * The tooltip options to update.
  24274. */
  24275. update(options) {
  24276. this.destroy();
  24277. this.init(this.chart, merge(true, this.options, options));
  24278. }
  24279. /**
  24280. * Find the new position and perform the move
  24281. *
  24282. * @private
  24283. * @function Highcharts.Tooltip#updatePosition
  24284. *
  24285. * @param {Highcharts.Point} point
  24286. */
  24287. updatePosition(point) {
  24288. const { chart, distance, options } = this, pointer = chart.pointer, label = this.getLabel(),
  24289. // Needed for outside: true (#11688)
  24290. { left, top, scaleX, scaleY } = pointer.getChartPosition(), pos = (options.positioner || this.getPosition).call(this, label.width, label.height, point);
  24291. let anchorX = (point.plotX || 0) + chart.plotLeft, anchorY = (point.plotY || 0) + chart.plotTop, pad;
  24292. // Set the renderer size dynamically to prevent document size to change
  24293. if (this.outside) {
  24294. // Corrects positions, occurs with tooltip positioner (#16944)
  24295. if (options.positioner) {
  24296. pos.x += left - distance;
  24297. pos.y += top - distance;
  24298. }
  24299. pad = (options.borderWidth || 0) + 2 * distance;
  24300. this.renderer.setSize(label.width + pad, label.height + pad, false);
  24301. // Anchor and tooltip container need scaling if chart container has
  24302. // scale transform/css zoom. #11329.
  24303. if (scaleX !== 1 || scaleY !== 1) {
  24304. css(this.container, {
  24305. transform: `scale(${scaleX}, ${scaleY})`
  24306. });
  24307. anchorX *= scaleX;
  24308. anchorY *= scaleY;
  24309. }
  24310. anchorX += left - pos.x;
  24311. anchorY += top - pos.y;
  24312. }
  24313. // do the move
  24314. this.move(Math.round(pos.x), Math.round(pos.y || 0), // can be undefined (#3977)
  24315. anchorX, anchorY);
  24316. }
  24317. }
  24318. /* *
  24319. *
  24320. * Class namespace
  24321. *
  24322. * */
  24323. (function (Tooltip) {
  24324. /* *
  24325. *
  24326. * Declarations
  24327. *
  24328. * */
  24329. /* *
  24330. *
  24331. * Constants
  24332. *
  24333. * */
  24334. const composedMembers = [];
  24335. /* *
  24336. *
  24337. * Functions
  24338. *
  24339. * */
  24340. /**
  24341. * @private
  24342. */
  24343. function compose(PointerClass) {
  24344. if (U.pushUnique(composedMembers, PointerClass)) {
  24345. addEvent(PointerClass, 'afterInit', function () {
  24346. const chart = this.chart;
  24347. if (chart.options.tooltip) {
  24348. /**
  24349. * Tooltip object for points of series.
  24350. *
  24351. * @name Highcharts.Chart#tooltip
  24352. * @type {Highcharts.Tooltip}
  24353. */
  24354. chart.tooltip = new Tooltip(chart, chart.options.tooltip);
  24355. }
  24356. });
  24357. }
  24358. }
  24359. Tooltip.compose = compose;
  24360. })(Tooltip || (Tooltip = {}));
  24361. /* *
  24362. *
  24363. * Default export
  24364. *
  24365. * */
  24366. /* *
  24367. *
  24368. * API Declarations
  24369. *
  24370. * */
  24371. /**
  24372. * Callback function to format the text of the tooltip from scratch.
  24373. *
  24374. * In case of single or shared tooltips, a string should be be returned. In case
  24375. * of splitted tooltips, it should return an array where the first item is the
  24376. * header, and subsequent items are mapped to the points. Return `false` to
  24377. * disable tooltip for a specific point on series.
  24378. *
  24379. * @callback Highcharts.TooltipFormatterCallbackFunction
  24380. *
  24381. * @param {Highcharts.TooltipFormatterContextObject} this
  24382. * Context to format
  24383. *
  24384. * @param {Highcharts.Tooltip} tooltip
  24385. * The tooltip instance
  24386. *
  24387. * @return {false|string|Array<(string|null|undefined)>|null|undefined}
  24388. * Formatted text or false
  24389. */
  24390. /**
  24391. * Configuration for the tooltip formatters.
  24392. *
  24393. * @interface Highcharts.TooltipFormatterContextObject
  24394. * @extends Highcharts.PointLabelObject
  24395. */ /**
  24396. * Array of points in shared tooltips.
  24397. * @name Highcharts.TooltipFormatterContextObject#points
  24398. * @type {Array<Highcharts.TooltipFormatterContextObject>|undefined}
  24399. */
  24400. /**
  24401. * A callback function to place the tooltip in a specific position.
  24402. *
  24403. * @callback Highcharts.TooltipPositionerCallbackFunction
  24404. *
  24405. * @param {Highcharts.Tooltip} this
  24406. * Tooltip context of the callback.
  24407. *
  24408. * @param {number} labelWidth
  24409. * Width of the tooltip.
  24410. *
  24411. * @param {number} labelHeight
  24412. * Height of the tooltip.
  24413. *
  24414. * @param {Highcharts.TooltipPositionerPointObject} point
  24415. * Point information for positioning a tooltip.
  24416. *
  24417. * @return {Highcharts.PositionObject}
  24418. * New position for the tooltip.
  24419. */
  24420. /**
  24421. * Point information for positioning a tooltip.
  24422. *
  24423. * @interface Highcharts.TooltipPositionerPointObject
  24424. * @extends Highcharts.Point
  24425. */ /**
  24426. * If `tooltip.split` option is enabled and positioner is called for each of the
  24427. * boxes separately, this property indicates the call on the xAxis header, which
  24428. * is not a point itself.
  24429. * @name Highcharts.TooltipPositionerPointObject#isHeader
  24430. * @type {boolean}
  24431. */ /**
  24432. * The reference point relative to the plot area. Add chart.plotLeft to get the
  24433. * full coordinates.
  24434. * @name Highcharts.TooltipPositionerPointObject#plotX
  24435. * @type {number}
  24436. */ /**
  24437. * The reference point relative to the plot area. Add chart.plotTop to get the
  24438. * full coordinates.
  24439. * @name Highcharts.TooltipPositionerPointObject#plotY
  24440. * @type {number}
  24441. */
  24442. /**
  24443. * @typedef {"callout"|"circle"|"square"} Highcharts.TooltipShapeValue
  24444. */
  24445. ''; // keeps doclets above in JS file
  24446. return Tooltip;
  24447. });
  24448. _registerModule(_modules, 'Core/Series/Point.js', [_modules['Core/Renderer/HTML/AST.js'], _modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Defaults.js'], _modules['Core/Templating.js'], _modules['Core/Utilities.js']], function (AST, A, D, F, U) {
  24449. /* *
  24450. *
  24451. * (c) 2010-2021 Torstein Honsi
  24452. *
  24453. * License: www.highcharts.com/license
  24454. *
  24455. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  24456. *
  24457. * */
  24458. const { animObject } = A;
  24459. const { defaultOptions } = D;
  24460. const { format } = F;
  24461. const { addEvent, defined, erase, extend, fireEvent, getNestedProperty, isArray, isFunction, isNumber, isObject, merge, objectEach, pick, syncTimeout, removeEvent, uniqueKey } = U;
  24462. /* eslint-disable no-invalid-this, valid-jsdoc */
  24463. /* *
  24464. *
  24465. * Class
  24466. *
  24467. * */
  24468. /**
  24469. * The Point object. The point objects are generated from the `series.data`
  24470. * configuration objects or raw numbers. They can be accessed from the
  24471. * `Series.points` array. Other ways to instantiate points are through {@link
  24472. * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
  24473. *
  24474. * @class
  24475. * @name Highcharts.Point
  24476. */
  24477. class Point {
  24478. constructor() {
  24479. /* *
  24480. *
  24481. * Properties
  24482. *
  24483. * */
  24484. /**
  24485. * For categorized axes this property holds the category name for the
  24486. * point. For other axes it holds the X value.
  24487. *
  24488. * @name Highcharts.Point#category
  24489. * @type {number|string}
  24490. */
  24491. this.category = void 0;
  24492. this.destroyed = false;
  24493. this.formatPrefix = 'point';
  24494. this.id = void 0;
  24495. this.isNull = false;
  24496. /**
  24497. * The name of the point. The name can be given as the first position of the
  24498. * point configuration array, or as a `name` property in the configuration:
  24499. *
  24500. * @example
  24501. * // Array config
  24502. * data: [
  24503. * ['John', 1],
  24504. * ['Jane', 2]
  24505. * ]
  24506. *
  24507. * // Object config
  24508. * data: [{
  24509. * name: 'John',
  24510. * y: 1
  24511. * }, {
  24512. * name: 'Jane',
  24513. * y: 2
  24514. * }]
  24515. *
  24516. * @name Highcharts.Point#name
  24517. * @type {string}
  24518. */
  24519. this.name = void 0;
  24520. /**
  24521. * The point's options as applied in the initial configuration, or
  24522. * extended through `Point.update`.
  24523. *
  24524. * In TypeScript you have to extend `PointOptionsObject` via an
  24525. * additional interface to allow custom data options:
  24526. *
  24527. * ```
  24528. * declare interface PointOptionsObject {
  24529. * customProperty: string;
  24530. * }
  24531. * ```
  24532. *
  24533. * @name Highcharts.Point#options
  24534. * @type {Highcharts.PointOptionsObject}
  24535. */
  24536. this.options = void 0;
  24537. /**
  24538. * The percentage for points in a stacked series, pies or gauges.
  24539. *
  24540. * @name Highcharts.Point#percentage
  24541. * @type {number|undefined}
  24542. */
  24543. this.percentage = void 0;
  24544. this.selected = false;
  24545. /**
  24546. * The series object associated with the point.
  24547. *
  24548. * @name Highcharts.Point#series
  24549. * @type {Highcharts.Series}
  24550. */
  24551. this.series = void 0;
  24552. /**
  24553. * The attributes of the rendered SVG shape like in `column` or `pie`
  24554. * series.
  24555. *
  24556. * @readonly
  24557. * @name Highcharts.Point#shapeArgs
  24558. * @type {Readonly<Highcharts.SVGAttributes>|undefined}
  24559. */
  24560. this.shapeArgs = void 0;
  24561. /**
  24562. * The total of values in either a stack for stacked series, or a pie in a
  24563. * pie series.
  24564. *
  24565. * @name Highcharts.Point#total
  24566. * @type {number|undefined}
  24567. */
  24568. this.total = void 0;
  24569. /**
  24570. * For certain series types, like pie charts, where individual points can
  24571. * be shown or hidden.
  24572. *
  24573. * @name Highcharts.Point#visible
  24574. * @type {boolean}
  24575. * @default true
  24576. */
  24577. this.visible = true;
  24578. this.x = void 0;
  24579. }
  24580. /* *
  24581. *
  24582. * Functions
  24583. *
  24584. * */
  24585. /**
  24586. * Animate SVG elements associated with the point.
  24587. *
  24588. * @private
  24589. * @function Highcharts.Point#animateBeforeDestroy
  24590. */
  24591. animateBeforeDestroy() {
  24592. const point = this, animateParams = { x: point.startXPos, opacity: 0 }, graphicalProps = point.getGraphicalProps();
  24593. graphicalProps.singular.forEach(function (prop) {
  24594. const isDataLabel = prop === 'dataLabel';
  24595. point[prop] = point[prop].animate(isDataLabel ? {
  24596. x: point[prop].startXPos,
  24597. y: point[prop].startYPos,
  24598. opacity: 0
  24599. } : animateParams);
  24600. });
  24601. graphicalProps.plural.forEach(function (plural) {
  24602. point[plural].forEach(function (item) {
  24603. if (item.element) {
  24604. item.animate(extend({ x: point.startXPos }, (item.startYPos ? {
  24605. x: item.startXPos,
  24606. y: item.startYPos
  24607. } : {})));
  24608. }
  24609. });
  24610. });
  24611. }
  24612. /**
  24613. * Apply the options containing the x and y data and possible some extra
  24614. * properties. Called on point init or from point.update.
  24615. *
  24616. * @private
  24617. * @function Highcharts.Point#applyOptions
  24618. *
  24619. * @param {Highcharts.PointOptionsType} options
  24620. * The point options as defined in series.data.
  24621. *
  24622. * @param {number} [x]
  24623. * Optionally, the x value.
  24624. *
  24625. * @return {Highcharts.Point}
  24626. * The Point instance.
  24627. */
  24628. applyOptions(options, x) {
  24629. const point = this, series = point.series, pointValKey = series.options.pointValKey || series.pointValKey;
  24630. options = Point.prototype.optionsToObject.call(this, options);
  24631. // copy options directly to point
  24632. extend(point, options);
  24633. point.options = point.options ?
  24634. extend(point.options, options) :
  24635. options;
  24636. // Since options are copied into the Point instance, some accidental
  24637. // options must be shielded (#5681)
  24638. if (options.group) {
  24639. delete point.group;
  24640. }
  24641. if (options.dataLabels) {
  24642. delete point.dataLabels;
  24643. }
  24644. /**
  24645. * The y value of the point.
  24646. * @name Highcharts.Point#y
  24647. * @type {number|undefined}
  24648. */
  24649. // For higher dimension series types. For instance, for ranges, point.y
  24650. // is mapped to point.low.
  24651. if (pointValKey) {
  24652. point.y = Point.prototype.getNestedProperty.call(point, pointValKey);
  24653. }
  24654. point.isNull = this.isValid && !this.isValid();
  24655. point.formatPrefix = point.isNull ? 'null' : 'point'; // #9233, #10874
  24656. // The point is initially selected by options (#5777)
  24657. if (point.selected) {
  24658. point.state = 'select';
  24659. }
  24660. /**
  24661. * The x value of the point.
  24662. * @name Highcharts.Point#x
  24663. * @type {number}
  24664. */
  24665. // If no x is set by now, get auto incremented value. All points must
  24666. // have an x value, however the y value can be null to create a gap in
  24667. // the series
  24668. if ('name' in point &&
  24669. typeof x === 'undefined' &&
  24670. series.xAxis &&
  24671. series.xAxis.hasNames) {
  24672. point.x = series.xAxis.nameToX(point);
  24673. }
  24674. if (typeof point.x === 'undefined' && series) {
  24675. if (typeof x === 'undefined') {
  24676. point.x = series.autoIncrement();
  24677. }
  24678. else {
  24679. point.x = x;
  24680. }
  24681. }
  24682. else if (isNumber(options.x) && series.options.relativeXValue) {
  24683. point.x = series.autoIncrement(options.x);
  24684. }
  24685. return point;
  24686. }
  24687. /**
  24688. * Destroy a point to clear memory. Its reference still stays in
  24689. * `series.data`.
  24690. *
  24691. * @private
  24692. * @function Highcharts.Point#destroy
  24693. */
  24694. destroy() {
  24695. if (!this.destroyed) {
  24696. const point = this, series = point.series, chart = series.chart, dataSorting = series.options.dataSorting, hoverPoints = chart.hoverPoints, globalAnimation = point.series.chart.renderer.globalAnimation, animation = animObject(globalAnimation);
  24697. /**
  24698. * Allow to call after animation.
  24699. * @private
  24700. */
  24701. const destroyPoint = () => {
  24702. // Remove all events and elements
  24703. if (point.graphic ||
  24704. point.graphics ||
  24705. point.dataLabel ||
  24706. point.dataLabels) {
  24707. removeEvent(point);
  24708. point.destroyElements();
  24709. }
  24710. for (const prop in point) { // eslint-disable-line guard-for-in
  24711. delete point[prop];
  24712. }
  24713. };
  24714. if (point.legendItem) {
  24715. // pies have legend items
  24716. chart.legend.destroyItem(point);
  24717. }
  24718. if (hoverPoints) {
  24719. point.setState();
  24720. erase(hoverPoints, point);
  24721. if (!hoverPoints.length) {
  24722. chart.hoverPoints = null;
  24723. }
  24724. }
  24725. if (point === chart.hoverPoint) {
  24726. point.onMouseOut();
  24727. }
  24728. // Remove properties after animation
  24729. if (!dataSorting || !dataSorting.enabled) {
  24730. destroyPoint();
  24731. }
  24732. else {
  24733. this.animateBeforeDestroy();
  24734. syncTimeout(destroyPoint, animation.duration);
  24735. }
  24736. chart.pointCount--;
  24737. }
  24738. this.destroyed = true;
  24739. }
  24740. /**
  24741. * Destroy SVG elements associated with the point.
  24742. *
  24743. * @private
  24744. * @function Highcharts.Point#destroyElements
  24745. * @param {Highcharts.Dictionary<number>} [kinds]
  24746. */
  24747. destroyElements(kinds) {
  24748. const point = this, props = point.getGraphicalProps(kinds);
  24749. props.singular.forEach(function (prop) {
  24750. point[prop] = point[prop].destroy();
  24751. });
  24752. props.plural.forEach(function (plural) {
  24753. point[plural].forEach(function (item) {
  24754. if (item && item.element) {
  24755. item.destroy();
  24756. }
  24757. });
  24758. delete point[plural];
  24759. });
  24760. }
  24761. /**
  24762. * Fire an event on the Point object.
  24763. *
  24764. * @private
  24765. * @function Highcharts.Point#firePointEvent
  24766. *
  24767. * @param {string} eventType
  24768. * Type of the event.
  24769. *
  24770. * @param {Highcharts.Dictionary<any>|Event} [eventArgs]
  24771. * Additional event arguments.
  24772. *
  24773. * @param {Highcharts.EventCallbackFunction<Highcharts.Point>|Function} [defaultFunction]
  24774. * Default event handler.
  24775. *
  24776. * @emits Highcharts.Point#event:*
  24777. */
  24778. firePointEvent(eventType, eventArgs, defaultFunction) {
  24779. const point = this, series = this.series, seriesOptions = series.options;
  24780. // load event handlers on demand to save time on mouseover/out
  24781. if (seriesOptions.point.events[eventType] ||
  24782. (point.options &&
  24783. point.options.events &&
  24784. point.options.events[eventType])) {
  24785. point.importEvents();
  24786. }
  24787. // add default handler if in selection mode
  24788. if (eventType === 'click' && seriesOptions.allowPointSelect) {
  24789. defaultFunction = function (event) {
  24790. // Control key is for Windows, meta (= Cmd key) for Mac, Shift
  24791. // for Opera.
  24792. if (point.select) { // #2911
  24793. point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
  24794. }
  24795. };
  24796. }
  24797. fireEvent(point, eventType, eventArgs, defaultFunction);
  24798. }
  24799. /**
  24800. * Get the CSS class names for individual points. Used internally where the
  24801. * returned value is set on every point.
  24802. *
  24803. * @function Highcharts.Point#getClassName
  24804. *
  24805. * @return {string}
  24806. * The class names.
  24807. */
  24808. getClassName() {
  24809. const point = this;
  24810. return 'highcharts-point' +
  24811. (point.selected ? ' highcharts-point-select' : '') +
  24812. (point.negative ? ' highcharts-negative' : '') +
  24813. (point.isNull ? ' highcharts-null-point' : '') +
  24814. (typeof point.colorIndex !== 'undefined' ?
  24815. ' highcharts-color-' + point.colorIndex : '') +
  24816. (point.options.className ? ' ' + point.options.className : '') +
  24817. (point.zone && point.zone.className ? ' ' +
  24818. point.zone.className.replace('highcharts-negative', '') : '');
  24819. }
  24820. /**
  24821. * Get props of all existing graphical point elements.
  24822. *
  24823. * @private
  24824. * @function Highcharts.Point#getGraphicalProps
  24825. */
  24826. getGraphicalProps(kinds) {
  24827. const point = this, props = [], graphicalProps = { singular: [], plural: [] };
  24828. let prop, i;
  24829. kinds = kinds || { graphic: 1, dataLabel: 1 };
  24830. if (kinds.graphic) {
  24831. props.push('graphic');
  24832. }
  24833. if (kinds.dataLabel) {
  24834. props.push('dataLabel', 'dataLabelPath', 'dataLabelUpper', 'connector');
  24835. }
  24836. i = props.length;
  24837. while (i--) {
  24838. prop = props[i];
  24839. if (point[prop]) {
  24840. graphicalProps.singular.push(prop);
  24841. }
  24842. }
  24843. [
  24844. 'graphic',
  24845. 'dataLabel',
  24846. 'connector'
  24847. ].forEach(function (prop) {
  24848. const plural = prop + 's';
  24849. if (kinds[prop] && point[plural]) {
  24850. graphicalProps.plural.push(plural);
  24851. }
  24852. });
  24853. return graphicalProps;
  24854. }
  24855. /**
  24856. * Return the configuration hash needed for the data label and tooltip
  24857. * formatters.
  24858. *
  24859. * @function Highcharts.Point#getLabelConfig
  24860. *
  24861. * @return {Highcharts.PointLabelObject}
  24862. * Abstract object used in formatters and formats.
  24863. */
  24864. getLabelConfig() {
  24865. return {
  24866. x: this.category,
  24867. y: this.y,
  24868. color: this.color,
  24869. colorIndex: this.colorIndex,
  24870. key: this.name || this.category,
  24871. series: this.series,
  24872. point: this,
  24873. percentage: this.percentage,
  24874. total: this.total || this.stackTotal
  24875. };
  24876. }
  24877. /**
  24878. * Returns the value of the point property for a given value.
  24879. * @private
  24880. */
  24881. getNestedProperty(key) {
  24882. if (!key) {
  24883. return;
  24884. }
  24885. if (key.indexOf('custom.') === 0) {
  24886. return getNestedProperty(key, this.options);
  24887. }
  24888. return this[key];
  24889. }
  24890. /**
  24891. * In a series with `zones`, return the zone that the point belongs to.
  24892. *
  24893. * @function Highcharts.Point#getZone
  24894. *
  24895. * @return {Highcharts.SeriesZonesOptionsObject}
  24896. * The zone item.
  24897. */
  24898. getZone() {
  24899. const series = this.series, zones = series.zones, zoneAxis = series.zoneAxis || 'y';
  24900. let zone, i = 0;
  24901. zone = zones[i];
  24902. while (this[zoneAxis] >= zone.value) {
  24903. zone = zones[++i];
  24904. }
  24905. // For resetting or reusing the point (#8100)
  24906. if (!this.nonZonedColor) {
  24907. this.nonZonedColor = this.color;
  24908. }
  24909. if (zone && zone.color && !this.options.color) {
  24910. this.color = zone.color;
  24911. }
  24912. else {
  24913. this.color = this.nonZonedColor;
  24914. }
  24915. return zone;
  24916. }
  24917. /**
  24918. * Utility to check if point has new shape type. Used in column series and
  24919. * all others that are based on column series.
  24920. * @private
  24921. */
  24922. hasNewShapeType() {
  24923. const point = this;
  24924. const oldShapeType = point.graphic &&
  24925. (point.graphic.symbolName || point.graphic.element.nodeName);
  24926. return oldShapeType !== this.shapeType;
  24927. }
  24928. /**
  24929. * Initialize the point. Called internally based on the `series.data`
  24930. * option.
  24931. *
  24932. * @function Highcharts.Point#init
  24933. *
  24934. * @param {Highcharts.Series} series
  24935. * The series object containing this point.
  24936. *
  24937. * @param {Highcharts.PointOptionsType} options
  24938. * The data in either number, array or object format.
  24939. *
  24940. * @param {number} [x]
  24941. * Optionally, the X value of the point.
  24942. *
  24943. * @return {Highcharts.Point}
  24944. * The Point instance.
  24945. *
  24946. * @emits Highcharts.Point#event:afterInit
  24947. */
  24948. init(series, options, x) {
  24949. this.series = series;
  24950. this.applyOptions(options, x);
  24951. // Add a unique ID to the point if none is assigned
  24952. this.id = defined(this.id) ? this.id : uniqueKey();
  24953. this.resolveColor();
  24954. series.chart.pointCount++;
  24955. fireEvent(this, 'afterInit');
  24956. return this;
  24957. }
  24958. /**
  24959. * Determine if point is valid.
  24960. * @private
  24961. * @function Highcharts.Point#isValid
  24962. */
  24963. isValid() {
  24964. return this.x !== null && isNumber(this.y);
  24965. }
  24966. /**
  24967. * Transform number or array configs into objects. Also called for object
  24968. * configs. Used internally to unify the different configuration formats for
  24969. * points. For example, a simple number `10` in a line series will be
  24970. * transformed to `{ y: 10 }`, and an array config like `[1, 10]` in a
  24971. * scatter series will be transformed to `{ x: 1, y: 10 }`.
  24972. *
  24973. * @deprecated
  24974. * @function Highcharts.Point#optionsToObject
  24975. *
  24976. * @param {Highcharts.PointOptionsType} options
  24977. * Series data options.
  24978. *
  24979. * @return {Highcharts.Dictionary<*>}
  24980. * Transformed point options.
  24981. */
  24982. optionsToObject(options) {
  24983. const series = this.series, keys = series.options.keys, pointArrayMap = keys || series.pointArrayMap || ['y'], valueCount = pointArrayMap.length;
  24984. let ret = {}, firstItemType, i = 0, j = 0;
  24985. if (isNumber(options) || options === null) {
  24986. ret[pointArrayMap[0]] = options;
  24987. }
  24988. else if (isArray(options)) {
  24989. // with leading x value
  24990. if (!keys && options.length > valueCount) {
  24991. firstItemType = typeof options[0];
  24992. if (firstItemType === 'string') {
  24993. ret.name = options[0];
  24994. }
  24995. else if (firstItemType === 'number') {
  24996. ret.x = options[0];
  24997. }
  24998. i++;
  24999. }
  25000. while (j < valueCount) {
  25001. // Skip undefined positions for keys
  25002. if (!keys || typeof options[i] !== 'undefined') {
  25003. if (pointArrayMap[j].indexOf('.') > 0) {
  25004. // Handle nested keys, e.g. ['color.pattern.image']
  25005. // Avoid function call unless necessary.
  25006. Point.prototype.setNestedProperty(ret, options[i], pointArrayMap[j]);
  25007. }
  25008. else {
  25009. ret[pointArrayMap[j]] = options[i];
  25010. }
  25011. }
  25012. i++;
  25013. j++;
  25014. }
  25015. }
  25016. else if (typeof options === 'object') {
  25017. ret = options;
  25018. // This is the fastest way to detect if there are individual point
  25019. // dataLabels that need to be considered in drawDataLabels. These
  25020. // can only occur in object configs.
  25021. if (options.dataLabels) {
  25022. series._hasPointLabels = true;
  25023. }
  25024. // Same approach as above for markers
  25025. if (options.marker) {
  25026. series._hasPointMarkers = true;
  25027. }
  25028. }
  25029. return ret;
  25030. }
  25031. /**
  25032. * Get the pixel position of the point relative to the plot area.
  25033. * @private
  25034. * @function Highcharts.Point#pos
  25035. */
  25036. pos(chartCoordinates, plotY = this.plotY) {
  25037. if (!this.destroyed) {
  25038. const { plotX, series } = this, { chart, xAxis, yAxis } = series;
  25039. let posX = 0, posY = 0;
  25040. if (isNumber(plotX) && isNumber(plotY)) {
  25041. if (chartCoordinates) {
  25042. posX = xAxis ? xAxis.pos : chart.plotLeft;
  25043. posY = yAxis ? yAxis.pos : chart.plotTop;
  25044. }
  25045. return chart.inverted && xAxis && yAxis ?
  25046. [yAxis.len - plotY + posY, xAxis.len - plotX + posX] :
  25047. [plotX + posX, plotY + posY];
  25048. }
  25049. }
  25050. }
  25051. /**
  25052. * @private
  25053. * @function Highcharts.Point#resolveColor
  25054. */
  25055. resolveColor() {
  25056. const series = this.series, optionsChart = series.chart.options.chart, styledMode = series.chart.styledMode;
  25057. let color, colors, colorCount = optionsChart.colorCount, colorIndex;
  25058. // remove points nonZonedColor for later recalculation
  25059. delete this.nonZonedColor;
  25060. if (series.options.colorByPoint) {
  25061. if (!styledMode) {
  25062. colors = series.options.colors || series.chart.options.colors;
  25063. color = colors[series.colorCounter];
  25064. colorCount = colors.length;
  25065. }
  25066. colorIndex = series.colorCounter;
  25067. series.colorCounter++;
  25068. // loop back to zero
  25069. if (series.colorCounter === colorCount) {
  25070. series.colorCounter = 0;
  25071. }
  25072. }
  25073. else {
  25074. if (!styledMode) {
  25075. color = series.color;
  25076. }
  25077. colorIndex = series.colorIndex;
  25078. }
  25079. /**
  25080. * The point's current color index, used in styled mode instead of
  25081. * `color`. The color index is inserted in class names used for styling.
  25082. *
  25083. * @name Highcharts.Point#colorIndex
  25084. * @type {number|undefined}
  25085. */
  25086. this.colorIndex = pick(this.options.colorIndex, colorIndex);
  25087. /**
  25088. * The point's current color.
  25089. *
  25090. * @name Highcharts.Point#color
  25091. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  25092. */
  25093. this.color = pick(this.options.color, color);
  25094. }
  25095. /**
  25096. * Set a value in an object, on the property defined by key. The key
  25097. * supports nested properties using dot notation. The function modifies the
  25098. * input object and does not make a copy.
  25099. *
  25100. * @function Highcharts.Point#setNestedProperty<T>
  25101. *
  25102. * @param {T} object
  25103. * The object to set the value on.
  25104. *
  25105. * @param {*} value
  25106. * The value to set.
  25107. *
  25108. * @param {string} key
  25109. * Key to the property to set.
  25110. *
  25111. * @return {T}
  25112. * The modified object.
  25113. */
  25114. setNestedProperty(object, value, key) {
  25115. const nestedKeys = key.split('.');
  25116. nestedKeys.reduce(function (result, key, i, arr) {
  25117. const isLastKey = arr.length - 1 === i;
  25118. result[key] = (isLastKey ?
  25119. value :
  25120. isObject(result[key], true) ?
  25121. result[key] :
  25122. {});
  25123. return result[key];
  25124. }, object);
  25125. return object;
  25126. }
  25127. shouldDraw() {
  25128. return !this.isNull;
  25129. }
  25130. /**
  25131. * Extendable method for formatting each point's tooltip line.
  25132. *
  25133. * @function Highcharts.Point#tooltipFormatter
  25134. *
  25135. * @param {string} pointFormat
  25136. * The point format.
  25137. *
  25138. * @return {string}
  25139. * A string to be concatenated in to the common tooltip text.
  25140. */
  25141. tooltipFormatter(pointFormat) {
  25142. // Insert options for valueDecimals, valuePrefix, and valueSuffix
  25143. const series = this.series, seriesTooltipOptions = series.tooltipOptions, valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), valuePrefix = seriesTooltipOptions.valuePrefix || '', valueSuffix = seriesTooltipOptions.valueSuffix || '';
  25144. // Replace default point style with class name
  25145. if (series.chart.styledMode) {
  25146. pointFormat =
  25147. series.chart.tooltip.styledModeFormat(pointFormat);
  25148. }
  25149. // Loop over the point array map and replace unformatted values with
  25150. // sprintf formatting markup
  25151. (series.pointArrayMap || ['y']).forEach(function (key) {
  25152. key = '{point.' + key; // without the closing bracket
  25153. if (valuePrefix || valueSuffix) {
  25154. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), valuePrefix + key + '}' + valueSuffix);
  25155. }
  25156. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), key + ':,.' + valueDecimals + 'f}');
  25157. });
  25158. return format(pointFormat, {
  25159. point: this,
  25160. series: this.series
  25161. }, series.chart);
  25162. }
  25163. /**
  25164. * Update point with new options (typically x/y data) and optionally redraw
  25165. * the series.
  25166. *
  25167. * @sample highcharts/members/point-update-column/
  25168. * Update column value
  25169. * @sample highcharts/members/point-update-pie/
  25170. * Update pie slice
  25171. * @sample maps/members/point-update/
  25172. * Update map area value in Highmaps
  25173. *
  25174. * @function Highcharts.Point#update
  25175. *
  25176. * @param {Highcharts.PointOptionsType} options
  25177. * The point options. Point options are handled as described under
  25178. * the `series.type.data` item for each series type. For example
  25179. * for a line series, if options is a single number, the point will
  25180. * be given that number as the marin y value. If it is an array, it
  25181. * will be interpreted as x and y values respectively. If it is an
  25182. * object, advanced options are applied.
  25183. *
  25184. * @param {boolean} [redraw=true]
  25185. * Whether to redraw the chart after the point is updated. If doing
  25186. * more operations on the chart, it is best practice to set
  25187. * `redraw` to false and call `chart.redraw()` after.
  25188. *
  25189. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=true]
  25190. * Whether to apply animation, and optionally animation
  25191. * configuration.
  25192. *
  25193. * @emits Highcharts.Point#event:update
  25194. */
  25195. update(options, redraw, animation, runEvent) {
  25196. const point = this, series = point.series, graphic = point.graphic, chart = series.chart, seriesOptions = series.options;
  25197. let i;
  25198. redraw = pick(redraw, true);
  25199. /**
  25200. * @private
  25201. */
  25202. function update() {
  25203. point.applyOptions(options);
  25204. // Update visuals, #4146
  25205. // Handle mock graphic elements for a11y, #12718
  25206. const hasMockGraphic = graphic && point.hasMockGraphic;
  25207. const shouldDestroyGraphic = point.y === null ?
  25208. !hasMockGraphic :
  25209. hasMockGraphic;
  25210. if (graphic && shouldDestroyGraphic) {
  25211. point.graphic = graphic.destroy();
  25212. delete point.hasMockGraphic;
  25213. }
  25214. if (isObject(options, true)) {
  25215. // Destroy so we can get new elements
  25216. if (graphic && graphic.element) {
  25217. // "null" is also a valid symbol
  25218. if (options &&
  25219. options.marker &&
  25220. typeof options.marker.symbol !== 'undefined') {
  25221. point.graphic = graphic.destroy();
  25222. }
  25223. }
  25224. if (options && options.dataLabels && point.dataLabel) {
  25225. point.dataLabel = point.dataLabel.destroy(); // #2468
  25226. }
  25227. if (point.connector) {
  25228. point.connector = point.connector.destroy(); // #7243
  25229. }
  25230. }
  25231. // record changes in the parallel arrays
  25232. i = point.index;
  25233. series.updateParallelArrays(point, i);
  25234. // Record the options to options.data. If the old or the new config
  25235. // is an object, use point options, otherwise use raw options
  25236. // (#4701, #4916).
  25237. seriesOptions.data[i] = (isObject(seriesOptions.data[i], true) ||
  25238. isObject(options, true)) ?
  25239. point.options :
  25240. pick(options, seriesOptions.data[i]);
  25241. // redraw
  25242. series.isDirty = series.isDirtyData = true;
  25243. if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
  25244. chart.isDirtyBox = true;
  25245. }
  25246. if (seriesOptions.legendType === 'point') { // #1831, #1885
  25247. chart.isDirtyLegend = true;
  25248. }
  25249. if (redraw) {
  25250. chart.redraw(animation);
  25251. }
  25252. }
  25253. // Fire the event with a default handler of doing the update
  25254. if (runEvent === false) { // When called from setData
  25255. update();
  25256. }
  25257. else {
  25258. point.firePointEvent('update', { options: options }, update);
  25259. }
  25260. }
  25261. /**
  25262. * Remove a point and optionally redraw the series and if necessary the axes
  25263. *
  25264. * @sample highcharts/plotoptions/series-point-events-remove/
  25265. * Remove point and confirm
  25266. * @sample highcharts/members/point-remove/
  25267. * Remove pie slice
  25268. * @sample maps/members/point-remove/
  25269. * Remove selected points in Highmaps
  25270. *
  25271. * @function Highcharts.Point#remove
  25272. *
  25273. * @param {boolean} [redraw=true]
  25274. * Whether to redraw the chart or wait for an explicit call. When
  25275. * doing more operations on the chart, for example running
  25276. * `point.remove()` in a loop, it is best practice to set `redraw`
  25277. * to false and call `chart.redraw()` after.
  25278. *
  25279. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=false]
  25280. * Whether to apply animation, and optionally animation
  25281. * configuration.
  25282. */
  25283. remove(redraw, animation) {
  25284. this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
  25285. }
  25286. /**
  25287. * Toggle the selection status of a point.
  25288. *
  25289. * @see Highcharts.Chart#getSelectedPoints
  25290. *
  25291. * @sample highcharts/members/point-select/
  25292. * Select a point from a button
  25293. * @sample highcharts/chart/events-selection-points/
  25294. * Select a range of points through a drag selection
  25295. * @sample maps/series/data-id/
  25296. * Select a point in Highmaps
  25297. *
  25298. * @function Highcharts.Point#select
  25299. *
  25300. * @param {boolean} [selected]
  25301. * When `true`, the point is selected. When `false`, the point is
  25302. * unselected. When `null` or `undefined`, the selection state is toggled.
  25303. *
  25304. * @param {boolean} [accumulate=false]
  25305. * When `true`, the selection is added to other selected points.
  25306. * When `false`, other selected points are deselected. Internally in
  25307. * Highcharts, when
  25308. * [allowPointSelect](https://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
  25309. * is `true`, selected points are accumulated on Control, Shift or Cmd
  25310. * clicking the point.
  25311. *
  25312. * @emits Highcharts.Point#event:select
  25313. * @emits Highcharts.Point#event:unselect
  25314. */
  25315. select(selected, accumulate) {
  25316. const point = this, series = point.series, chart = series.chart;
  25317. selected = pick(selected, !point.selected);
  25318. this.selectedStaging = selected;
  25319. // fire the event with the default handler
  25320. point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
  25321. /**
  25322. * Whether the point is selected or not.
  25323. *
  25324. * @see Point#select
  25325. * @see Chart#getSelectedPoints
  25326. *
  25327. * @name Highcharts.Point#selected
  25328. * @type {boolean}
  25329. */
  25330. point.selected = point.options.selected = selected;
  25331. series.options.data[series.data.indexOf(point)] =
  25332. point.options;
  25333. point.setState(selected && 'select');
  25334. // unselect all other points unless Ctrl or Cmd + click
  25335. if (!accumulate) {
  25336. chart.getSelectedPoints().forEach(function (loopPoint) {
  25337. const loopSeries = loopPoint.series;
  25338. if (loopPoint.selected && loopPoint !== point) {
  25339. loopPoint.selected = loopPoint.options.selected =
  25340. false;
  25341. loopSeries.options.data[loopSeries.data.indexOf(loopPoint)] = loopPoint.options;
  25342. // Programatically selecting a point should restore
  25343. // normal state, but when click happened on other
  25344. // point, set inactive state to match other points
  25345. loopPoint.setState(chart.hoverPoints &&
  25346. loopSeries.options.inactiveOtherPoints ?
  25347. 'inactive' : '');
  25348. loopPoint.firePointEvent('unselect');
  25349. }
  25350. });
  25351. }
  25352. });
  25353. delete this.selectedStaging;
  25354. }
  25355. /**
  25356. * Runs on mouse over the point. Called internally from mouse and touch
  25357. * events.
  25358. *
  25359. * @function Highcharts.Point#onMouseOver
  25360. *
  25361. * @param {Highcharts.PointerEventObject} [e]
  25362. * The event arguments.
  25363. */
  25364. onMouseOver(e) {
  25365. const point = this, series = point.series, chart = series.chart, pointer = chart.pointer;
  25366. e = e ?
  25367. pointer.normalize(e) :
  25368. // In cases where onMouseOver is called directly without an event
  25369. pointer.getChartCoordinatesFromPoint(point, chart.inverted);
  25370. pointer.runPointActions(e, point);
  25371. }
  25372. /**
  25373. * Runs on mouse out from the point. Called internally from mouse and touch
  25374. * events.
  25375. *
  25376. * @function Highcharts.Point#onMouseOut
  25377. * @emits Highcharts.Point#event:mouseOut
  25378. */
  25379. onMouseOut() {
  25380. const point = this, chart = point.series.chart;
  25381. point.firePointEvent('mouseOut');
  25382. if (!point.series.options.inactiveOtherPoints) {
  25383. (chart.hoverPoints || []).forEach(function (p) {
  25384. p.setState();
  25385. });
  25386. }
  25387. chart.hoverPoints = chart.hoverPoint = null;
  25388. }
  25389. /**
  25390. * Import events from the series' and point's options. Only do it on
  25391. * demand, to save processing time on hovering.
  25392. *
  25393. * @private
  25394. * @function Highcharts.Point#importEvents
  25395. */
  25396. importEvents() {
  25397. if (!this.hasImportedEvents) {
  25398. const point = this, options = merge(point.series.options.point, point.options), events = options.events;
  25399. point.events = events;
  25400. objectEach(events, function (event, eventType) {
  25401. if (isFunction(event)) {
  25402. addEvent(point, eventType, event);
  25403. }
  25404. });
  25405. this.hasImportedEvents = true;
  25406. }
  25407. }
  25408. /**
  25409. * Set the point's state.
  25410. *
  25411. * @function Highcharts.Point#setState
  25412. *
  25413. * @param {Highcharts.PointStateValue|""} [state]
  25414. * The new state, can be one of `'hover'`, `'select'`, `'inactive'`,
  25415. * or `''` (an empty string), `'normal'` or `undefined` to set to
  25416. * normal state.
  25417. * @param {boolean} [move]
  25418. * State for animation.
  25419. *
  25420. * @emits Highcharts.Point#event:afterSetState
  25421. */
  25422. setState(state, move) {
  25423. const point = this, series = point.series, previousState = point.state, stateOptions = (series.options.states[state || 'normal'] ||
  25424. {}), markerOptions = (defaultOptions.plotOptions[series.type].marker &&
  25425. series.options.marker), normalDisabled = (markerOptions && markerOptions.enabled === false), markerStateOptions = ((markerOptions &&
  25426. markerOptions.states &&
  25427. markerOptions.states[state || 'normal']) || {}), stateDisabled = markerStateOptions.enabled === false, pointMarker = point.marker || {}, chart = series.chart, hasMarkers = (markerOptions && series.markerAttribs);
  25428. let halo = series.halo, markerAttribs, pointAttribs, pointAttribsAnimation, stateMarkerGraphic = series.stateMarkerGraphic, newSymbol;
  25429. state = state || ''; // empty string
  25430. if (
  25431. // already has this state
  25432. (state === point.state && !move) ||
  25433. // selected points don't respond to hover
  25434. (point.selected && state !== 'select') ||
  25435. // series' state options is disabled
  25436. (stateOptions.enabled === false) ||
  25437. // general point marker's state options is disabled
  25438. (state && (stateDisabled ||
  25439. (normalDisabled &&
  25440. markerStateOptions.enabled === false))) ||
  25441. // individual point marker's state options is disabled
  25442. (state &&
  25443. pointMarker.states &&
  25444. pointMarker.states[state] &&
  25445. pointMarker.states[state].enabled === false) // #1610
  25446. ) {
  25447. return;
  25448. }
  25449. point.state = state;
  25450. if (hasMarkers) {
  25451. markerAttribs = series.markerAttribs(point, state);
  25452. }
  25453. // Apply hover styles to the existing point
  25454. // Prevent from mocked null points (#14966)
  25455. if (point.graphic && !point.hasMockGraphic) {
  25456. if (previousState) {
  25457. point.graphic.removeClass('highcharts-point-' + previousState);
  25458. }
  25459. if (state) {
  25460. point.graphic.addClass('highcharts-point-' + state);
  25461. }
  25462. if (!chart.styledMode) {
  25463. pointAttribs = series.pointAttribs(point, state);
  25464. pointAttribsAnimation = pick(chart.options.chart.animation, stateOptions.animation);
  25465. const opacity = pointAttribs.opacity;
  25466. // Some inactive points (e.g. slices in pie) should apply
  25467. // opacity also for their labels
  25468. if (series.options.inactiveOtherPoints && isNumber(opacity)) {
  25469. (point.dataLabels || []).forEach(function (label) {
  25470. if (label &&
  25471. !label.hasClass('highcharts-data-label-hidden')) {
  25472. label.animate({ opacity }, pointAttribsAnimation);
  25473. }
  25474. });
  25475. if (point.connector) {
  25476. point.connector.animate({ opacity }, pointAttribsAnimation);
  25477. }
  25478. }
  25479. point.graphic.animate(pointAttribs, pointAttribsAnimation);
  25480. }
  25481. if (markerAttribs) {
  25482. point.graphic.animate(markerAttribs, pick(
  25483. // Turn off globally:
  25484. chart.options.chart.animation, markerStateOptions.animation, markerOptions.animation));
  25485. }
  25486. // Zooming in from a range with no markers to a range with markers
  25487. if (stateMarkerGraphic) {
  25488. stateMarkerGraphic.hide();
  25489. }
  25490. }
  25491. else {
  25492. // if a graphic is not applied to each point in the normal state,
  25493. // create a shared graphic for the hover state
  25494. if (state && markerStateOptions) {
  25495. newSymbol = pointMarker.symbol || series.symbol;
  25496. // If the point has another symbol than the previous one, throw
  25497. // away the state marker graphic and force a new one (#1459)
  25498. if (stateMarkerGraphic &&
  25499. stateMarkerGraphic.currentSymbol !== newSymbol) {
  25500. stateMarkerGraphic = stateMarkerGraphic.destroy();
  25501. }
  25502. // Add a new state marker graphic
  25503. if (markerAttribs) {
  25504. if (!stateMarkerGraphic) {
  25505. if (newSymbol) {
  25506. series.stateMarkerGraphic = stateMarkerGraphic =
  25507. chart.renderer
  25508. .symbol(newSymbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height)
  25509. .add(series.markerGroup);
  25510. stateMarkerGraphic.currentSymbol = newSymbol;
  25511. }
  25512. // Move the existing graphic
  25513. }
  25514. else {
  25515. stateMarkerGraphic[move ? 'animate' : 'attr']({
  25516. x: markerAttribs.x,
  25517. y: markerAttribs.y
  25518. });
  25519. }
  25520. }
  25521. if (!chart.styledMode && stateMarkerGraphic &&
  25522. point.state !== 'inactive') {
  25523. stateMarkerGraphic.attr(series.pointAttribs(point, state));
  25524. }
  25525. }
  25526. if (stateMarkerGraphic) {
  25527. stateMarkerGraphic[state && point.isInside ? 'show' : 'hide'](); // #2450
  25528. stateMarkerGraphic.element.point = point; // #4310
  25529. stateMarkerGraphic.addClass(point.getClassName(), true);
  25530. }
  25531. }
  25532. // Show me your halo
  25533. const haloOptions = stateOptions.halo;
  25534. const markerGraphic = (point.graphic || stateMarkerGraphic);
  25535. const markerVisibility = (markerGraphic && markerGraphic.visibility || 'inherit');
  25536. if (haloOptions &&
  25537. haloOptions.size &&
  25538. markerGraphic &&
  25539. markerVisibility !== 'hidden' &&
  25540. !point.isCluster) {
  25541. if (!halo) {
  25542. series.halo = halo = chart.renderer.path()
  25543. // #5818, #5903, #6705
  25544. .add(markerGraphic.parentGroup);
  25545. }
  25546. halo.show()[move ? 'animate' : 'attr']({
  25547. d: point.haloPath(haloOptions.size)
  25548. });
  25549. halo.attr({
  25550. 'class': 'highcharts-halo highcharts-color-' +
  25551. pick(point.colorIndex, series.colorIndex) +
  25552. (point.className ? ' ' + point.className : ''),
  25553. 'visibility': markerVisibility,
  25554. 'zIndex': -1 // #4929, #8276
  25555. });
  25556. halo.point = point; // #6055
  25557. if (!chart.styledMode) {
  25558. halo.attr(extend({
  25559. 'fill': point.color || series.color,
  25560. 'fill-opacity': haloOptions.opacity
  25561. }, AST.filterUserAttributes(haloOptions.attributes || {})));
  25562. }
  25563. }
  25564. else if (halo && halo.point && halo.point.haloPath) {
  25565. // Animate back to 0 on the current halo point (#6055)
  25566. halo.animate({ d: halo.point.haloPath(0) }, null,
  25567. // Hide after unhovering. The `complete` callback runs in the
  25568. // halo's context (#7681).
  25569. halo.hide);
  25570. }
  25571. fireEvent(point, 'afterSetState', { state });
  25572. }
  25573. /**
  25574. * Get the path definition for the halo, which is usually a shadow-like
  25575. * circle around the currently hovered point.
  25576. *
  25577. * @function Highcharts.Point#haloPath
  25578. *
  25579. * @param {number} size
  25580. * The radius of the circular halo.
  25581. *
  25582. * @return {Highcharts.SVGPathArray}
  25583. * The path definition.
  25584. */
  25585. haloPath(size) {
  25586. const pos = this.pos();
  25587. return pos ? this.series.chart.renderer.symbols.circle(Math.floor(pos[0]) - size, pos[1] - size, size * 2, size * 2) : [];
  25588. }
  25589. }
  25590. /* *
  25591. *
  25592. * Default Export
  25593. *
  25594. * */
  25595. /* *
  25596. *
  25597. * API Declarations
  25598. *
  25599. * */
  25600. /**
  25601. * Function callback when a series point is clicked. Return false to cancel the
  25602. * action.
  25603. *
  25604. * @callback Highcharts.PointClickCallbackFunction
  25605. *
  25606. * @param {Highcharts.Point} this
  25607. * The point where the event occured.
  25608. *
  25609. * @param {Highcharts.PointClickEventObject} event
  25610. * Event arguments.
  25611. */
  25612. /**
  25613. * Common information for a click event on a series point.
  25614. *
  25615. * @interface Highcharts.PointClickEventObject
  25616. * @extends Highcharts.PointerEventObject
  25617. */ /**
  25618. * Clicked point.
  25619. * @name Highcharts.PointClickEventObject#point
  25620. * @type {Highcharts.Point}
  25621. */
  25622. /**
  25623. * Configuration for the data label and tooltip formatters.
  25624. *
  25625. * @interface Highcharts.PointLabelObject
  25626. */ /**
  25627. * The point's current color.
  25628. * @name Highcharts.PointLabelObject#color
  25629. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  25630. */ /**
  25631. * The point's current color index, used in styled mode instead of `color`. The
  25632. * color index is inserted in class names used for styling.
  25633. * @name Highcharts.PointLabelObject#colorIndex
  25634. * @type {number}
  25635. */ /**
  25636. * The name of the related point.
  25637. * @name Highcharts.PointLabelObject#key
  25638. * @type {string|undefined}
  25639. */ /**
  25640. * The percentage for related points in a stacked series or pies.
  25641. * @name Highcharts.PointLabelObject#percentage
  25642. * @type {number}
  25643. */ /**
  25644. * The related point. The point name, if defined, is available through
  25645. * `this.point.name`.
  25646. * @name Highcharts.PointLabelObject#point
  25647. * @type {Highcharts.Point}
  25648. */ /**
  25649. * The related series. The series name is available through `this.series.name`.
  25650. * @name Highcharts.PointLabelObject#series
  25651. * @type {Highcharts.Series}
  25652. */ /**
  25653. * The total of values in either a stack for stacked series, or a pie in a pie
  25654. * series.
  25655. * @name Highcharts.PointLabelObject#total
  25656. * @type {number|undefined}
  25657. */ /**
  25658. * For categorized axes this property holds the category name for the point. For
  25659. * other axes it holds the X value.
  25660. * @name Highcharts.PointLabelObject#x
  25661. * @type {number|string|undefined}
  25662. */ /**
  25663. * The y value of the point.
  25664. * @name Highcharts.PointLabelObject#y
  25665. * @type {number|null|undefined}
  25666. */
  25667. /**
  25668. * Gets fired when the mouse leaves the area close to the point.
  25669. *
  25670. * @callback Highcharts.PointMouseOutCallbackFunction
  25671. *
  25672. * @param {Highcharts.Point} this
  25673. * Point where the event occured.
  25674. *
  25675. * @param {global.PointerEvent} event
  25676. * Event that occured.
  25677. */
  25678. /**
  25679. * Gets fired when the mouse enters the area close to the point.
  25680. *
  25681. * @callback Highcharts.PointMouseOverCallbackFunction
  25682. *
  25683. * @param {Highcharts.Point} this
  25684. * Point where the event occured.
  25685. *
  25686. * @param {global.Event} event
  25687. * Event that occured.
  25688. */
  25689. /**
  25690. * The generic point options for all series.
  25691. *
  25692. * In TypeScript you have to extend `PointOptionsObject` with an additional
  25693. * declaration to allow custom data options:
  25694. *
  25695. * ```
  25696. * declare interface PointOptionsObject {
  25697. * customProperty: string;
  25698. * }
  25699. * ```
  25700. *
  25701. * @interface Highcharts.PointOptionsObject
  25702. */
  25703. /**
  25704. * Possible option types for a data point. Use `null` to indicate a gap.
  25705. *
  25706. * @typedef {number|string|Highcharts.PointOptionsObject|Array<(number|string|null)>|null} Highcharts.PointOptionsType
  25707. */
  25708. /**
  25709. * Gets fired when the point is removed using the `.remove()` method.
  25710. *
  25711. * @callback Highcharts.PointRemoveCallbackFunction
  25712. *
  25713. * @param {Highcharts.Point} this
  25714. * Point where the event occured.
  25715. *
  25716. * @param {global.Event} event
  25717. * Event that occured.
  25718. */
  25719. /**
  25720. * Possible key values for the point state options.
  25721. *
  25722. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.PointStateValue
  25723. */
  25724. /**
  25725. * Gets fired when the point is updated programmatically through the `.update()`
  25726. * method.
  25727. *
  25728. * @callback Highcharts.PointUpdateCallbackFunction
  25729. *
  25730. * @param {Highcharts.Point} this
  25731. * Point where the event occured.
  25732. *
  25733. * @param {Highcharts.PointUpdateEventObject} event
  25734. * Event that occured.
  25735. */
  25736. /**
  25737. * Information about the update event.
  25738. *
  25739. * @interface Highcharts.PointUpdateEventObject
  25740. * @extends global.Event
  25741. */ /**
  25742. * Options data of the update event.
  25743. * @name Highcharts.PointUpdateEventObject#options
  25744. * @type {Highcharts.PointOptionsType}
  25745. */
  25746. /**
  25747. * @interface Highcharts.PointEventsOptionsObject
  25748. */ /**
  25749. * Fires when the point is selected either programmatically or following a click
  25750. * on the point. One parameter, `event`, is passed to the function. Returning
  25751. * `false` cancels the operation.
  25752. * @name Highcharts.PointEventsOptionsObject#select
  25753. * @type {Highcharts.PointSelectCallbackFunction|undefined}
  25754. */ /**
  25755. * Fires when the point is unselected either programmatically or following a
  25756. * click on the point. One parameter, `event`, is passed to the function.
  25757. * Returning `false` cancels the operation.
  25758. * @name Highcharts.PointEventsOptionsObject#unselect
  25759. * @type {Highcharts.PointUnselectCallbackFunction|undefined}
  25760. */
  25761. /**
  25762. * Information about the select/unselect event.
  25763. *
  25764. * @interface Highcharts.PointInteractionEventObject
  25765. * @extends global.Event
  25766. */ /**
  25767. * @name Highcharts.PointInteractionEventObject#accumulate
  25768. * @type {boolean}
  25769. */
  25770. /**
  25771. * Gets fired when the point is selected either programmatically or following a
  25772. * click on the point.
  25773. *
  25774. * @callback Highcharts.PointSelectCallbackFunction
  25775. *
  25776. * @param {Highcharts.Point} this
  25777. * Point where the event occured.
  25778. *
  25779. * @param {Highcharts.PointInteractionEventObject} event
  25780. * Event that occured.
  25781. */
  25782. /**
  25783. * Fires when the point is unselected either programmatically or following a
  25784. * click on the point.
  25785. *
  25786. * @callback Highcharts.PointUnselectCallbackFunction
  25787. *
  25788. * @param {Highcharts.Point} this
  25789. * Point where the event occured.
  25790. *
  25791. * @param {Highcharts.PointInteractionEventObject} event
  25792. * Event that occured.
  25793. */
  25794. ''; // keeps doclets above in JS file.
  25795. return Point;
  25796. });
  25797. _registerModule(_modules, 'Core/Pointer.js', [_modules['Core/Color/Color.js'], _modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (Color, H, U) {
  25798. /* *
  25799. *
  25800. * (c) 2010-2021 Torstein Honsi
  25801. *
  25802. * License: www.highcharts.com/license
  25803. *
  25804. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  25805. *
  25806. * */
  25807. const { parse: color } = Color;
  25808. const { charts, noop } = H;
  25809. const { addEvent, attr, css, defined, extend, find, fireEvent, isNumber, isObject, objectEach, offset, pick, splat } = U;
  25810. /* *
  25811. *
  25812. * Class
  25813. *
  25814. * */
  25815. /**
  25816. * The mouse and touch tracker object. Each {@link Chart} item has one
  25817. * associated Pointer item that can be accessed from the {@link Chart.pointer}
  25818. * property.
  25819. *
  25820. * @class
  25821. * @name Highcharts.Pointer
  25822. *
  25823. * @param {Highcharts.Chart} chart
  25824. * The chart instance.
  25825. *
  25826. * @param {Highcharts.Options} options
  25827. * The root options object. The pointer uses options from the chart and tooltip
  25828. * structures.
  25829. */
  25830. class Pointer {
  25831. /* *
  25832. *
  25833. * Constructors
  25834. *
  25835. * */
  25836. constructor(chart, options) {
  25837. this.lastValidTouch = {};
  25838. this.pinchDown = [];
  25839. this.runChartClick = false;
  25840. this.eventsToUnbind = [];
  25841. this.chart = chart;
  25842. this.hasDragged = false;
  25843. this.options = options;
  25844. this.init(chart, options);
  25845. }
  25846. /* *
  25847. *
  25848. * Functions
  25849. *
  25850. * */
  25851. /**
  25852. * Set inactive state to all series that are not currently hovered,
  25853. * or, if `inactiveOtherPoints` is set to true, set inactive state to
  25854. * all points within that series.
  25855. *
  25856. * @private
  25857. * @function Highcharts.Pointer#applyInactiveState
  25858. *
  25859. * @param {Array<Highcharts.Point>} points
  25860. * Currently hovered points
  25861. */
  25862. applyInactiveState(points) {
  25863. let activeSeries = [], series;
  25864. // Get all active series from the hovered points
  25865. (points || []).forEach(function (item) {
  25866. series = item.series;
  25867. // Include itself
  25868. activeSeries.push(series);
  25869. // Include parent series
  25870. if (series.linkedParent) {
  25871. activeSeries.push(series.linkedParent);
  25872. }
  25873. // Include all child series
  25874. if (series.linkedSeries) {
  25875. activeSeries = activeSeries.concat(series.linkedSeries);
  25876. }
  25877. // Include navigator series
  25878. if (series.navigatorSeries) {
  25879. activeSeries.push(series.navigatorSeries);
  25880. }
  25881. });
  25882. // Now loop over all series, filtering out active series
  25883. this.chart.series.forEach(function (inactiveSeries) {
  25884. if (activeSeries.indexOf(inactiveSeries) === -1) {
  25885. // Inactive series
  25886. inactiveSeries.setState('inactive', true);
  25887. }
  25888. else if (inactiveSeries.options.inactiveOtherPoints) {
  25889. // Active series, but other points should be inactivated
  25890. inactiveSeries.setAllPointsToState('inactive');
  25891. }
  25892. });
  25893. }
  25894. /**
  25895. * Destroys the Pointer object and disconnects DOM events.
  25896. *
  25897. * @function Highcharts.Pointer#destroy
  25898. */
  25899. destroy() {
  25900. const pointer = this;
  25901. this.eventsToUnbind.forEach((unbind) => unbind());
  25902. this.eventsToUnbind = [];
  25903. if (!H.chartCount) {
  25904. if (Pointer.unbindDocumentMouseUp) {
  25905. Pointer.unbindDocumentMouseUp = Pointer.unbindDocumentMouseUp();
  25906. }
  25907. if (Pointer.unbindDocumentTouchEnd) {
  25908. Pointer.unbindDocumentTouchEnd = (Pointer.unbindDocumentTouchEnd());
  25909. }
  25910. }
  25911. // memory and CPU leak
  25912. clearInterval(pointer.tooltipTimeout);
  25913. objectEach(pointer, function (_val, prop) {
  25914. pointer[prop] = void 0;
  25915. });
  25916. }
  25917. /**
  25918. * Calculate attrs for selection marker.
  25919. * @private
  25920. * @function Highcharts.Pointer#getSelectionMarkerAttrs
  25921. * @emits getSelectionMarkerAttrs
  25922. */
  25923. getSelectionMarkerAttrs(chartX, chartY) {
  25924. const e = {
  25925. args: { chartX, chartY },
  25926. attrs: {},
  25927. shapeType: 'rect'
  25928. };
  25929. fireEvent(this, 'getSelectionMarkerAttrs', e, (e) => {
  25930. const { chart, mouseDownX = 0, mouseDownY = 0, zoomHor, zoomVert } = this, attrs = e.attrs;
  25931. let size;
  25932. attrs.x = chart.plotLeft;
  25933. attrs.y = chart.plotTop;
  25934. attrs.width = zoomHor ? 1 : chart.plotWidth;
  25935. attrs.height = zoomVert ? 1 : chart.plotHeight;
  25936. // Adjust the width of the selection marker
  25937. if (zoomHor) {
  25938. size = chartX - mouseDownX;
  25939. attrs.width = Math.abs(size);
  25940. attrs.x = (size > 0 ? 0 : size) + mouseDownX;
  25941. }
  25942. // Adjust the height of the selection marker
  25943. if (zoomVert) {
  25944. size = chartY - mouseDownY;
  25945. attrs.height = Math.abs(size);
  25946. attrs.y = (size > 0 ? 0 : size) + mouseDownY;
  25947. }
  25948. });
  25949. return e;
  25950. }
  25951. /**
  25952. * Perform a drag operation in response to a mousemove event while the mouse
  25953. * is down.
  25954. * @private
  25955. * @function Highcharts.Pointer#drag
  25956. */
  25957. drag(e) {
  25958. const chart = this.chart, chartOptions = chart.options.chart, plotLeft = chart.plotLeft, plotTop = chart.plotTop, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, mouseDownX = (this.mouseDownX || 0), mouseDownY = (this.mouseDownY || 0), panningEnabled = isObject(chartOptions.panning) ?
  25959. chartOptions.panning && chartOptions.panning.enabled :
  25960. chartOptions.panning, panKey = (chartOptions.panKey && e[chartOptions.panKey + 'Key']);
  25961. let chartX = e.chartX, chartY = e.chartY, clickedInside, selectionMarker = this.selectionMarker;
  25962. // If the device supports both touch and mouse (like IE11), and we are
  25963. // touch-dragging inside the plot area, don't handle the mouse event.
  25964. // #4339.
  25965. if (selectionMarker && selectionMarker.touch) {
  25966. return;
  25967. }
  25968. // If the mouse is outside the plot area, adjust to coordinates
  25969. // inside to prevent the selection marker from going outside
  25970. if (chartX < plotLeft) {
  25971. chartX = plotLeft;
  25972. }
  25973. else if (chartX > plotLeft + plotWidth) {
  25974. chartX = plotLeft + plotWidth;
  25975. }
  25976. if (chartY < plotTop) {
  25977. chartY = plotTop;
  25978. }
  25979. else if (chartY > plotTop + plotHeight) {
  25980. chartY = plotTop + plotHeight;
  25981. }
  25982. // determine if the mouse has moved more than 10px
  25983. this.hasDragged = Math.sqrt(Math.pow(mouseDownX - chartX, 2) +
  25984. Math.pow(mouseDownY - chartY, 2));
  25985. if (this.hasDragged > 10) {
  25986. clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop, {
  25987. visiblePlotOnly: true
  25988. });
  25989. const { shapeType, attrs } = this.getSelectionMarkerAttrs(chartX, chartY);
  25990. // make a selection
  25991. if ((chart.hasCartesianSeries || chart.mapView) &&
  25992. (this.zoomX || this.zoomY) &&
  25993. clickedInside &&
  25994. !panKey) {
  25995. if (!selectionMarker) {
  25996. this.selectionMarker = selectionMarker =
  25997. chart.renderer[shapeType]();
  25998. selectionMarker
  25999. .attr({
  26000. 'class': 'highcharts-selection-marker',
  26001. zIndex: 7
  26002. })
  26003. .add();
  26004. if (!chart.styledMode) {
  26005. selectionMarker.attr({
  26006. fill: chartOptions.selectionMarkerFill ||
  26007. color("#334eff" /* Palette.highlightColor80 */)
  26008. .setOpacity(0.25).get()
  26009. });
  26010. }
  26011. }
  26012. }
  26013. if (selectionMarker) {
  26014. selectionMarker.attr(attrs);
  26015. }
  26016. // panning
  26017. if (clickedInside &&
  26018. !selectionMarker &&
  26019. panningEnabled) {
  26020. chart.pan(e, chartOptions.panning);
  26021. }
  26022. }
  26023. }
  26024. /**
  26025. * Start a drag operation.
  26026. * @private
  26027. * @function Highcharts.Pointer#dragStart
  26028. */
  26029. dragStart(e) {
  26030. const chart = this.chart;
  26031. // Record the start position
  26032. chart.mouseIsDown = e.type;
  26033. chart.cancelClick = false;
  26034. chart.mouseDownX = this.mouseDownX = e.chartX;
  26035. chart.mouseDownY = this.mouseDownY = e.chartY;
  26036. }
  26037. /**
  26038. * Get selection box to calculate extremes
  26039. * @private
  26040. * @function Highcharts.Pointer#getSelectionBox
  26041. * @emits getSelectionBox
  26042. */
  26043. getSelectionBox(marker) {
  26044. const e = {
  26045. args: { marker },
  26046. result: {}
  26047. };
  26048. fireEvent(this, 'getSelectionBox', e, (e) => {
  26049. e.result = {
  26050. x: marker.attr ? +marker.attr('x') : marker.x,
  26051. y: marker.attr ? +marker.attr('y') : marker.y,
  26052. width: marker.attr ? marker.attr('width') : marker.width,
  26053. height: marker.attr ? marker.attr('height') : marker.height
  26054. };
  26055. });
  26056. return e.result;
  26057. }
  26058. /**
  26059. * On mouse up or touch end across the entire document, drop the selection.
  26060. * @private
  26061. * @function Highcharts.Pointer#drop
  26062. */
  26063. drop(e) {
  26064. const pointer = this, chart = this.chart, hasPinched = this.hasPinched;
  26065. if (this.selectionMarker) {
  26066. const { x, y, width, height } = this.getSelectionBox(this.selectionMarker);
  26067. const selectionData = {
  26068. originalEvent: e,
  26069. xAxis: [],
  26070. yAxis: [],
  26071. x,
  26072. y,
  26073. width,
  26074. height
  26075. };
  26076. // Start by false runZoom, unless when we have a mapView, in
  26077. // which case the zoom will be handled in the selection event.
  26078. let runZoom = Boolean(chart.mapView);
  26079. // a selection has been made
  26080. if (this.hasDragged || hasPinched) {
  26081. // record each axis' min and max
  26082. chart.axes.forEach(function (axis) {
  26083. if (axis.zoomEnabled &&
  26084. defined(axis.min) &&
  26085. (hasPinched ||
  26086. pointer[{
  26087. xAxis: 'zoomX',
  26088. yAxis: 'zoomY'
  26089. }[axis.coll]]) &&
  26090. isNumber(x) &&
  26091. isNumber(y) &&
  26092. isNumber(width) &&
  26093. isNumber(height)) { // #859, #3569
  26094. const horiz = axis.horiz, minPixelPadding = e.type === 'touchend' ?
  26095. axis.minPixelPadding :
  26096. 0, // #1207, #3075
  26097. selectionMin = axis.toValue((horiz ? x : y) + minPixelPadding), selectionMax = axis.toValue((horiz ? x + width : y + height) -
  26098. minPixelPadding);
  26099. selectionData[axis.coll].push({
  26100. axis: axis,
  26101. // Min/max for reversed axes
  26102. min: Math.min(selectionMin, selectionMax),
  26103. max: Math.max(selectionMin, selectionMax)
  26104. });
  26105. runZoom = true;
  26106. }
  26107. });
  26108. // if (runZoom) {
  26109. // fireEvent(chart, 'selection', selectionData, function (args) {
  26110. // chart.zoom(extend(args, hasPinched ?
  26111. // { animation: false } :
  26112. // null));
  26113. // });
  26114. // }
  26115. fireEvent(chart, 'selection', selectionData, function (args) {
  26116. chart.zoom(extend(args, hasPinched ?
  26117. { animation: false } :
  26118. null));
  26119. });
  26120. }
  26121. if (isNumber(chart.index)) {
  26122. this.selectionMarker = this.selectionMarker.destroy();
  26123. }
  26124. // Reset scaling preview
  26125. if (hasPinched) {
  26126. this.scaleGroups();
  26127. }
  26128. }
  26129. // Reset all. Check isNumber because it may be destroyed on mouse up
  26130. // (#877)
  26131. if (chart && isNumber(chart.index)) {
  26132. css(chart.container, { cursor: chart._cursor });
  26133. chart.cancelClick = this.hasDragged > 10; // #370
  26134. chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
  26135. this.pinchDown = [];
  26136. }
  26137. }
  26138. /**
  26139. * Finds the closest point to a set of coordinates, using the k-d-tree
  26140. * algorithm.
  26141. *
  26142. * @function Highcharts.Pointer#findNearestKDPoint
  26143. *
  26144. * @param {Array<Highcharts.Series>} series
  26145. * All the series to search in.
  26146. *
  26147. * @param {boolean|undefined} shared
  26148. * Whether it is a shared tooltip or not.
  26149. *
  26150. * @param {Highcharts.PointerEventObject} e
  26151. * The pointer event object, containing chart coordinates of the pointer.
  26152. *
  26153. * @return {Highcharts.Point|undefined}
  26154. * The point closest to given coordinates.
  26155. */
  26156. findNearestKDPoint(series, shared, e) {
  26157. let closest;
  26158. /** @private */
  26159. function sort(p1, p2) {
  26160. const isCloserX = p1.distX - p2.distX, isCloser = p1.dist - p2.dist, isAbove = ((p2.series.group && p2.series.group.zIndex) -
  26161. (p1.series.group && p1.series.group.zIndex));
  26162. let result;
  26163. // We have two points which are not in the same place on xAxis
  26164. // and shared tooltip:
  26165. if (isCloserX !== 0 && shared) { // #5721
  26166. result = isCloserX;
  26167. // Points are not exactly in the same place on x/yAxis:
  26168. }
  26169. else if (isCloser !== 0) {
  26170. result = isCloser;
  26171. // The same xAxis and yAxis position, sort by z-index:
  26172. }
  26173. else if (isAbove !== 0) {
  26174. result = isAbove;
  26175. // The same zIndex, sort by array index:
  26176. }
  26177. else {
  26178. result =
  26179. p1.series.index > p2.series.index ?
  26180. -1 :
  26181. 1;
  26182. }
  26183. return result;
  26184. }
  26185. series.forEach(function (s) {
  26186. const noSharedTooltip = s.noSharedTooltip && shared, compareX = (!noSharedTooltip &&
  26187. s.options.findNearestPointBy.indexOf('y') < 0), point = s.searchPoint(e, compareX);
  26188. if ( // Check that we actually found a point on the series.
  26189. isObject(point, true) && point.series &&
  26190. // Use the new point if it is closer.
  26191. (!isObject(closest, true) ||
  26192. (sort(closest, point) > 0))) {
  26193. closest = point;
  26194. }
  26195. });
  26196. return closest;
  26197. }
  26198. /**
  26199. * @private
  26200. * @function Highcharts.Pointer#getChartCoordinatesFromPoint
  26201. */
  26202. getChartCoordinatesFromPoint(point, inverted) {
  26203. const series = point.series, xAxis = series.xAxis, yAxis = series.yAxis, shapeArgs = point.shapeArgs;
  26204. if (xAxis && yAxis) {
  26205. let x = pick(point.clientX, point.plotX);
  26206. let y = point.plotY || 0;
  26207. if (point.isNode &&
  26208. shapeArgs &&
  26209. isNumber(shapeArgs.x) &&
  26210. isNumber(shapeArgs.y)) {
  26211. x = shapeArgs.x;
  26212. y = shapeArgs.y;
  26213. }
  26214. return inverted ? {
  26215. chartX: yAxis.len + yAxis.pos - y,
  26216. chartY: xAxis.len + xAxis.pos - x
  26217. } : {
  26218. chartX: x + xAxis.pos,
  26219. chartY: y + yAxis.pos
  26220. };
  26221. }
  26222. if (shapeArgs && shapeArgs.x && shapeArgs.y) {
  26223. // E.g. pies do not have axes
  26224. return {
  26225. chartX: shapeArgs.x,
  26226. chartY: shapeArgs.y
  26227. };
  26228. }
  26229. }
  26230. /**
  26231. * Return the cached chartPosition if it is available on the Pointer,
  26232. * otherwise find it. Running offset is quite expensive, so it should be
  26233. * avoided when we know the chart hasn't moved.
  26234. *
  26235. * @function Highcharts.Pointer#getChartPosition
  26236. *
  26237. * @return {Highcharts.ChartPositionObject}
  26238. * The offset of the chart container within the page
  26239. */
  26240. getChartPosition() {
  26241. if (this.chartPosition) {
  26242. return this.chartPosition;
  26243. }
  26244. const { container } = this.chart;
  26245. const pos = offset(container);
  26246. this.chartPosition = {
  26247. left: pos.left,
  26248. top: pos.top,
  26249. scaleX: 1,
  26250. scaleY: 1
  26251. };
  26252. const offsetWidth = container.offsetWidth;
  26253. const offsetHeight = container.offsetHeight;
  26254. // #13342 - tooltip was not visible in Chrome, when chart
  26255. // updates height.
  26256. if (offsetWidth > 2 && // #13342
  26257. offsetHeight > 2 // #13342
  26258. ) {
  26259. this.chartPosition.scaleX = pos.width / offsetWidth;
  26260. this.chartPosition.scaleY = pos.height / offsetHeight;
  26261. }
  26262. return this.chartPosition;
  26263. }
  26264. /**
  26265. * Get the click position in terms of axis values.
  26266. *
  26267. * @function Highcharts.Pointer#getCoordinates
  26268. *
  26269. * @param {Highcharts.PointerEventObject} e
  26270. * Pointer event, extended with `chartX` and `chartY` properties.
  26271. *
  26272. * @return {Highcharts.PointerAxisCoordinatesObject}
  26273. * Axis coordinates.
  26274. */
  26275. getCoordinates(e) {
  26276. const coordinates = {
  26277. xAxis: [],
  26278. yAxis: []
  26279. };
  26280. this.chart.axes.forEach(function (axis) {
  26281. coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
  26282. axis: axis,
  26283. value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
  26284. });
  26285. });
  26286. return coordinates;
  26287. }
  26288. /**
  26289. * Calculates what is the current hovered point/points and series.
  26290. *
  26291. * @private
  26292. * @function Highcharts.Pointer#getHoverData
  26293. *
  26294. * @param {Highcharts.Point|undefined} existingHoverPoint
  26295. * The point currently being hovered.
  26296. *
  26297. * @param {Highcharts.Series|undefined} existingHoverSeries
  26298. * The series currently being hovered.
  26299. *
  26300. * @param {Array<Highcharts.Series>} series
  26301. * All the series in the chart.
  26302. *
  26303. * @param {boolean} isDirectTouch
  26304. * Is the pointer directly hovering the point.
  26305. *
  26306. * @param {boolean|undefined} shared
  26307. * Whether it is a shared tooltip or not.
  26308. *
  26309. * @param {Highcharts.PointerEventObject} [e]
  26310. * The triggering event, containing chart coordinates of the pointer.
  26311. *
  26312. * @return {Object}
  26313. * Object containing resulting hover data: hoverPoint, hoverSeries, and
  26314. * hoverPoints.
  26315. */
  26316. getHoverData(existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
  26317. const hoverPoints = [], useExisting = !!(isDirectTouch && existingHoverPoint), filter = function (s) {
  26318. return (s.visible &&
  26319. !(!shared && s.directTouch) && // #3821
  26320. pick(s.options.enableMouseTracking, true));
  26321. };
  26322. let hoverSeries = existingHoverSeries,
  26323. // Which series to look in for the hover point
  26324. searchSeries,
  26325. // Parameters needed for beforeGetHoverData event.
  26326. eventArgs = {
  26327. chartX: e ? e.chartX : void 0,
  26328. chartY: e ? e.chartY : void 0,
  26329. shared: shared
  26330. };
  26331. // Find chart.hoverPane and update filter method in polar.
  26332. fireEvent(this, 'beforeGetHoverData', eventArgs);
  26333. const notSticky = hoverSeries && !hoverSeries.stickyTracking;
  26334. searchSeries = notSticky ?
  26335. // Only search on hovered series if it has stickyTracking false
  26336. [hoverSeries] :
  26337. // Filter what series to look in.
  26338. series.filter((s) => s.stickyTracking &&
  26339. (eventArgs.filter || filter)(s));
  26340. // Use existing hovered point or find the one closest to coordinates.
  26341. const hoverPoint = useExisting || !e ?
  26342. existingHoverPoint :
  26343. this.findNearestKDPoint(searchSeries, shared, e);
  26344. // Assign hover series
  26345. hoverSeries = hoverPoint && hoverPoint.series;
  26346. // If we have a hoverPoint, assign hoverPoints.
  26347. if (hoverPoint) {
  26348. // When tooltip is shared, it displays more than one point
  26349. if (shared && !hoverSeries.noSharedTooltip) {
  26350. searchSeries = series.filter(function (s) {
  26351. return eventArgs.filter ?
  26352. eventArgs.filter(s) : filter(s) && !s.noSharedTooltip;
  26353. });
  26354. // Get all points with the same x value as the hoverPoint
  26355. searchSeries.forEach(function (s) {
  26356. let point = find(s.points, function (p) {
  26357. return p.x === hoverPoint.x && !p.isNull;
  26358. });
  26359. if (isObject(point)) {
  26360. /*
  26361. * Boost returns a minimal point. Convert it to a usable
  26362. * point for tooltip and states.
  26363. */
  26364. if (s.boosted && s.boost) {
  26365. point = s.boost.getPoint(point);
  26366. }
  26367. hoverPoints.push(point);
  26368. }
  26369. });
  26370. }
  26371. else {
  26372. hoverPoints.push(hoverPoint);
  26373. }
  26374. }
  26375. // Check whether the hoverPoint is inside pane we are hovering over.
  26376. eventArgs = { hoverPoint: hoverPoint };
  26377. fireEvent(this, 'afterGetHoverData', eventArgs);
  26378. return {
  26379. hoverPoint: eventArgs.hoverPoint,
  26380. hoverSeries: hoverSeries,
  26381. hoverPoints: hoverPoints
  26382. };
  26383. }
  26384. /**
  26385. * @private
  26386. * @function Highcharts.Pointer#getPointFromEvent
  26387. */
  26388. getPointFromEvent(e) {
  26389. let target = e.target, point;
  26390. while (target && !point) {
  26391. point = target.point;
  26392. target = target.parentNode;
  26393. }
  26394. return point;
  26395. }
  26396. /**
  26397. * @private
  26398. * @function Highcharts.Pointer#onTrackerMouseOut
  26399. */
  26400. onTrackerMouseOut(e) {
  26401. const chart = this.chart;
  26402. const relatedTarget = e.relatedTarget;
  26403. const series = chart.hoverSeries;
  26404. this.isDirectTouch = false;
  26405. if (series &&
  26406. relatedTarget &&
  26407. !series.stickyTracking &&
  26408. !this.inClass(relatedTarget, 'highcharts-tooltip') &&
  26409. (!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465, #5553
  26410. !this.inClass(relatedTarget, 'highcharts-tracker'))) {
  26411. series.onMouseOut();
  26412. }
  26413. }
  26414. /**
  26415. * Utility to detect whether an element has, or has a parent with, a
  26416. * specific class name. Used on detection of tracker objects and on deciding
  26417. * whether hovering the tooltip should cause the active series to mouse out.
  26418. *
  26419. * @function Highcharts.Pointer#inClass
  26420. *
  26421. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  26422. * The element to investigate.
  26423. *
  26424. * @param {string} className
  26425. * The class name to look for.
  26426. *
  26427. * @return {boolean|undefined}
  26428. * True if either the element or one of its parents has the given class
  26429. * name.
  26430. */
  26431. inClass(element, className) {
  26432. let elem = element, elemClassName;
  26433. while (elem) {
  26434. elemClassName = attr(elem, 'class');
  26435. if (elemClassName) {
  26436. if (elemClassName.indexOf(className) !== -1) {
  26437. return true;
  26438. }
  26439. if (elemClassName.indexOf('highcharts-container') !== -1) {
  26440. return false;
  26441. }
  26442. }
  26443. elem = elem.parentElement;
  26444. }
  26445. }
  26446. /**
  26447. * Initialize the Pointer.
  26448. *
  26449. * @private
  26450. * @function Highcharts.Pointer#init
  26451. *
  26452. * @param {Highcharts.Chart} chart
  26453. * The Chart instance.
  26454. *
  26455. * @param {Highcharts.Options} options
  26456. * The root options object. The pointer uses options from the chart and
  26457. * tooltip structures.
  26458. */
  26459. init(chart, options) {
  26460. // Store references
  26461. this.options = options;
  26462. this.chart = chart;
  26463. // Do we need to handle click on a touch device?
  26464. this.runChartClick = Boolean(options.chart.events && options.chart.events.click);
  26465. this.pinchDown = [];
  26466. this.lastValidTouch = {};
  26467. this.setDOMEvents();
  26468. fireEvent(this, 'afterInit');
  26469. }
  26470. /**
  26471. * Takes a browser event object and extends it with custom Highcharts
  26472. * properties `chartX` and `chartY` in order to work on the internal
  26473. * coordinate system.
  26474. *
  26475. * On map charts, the properties `lon` and `lat` are added to the event
  26476. * object given that the chart has projection information.
  26477. *
  26478. * @function Highcharts.Pointer#normalize
  26479. *
  26480. * @param {global.MouseEvent|global.PointerEvent|global.TouchEvent} e
  26481. * Event object in standard browsers.
  26482. *
  26483. * @param {Highcharts.OffsetObject} [chartPosition]
  26484. * Additional chart offset.
  26485. *
  26486. * @return {Highcharts.PointerEventObject}
  26487. * A browser event with extended properties `chartX` and `chartY`.
  26488. */
  26489. normalize(e, chartPosition) {
  26490. const touches = e.touches;
  26491. // iOS (#2757)
  26492. const ePos = (touches ?
  26493. touches.length ?
  26494. touches.item(0) :
  26495. (pick(// #13534
  26496. touches.changedTouches, e.changedTouches))[0] :
  26497. e);
  26498. // Get mouse position
  26499. if (!chartPosition) {
  26500. chartPosition = this.getChartPosition();
  26501. }
  26502. let chartX = ePos.pageX - chartPosition.left, chartY = ePos.pageY - chartPosition.top;
  26503. // #11329 - when there is scaling on a parent element, we need to take
  26504. // this into account
  26505. chartX /= chartPosition.scaleX;
  26506. chartY /= chartPosition.scaleY;
  26507. return extend(e, {
  26508. chartX: Math.round(chartX),
  26509. chartY: Math.round(chartY)
  26510. });
  26511. }
  26512. /**
  26513. * @private
  26514. * @function Highcharts.Pointer#onContainerClick
  26515. */
  26516. onContainerClick(e) {
  26517. const chart = this.chart;
  26518. const hoverPoint = chart.hoverPoint;
  26519. const pEvt = this.normalize(e);
  26520. const plotLeft = chart.plotLeft;
  26521. const plotTop = chart.plotTop;
  26522. if (!chart.cancelClick) {
  26523. // On tracker click, fire the series and point events. #783, #1583
  26524. if (hoverPoint &&
  26525. this.inClass(pEvt.target, 'highcharts-tracker')) {
  26526. // the series click event
  26527. fireEvent(hoverPoint.series, 'click', extend(pEvt, {
  26528. point: hoverPoint
  26529. }));
  26530. // the point click event
  26531. if (chart.hoverPoint) { // it may be destroyed (#1844)
  26532. hoverPoint.firePointEvent('click', pEvt);
  26533. }
  26534. // When clicking outside a tracker, fire a chart event
  26535. }
  26536. else {
  26537. extend(pEvt, this.getCoordinates(pEvt));
  26538. // fire a click event in the chart
  26539. if (chart.isInsidePlot(pEvt.chartX - plotLeft, pEvt.chartY - plotTop, {
  26540. visiblePlotOnly: true
  26541. })) {
  26542. fireEvent(chart, 'click', pEvt);
  26543. }
  26544. }
  26545. }
  26546. }
  26547. /**
  26548. * @private
  26549. * @function Highcharts.Pointer#onContainerMouseDown
  26550. */
  26551. onContainerMouseDown(e) {
  26552. const isPrimaryButton = ((e.buttons || e.button) & 1) === 1;
  26553. e = this.normalize(e);
  26554. // #11635, Firefox does not reliably fire move event after click scroll
  26555. if (H.isFirefox &&
  26556. e.button !== 0) {
  26557. this.onContainerMouseMove(e);
  26558. }
  26559. // #11635, limiting to primary button
  26560. if (typeof e.button === 'undefined' ||
  26561. isPrimaryButton) {
  26562. this.zoomOption(e);
  26563. // #295, #13737 solve conflict between container drag and chart zoom
  26564. if (isPrimaryButton &&
  26565. e.preventDefault) {
  26566. e.preventDefault();
  26567. }
  26568. this.dragStart(e);
  26569. }
  26570. }
  26571. /**
  26572. * When mouse leaves the container, hide the tooltip.
  26573. * @private
  26574. * @function Highcharts.Pointer#onContainerMouseLeave
  26575. */
  26576. onContainerMouseLeave(e) {
  26577. const chart = charts[pick(Pointer.hoverChartIndex, -1)];
  26578. e = this.normalize(e);
  26579. // #4886, MS Touch end fires mouseleave but with no related target
  26580. if (chart &&
  26581. e.relatedTarget &&
  26582. !this.inClass(e.relatedTarget, 'highcharts-tooltip')) {
  26583. chart.pointer.reset();
  26584. // Also reset the chart position, used in #149 fix
  26585. chart.pointer.chartPosition = void 0;
  26586. }
  26587. }
  26588. /**
  26589. * When mouse enters the container, delete pointer's chartPosition.
  26590. * @private
  26591. * @function Highcharts.Pointer#onContainerMouseEnter
  26592. */
  26593. onContainerMouseEnter(e) {
  26594. delete this.chartPosition;
  26595. }
  26596. /**
  26597. * The mousemove, touchmove and touchstart event handler
  26598. * @private
  26599. * @function Highcharts.Pointer#onContainerMouseMove
  26600. */
  26601. onContainerMouseMove(e) {
  26602. const chart = this.chart, tooltip = chart.tooltip, pEvt = this.normalize(e);
  26603. this.setHoverChartIndex();
  26604. if (chart.mouseIsDown === 'mousedown' || this.touchSelect(pEvt)) {
  26605. this.drag(pEvt);
  26606. }
  26607. // Show the tooltip and run mouse over events (#977)
  26608. if (!chart.openMenu &&
  26609. (this.inClass(pEvt.target, 'highcharts-tracker') ||
  26610. chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
  26611. visiblePlotOnly: true
  26612. })) &&
  26613. // If the tooltip has stickOnContact enabled, do nothing. This
  26614. // applies regardless of any combinations of the `split` and
  26615. // `useHTML` options.
  26616. !(tooltip &&
  26617. tooltip.shouldStickOnContact(pEvt))) {
  26618. if (this.inClass(pEvt.target, 'highcharts-no-tooltip')) {
  26619. this.reset(false, 0);
  26620. }
  26621. else {
  26622. this.runPointActions(pEvt);
  26623. }
  26624. }
  26625. }
  26626. /**
  26627. * @private
  26628. * @function Highcharts.Pointer#onDocumentTouchEnd
  26629. */
  26630. onDocumentTouchEnd(e) {
  26631. const hoverChart = charts[pick(Pointer.hoverChartIndex, -1)];
  26632. if (hoverChart) {
  26633. hoverChart.pointer.drop(e);
  26634. }
  26635. }
  26636. /**
  26637. * @private
  26638. * @function Highcharts.Pointer#onContainerTouchMove
  26639. */
  26640. onContainerTouchMove(e) {
  26641. if (this.touchSelect(e)) {
  26642. this.onContainerMouseMove(e);
  26643. }
  26644. else {
  26645. this.touch(e);
  26646. }
  26647. }
  26648. /**
  26649. * @private
  26650. * @function Highcharts.Pointer#onContainerTouchStart
  26651. */
  26652. onContainerTouchStart(e) {
  26653. if (this.touchSelect(e)) {
  26654. this.onContainerMouseDown(e);
  26655. }
  26656. else {
  26657. this.zoomOption(e);
  26658. this.touch(e, true);
  26659. }
  26660. }
  26661. /**
  26662. * Special handler for mouse move that will hide the tooltip when the mouse
  26663. * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
  26664. * always fire.
  26665. * @private
  26666. * @function Highcharts.Pointer#onDocumentMouseMove
  26667. */
  26668. onDocumentMouseMove(e) {
  26669. const chart = this.chart;
  26670. const tooltip = chart.tooltip;
  26671. const chartPosition = this.chartPosition;
  26672. const pEvt = this.normalize(e, chartPosition);
  26673. // If we're outside, hide the tooltip
  26674. if (chartPosition &&
  26675. !chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
  26676. visiblePlotOnly: true
  26677. }) &&
  26678. !(tooltip &&
  26679. tooltip.shouldStickOnContact(pEvt)) &&
  26680. !this.inClass(pEvt.target, 'highcharts-tracker')) {
  26681. this.reset();
  26682. }
  26683. }
  26684. /**
  26685. * @private
  26686. * @function Highcharts.Pointer#onDocumentMouseUp
  26687. */
  26688. onDocumentMouseUp(e) {
  26689. const chart = charts[pick(Pointer.hoverChartIndex, -1)];
  26690. if (chart) {
  26691. chart.pointer.drop(e);
  26692. }
  26693. }
  26694. /**
  26695. * Handle touch events with two touches
  26696. * @private
  26697. * @function Highcharts.Pointer#pinch
  26698. */
  26699. pinch(e) {
  26700. const self = this, chart = self.chart, pinchDown = self.pinchDown, touches = (e.touches || []), touchesLength = touches.length, lastValidTouch = self.lastValidTouch, hasZoom = self.hasZoom, transform = {}, fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, 'highcharts-tracker') &&
  26701. chart.runTrackerClick) ||
  26702. self.runChartClick), clip = {}, tooltip = self.chart.tooltip, followTouchMove = touchesLength === 1 &&
  26703. pick((tooltip && tooltip.options.followTouchMove), true);
  26704. let selectionMarker = self.selectionMarker;
  26705. // Don't initiate panning until the user has pinched. This prevents us
  26706. // from blocking page scrolling as users scroll down a long page
  26707. // (#4210).
  26708. if (touchesLength > 1) {
  26709. self.initiated = true;
  26710. }
  26711. else if (followTouchMove) {
  26712. // #16119: Prevent blocking scroll when single-finger panning is
  26713. // not enabled
  26714. self.initiated = false;
  26715. }
  26716. // On touch devices, only proceed to trigger click if a handler is
  26717. // defined
  26718. if (hasZoom &&
  26719. self.initiated &&
  26720. !fireClickEvent &&
  26721. e.cancelable !== false) {
  26722. e.preventDefault();
  26723. }
  26724. // Normalize each touch
  26725. [].map.call(touches, function (e) {
  26726. return self.normalize(e);
  26727. });
  26728. // Register the touch start position
  26729. if (e.type === 'touchstart') {
  26730. [].forEach.call(touches, function (e, i) {
  26731. pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
  26732. });
  26733. lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
  26734. pinchDown[1].chartX];
  26735. lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
  26736. pinchDown[1].chartY];
  26737. // Identify the data bounds in pixels
  26738. chart.axes.forEach(function (axis) {
  26739. if (axis.zoomEnabled) {
  26740. const bounds = chart.bounds[axis.horiz ? 'h' : 'v'], minPixelPadding = axis.minPixelPadding, min = axis.toPixels(Math.min(pick(axis.options.min, axis.dataMin), axis.dataMin)), max = axis.toPixels(Math.max(pick(axis.options.max, axis.dataMax), axis.dataMax)), absMin = Math.min(min, max), absMax = Math.max(min, max);
  26741. // Store the bounds for use in the touchmove handler
  26742. bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
  26743. bounds.max = Math.max(axis.pos + axis.len, absMax + minPixelPadding);
  26744. }
  26745. });
  26746. self.res = true; // reset on next move
  26747. // Optionally move the tooltip on touchmove
  26748. }
  26749. else if (followTouchMove) {
  26750. this.runPointActions(self.normalize(e));
  26751. // Event type is touchmove, handle panning and pinching
  26752. }
  26753. else if (pinchDown.length) { // can be 0 when releasing, if touchend
  26754. // fires first
  26755. fireEvent(chart, 'touchpan', { originalEvent: e }, () => {
  26756. // Set the marker
  26757. if (!selectionMarker) {
  26758. // @todo It's a mock object, so maybe we need a separate
  26759. // interface
  26760. self.selectionMarker = selectionMarker = extend({
  26761. destroy: noop,
  26762. touch: true
  26763. }, chart.plotBox);
  26764. }
  26765. self.pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26766. self.hasPinched = hasZoom;
  26767. // Scale and translate the groups to provide visual feedback
  26768. // during pinching
  26769. self.scaleGroups(transform, clip);
  26770. });
  26771. if (self.res) {
  26772. self.res = false;
  26773. this.reset(false, 0);
  26774. }
  26775. }
  26776. }
  26777. /**
  26778. * Run translation operations
  26779. * @private
  26780. * @function Highcharts.Pointer#pinchTranslate
  26781. */
  26782. pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
  26783. if (this.zoomHor) {
  26784. this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26785. }
  26786. if (this.zoomVert) {
  26787. this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26788. }
  26789. }
  26790. /**
  26791. * Run translation operations for each direction (horizontal and vertical)
  26792. * independently.
  26793. * @private
  26794. * @function Highcharts.Pointer#pinchTranslateDirection
  26795. */
  26796. pinchTranslateDirection(horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) {
  26797. const chart = this.chart, xy = horiz ? 'x' : 'y', XY = horiz ? 'X' : 'Y', sChartXY = ('chart' + XY), wh = horiz ? 'width' : 'height', plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')], inverted = chart.inverted, bounds = chart.bounds[horiz ? 'h' : 'v'], singleTouch = pinchDown.length === 1, touch0Start = pinchDown[0][sChartXY], touch1Start = !singleTouch && pinchDown[1][sChartXY], setScale = function () {
  26798. // Don't zoom if fingers are too close on this axis
  26799. if (typeof touch1Now === 'number' &&
  26800. Math.abs(touch0Start - touch1Start) > 20) {
  26801. scale = forcedScale ||
  26802. Math.abs(touch0Now - touch1Now) /
  26803. Math.abs(touch0Start - touch1Start);
  26804. }
  26805. clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
  26806. selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
  26807. };
  26808. let selectionWH, selectionXY, clipXY, scale = forcedScale || 1, touch0Now = touches[0][sChartXY], touch1Now = !singleTouch && touches[1][sChartXY], outOfBounds;
  26809. // Set the scale, first pass
  26810. setScale();
  26811. // The clip position (x or y) is altered if out of bounds, the selection
  26812. // position is not
  26813. selectionXY = clipXY;
  26814. // Out of bounds
  26815. if (selectionXY < bounds.min) {
  26816. selectionXY = bounds.min;
  26817. outOfBounds = true;
  26818. }
  26819. else if (selectionXY + selectionWH > bounds.max) {
  26820. selectionXY = bounds.max - selectionWH;
  26821. outOfBounds = true;
  26822. }
  26823. // Is the chart dragged off its bounds, determined by dataMin and
  26824. // dataMax?
  26825. if (outOfBounds) {
  26826. // Modify the touchNow position in order to create an elastic drag
  26827. // movement. This indicates to the user that the chart is responsive
  26828. // but can't be dragged further.
  26829. touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
  26830. if (typeof touch1Now === 'number') {
  26831. touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
  26832. }
  26833. // Set the scale, second pass to adapt to the modified touchNow
  26834. // positions
  26835. setScale();
  26836. }
  26837. else {
  26838. lastValidTouch[xy] = [touch0Now, touch1Now];
  26839. }
  26840. // Set geometry for clipping, selection and transformation
  26841. if (!inverted) {
  26842. clip[xy] = clipXY - plotLeftTop;
  26843. clip[wh] = selectionWH;
  26844. }
  26845. const scaleKey = inverted ?
  26846. (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
  26847. const transformScale = inverted ? 1 / scale : scale;
  26848. selectionMarker[wh] = selectionWH;
  26849. selectionMarker[xy] = selectionXY;
  26850. transform[scaleKey] = scale;
  26851. transform['translate' + XY] = (transformScale * plotLeftTop) +
  26852. (touch0Now - (transformScale * touch0Start));
  26853. }
  26854. /**
  26855. * Reset the tracking by hiding the tooltip, the hover series state and the
  26856. * hover point
  26857. *
  26858. * @function Highcharts.Pointer#reset
  26859. *
  26860. * @param {boolean} [allowMove]
  26861. * Instead of destroying the tooltip altogether, allow moving it if
  26862. * possible.
  26863. *
  26864. * @param {number} [delay]
  26865. */
  26866. reset(allowMove, delay) {
  26867. const pointer = this, chart = pointer.chart, hoverSeries = chart.hoverSeries, hoverPoint = chart.hoverPoint, hoverPoints = chart.hoverPoints, tooltip = chart.tooltip, tooltipPoints = tooltip && tooltip.shared ?
  26868. hoverPoints :
  26869. hoverPoint;
  26870. // Check if the points have moved outside the plot area (#1003, #4736,
  26871. // #5101)
  26872. if (allowMove && tooltipPoints) {
  26873. splat(tooltipPoints).forEach(function (point) {
  26874. if (point.series.isCartesian &&
  26875. typeof point.plotX === 'undefined') {
  26876. allowMove = false;
  26877. }
  26878. });
  26879. }
  26880. // Just move the tooltip, #349
  26881. if (allowMove) {
  26882. if (tooltip && tooltipPoints && splat(tooltipPoints).length) {
  26883. tooltip.refresh(tooltipPoints);
  26884. if (tooltip.shared && hoverPoints) { // #8284
  26885. hoverPoints.forEach(function (point) {
  26886. point.setState(point.state, true);
  26887. if (point.series.isCartesian) {
  26888. if (point.series.xAxis.crosshair) {
  26889. point.series.xAxis
  26890. .drawCrosshair(null, point);
  26891. }
  26892. if (point.series.yAxis.crosshair) {
  26893. point.series.yAxis
  26894. .drawCrosshair(null, point);
  26895. }
  26896. }
  26897. });
  26898. }
  26899. else if (hoverPoint) { // #2500
  26900. hoverPoint.setState(hoverPoint.state, true);
  26901. chart.axes.forEach(function (axis) {
  26902. if (axis.crosshair &&
  26903. hoverPoint.series[axis.coll] === axis) {
  26904. axis.drawCrosshair(null, hoverPoint);
  26905. }
  26906. });
  26907. }
  26908. }
  26909. // Full reset
  26910. }
  26911. else {
  26912. if (hoverPoint) {
  26913. hoverPoint.onMouseOut();
  26914. }
  26915. if (hoverPoints) {
  26916. hoverPoints.forEach(function (point) {
  26917. point.setState();
  26918. });
  26919. }
  26920. if (hoverSeries) {
  26921. hoverSeries.onMouseOut();
  26922. }
  26923. if (tooltip) {
  26924. tooltip.hide(delay);
  26925. }
  26926. if (pointer.unDocMouseMove) {
  26927. pointer.unDocMouseMove = pointer.unDocMouseMove();
  26928. }
  26929. // Remove crosshairs
  26930. chart.axes.forEach(function (axis) {
  26931. axis.hideCrosshair();
  26932. });
  26933. pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
  26934. }
  26935. }
  26936. /**
  26937. * With line type charts with a single tracker, get the point closest to the
  26938. * mouse. Run Point.onMouseOver and display tooltip for the point or points.
  26939. *
  26940. * @private
  26941. * @function Highcharts.Pointer#runPointActions
  26942. *
  26943. * @emits Highcharts.Point#event:mouseOut
  26944. * @emits Highcharts.Point#event:mouseOver
  26945. */
  26946. runPointActions(e, p, force) {
  26947. const pointer = this, chart = pointer.chart, series = chart.series, tooltip = (chart.tooltip && chart.tooltip.options.enabled ?
  26948. chart.tooltip :
  26949. void 0), shared = (tooltip ?
  26950. tooltip.shared :
  26951. false);
  26952. let hoverPoint = p || chart.hoverPoint, hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries;
  26953. const // onMouseOver or already hovering a series with directTouch
  26954. isDirectTouch = (!e || e.type !== 'touchmove') && (!!p || ((hoverSeries && hoverSeries.directTouch) &&
  26955. pointer.isDirectTouch)), hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e);
  26956. // Update variables from hoverData.
  26957. hoverPoint = hoverData.hoverPoint;
  26958. hoverSeries = hoverData.hoverSeries;
  26959. const points = hoverData.hoverPoints, followPointer = hoverSeries &&
  26960. hoverSeries.tooltipOptions.followPointer &&
  26961. !hoverSeries.tooltipOptions.split, useSharedTooltip = (shared &&
  26962. hoverSeries &&
  26963. !hoverSeries.noSharedTooltip);
  26964. // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
  26965. // #3926, #4200
  26966. if (hoverPoint &&
  26967. (force ||
  26968. hoverPoint !== chart.hoverPoint ||
  26969. (tooltip && tooltip.isHidden))) {
  26970. (chart.hoverPoints || []).forEach(function (p) {
  26971. if (points.indexOf(p) === -1) {
  26972. p.setState();
  26973. }
  26974. });
  26975. // Set normal state to previous series
  26976. if (chart.hoverSeries !== hoverSeries) {
  26977. hoverSeries.onMouseOver();
  26978. }
  26979. pointer.applyInactiveState(points);
  26980. // Do mouseover on all points (#3919, #3985, #4410, #5622)
  26981. (points || []).forEach(function (p) {
  26982. p.setState('hover');
  26983. });
  26984. // If tracking is on series in stead of on each point,
  26985. // fire mouseOver on hover point. // #4448
  26986. if (chart.hoverPoint) {
  26987. chart.hoverPoint.firePointEvent('mouseOut');
  26988. }
  26989. // Hover point may have been destroyed in the event handlers (#7127)
  26990. if (!hoverPoint.series) {
  26991. return;
  26992. }
  26993. /**
  26994. * Contains all hovered points.
  26995. *
  26996. * @name Highcharts.Chart#hoverPoints
  26997. * @type {Array<Highcharts.Point>|null}
  26998. */
  26999. chart.hoverPoints = points;
  27000. /**
  27001. * Contains the original hovered point.
  27002. *
  27003. * @name Highcharts.Chart#hoverPoint
  27004. * @type {Highcharts.Point|null}
  27005. */
  27006. chart.hoverPoint = hoverPoint;
  27007. /**
  27008. * Hover state should not be lost when axis is updated (#12569)
  27009. * Axis.update runs pointer.reset which uses chart.hoverPoint.state
  27010. * to apply state which does not exist in hoverPoint yet.
  27011. * The mouseOver event should be triggered when hoverPoint
  27012. * is correct.
  27013. */
  27014. hoverPoint.firePointEvent('mouseOver', void 0, () => {
  27015. // Draw tooltip if necessary
  27016. if (tooltip && hoverPoint) {
  27017. tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
  27018. }
  27019. });
  27020. // Update positions (regardless of kdpoint or hoverPoint)
  27021. }
  27022. else if (followPointer && tooltip && !tooltip.isHidden) {
  27023. const anchor = tooltip.getAnchor([{}], e);
  27024. if (chart.isInsidePlot(anchor[0], anchor[1], {
  27025. visiblePlotOnly: true
  27026. })) {
  27027. tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
  27028. }
  27029. }
  27030. // Start the event listener to pick up the tooltip and crosshairs
  27031. if (!pointer.unDocMouseMove) {
  27032. pointer.unDocMouseMove = addEvent(chart.container.ownerDocument, 'mousemove', function (e) {
  27033. const chart = charts[Pointer.hoverChartIndex];
  27034. if (chart) {
  27035. chart.pointer.onDocumentMouseMove(e);
  27036. }
  27037. });
  27038. pointer.eventsToUnbind.push(pointer.unDocMouseMove);
  27039. }
  27040. // Issues related to crosshair #4927, #5269 #5066, #5658
  27041. chart.axes.forEach(function drawAxisCrosshair(axis) {
  27042. const snap = pick((axis.crosshair || {}).snap, true);
  27043. let point;
  27044. if (snap) {
  27045. point = chart.hoverPoint; // #13002
  27046. if (!point || point.series[axis.coll] !== axis) {
  27047. point = find(points, (p) => p.series && p.series[axis.coll] === axis);
  27048. }
  27049. }
  27050. // Axis has snapping crosshairs, and one of the hover points belongs
  27051. // to axis. Always call drawCrosshair when it is not snap.
  27052. if (point || !snap) {
  27053. axis.drawCrosshair(e, point);
  27054. // Axis has snapping crosshairs, but no hover point belongs to axis
  27055. }
  27056. else {
  27057. axis.hideCrosshair();
  27058. }
  27059. });
  27060. }
  27061. /**
  27062. * Scale series groups to a certain scale and translation.
  27063. * @private
  27064. * @function Highcharts.Pointer#scaleGroups
  27065. */
  27066. scaleGroups(attribs, clip) {
  27067. const chart = this.chart;
  27068. // Scale each series
  27069. chart.series.forEach(function (series) {
  27070. const seriesAttribs = attribs || series.getPlotBox(); // #1701
  27071. if (series.group &&
  27072. ((series.xAxis && series.xAxis.zoomEnabled) ||
  27073. chart.mapView)) {
  27074. series.group.attr(seriesAttribs);
  27075. if (series.markerGroup) {
  27076. series.markerGroup.attr(seriesAttribs);
  27077. series.markerGroup.clip(clip ? chart.clipRect : null);
  27078. }
  27079. if (series.dataLabelsGroup) {
  27080. series.dataLabelsGroup.attr(seriesAttribs);
  27081. }
  27082. }
  27083. });
  27084. // Clip
  27085. chart.clipRect.attr(clip || chart.clipBox);
  27086. }
  27087. /**
  27088. * Set the JS DOM events on the container and document. This method should
  27089. * contain a one-to-one assignment between methods and their handlers. Any
  27090. * advanced logic should be moved to the handler reflecting the event's
  27091. * name.
  27092. * @private
  27093. * @function Highcharts.Pointer#setDOMEvents
  27094. */
  27095. setDOMEvents() {
  27096. const container = this.chart.container, ownerDoc = container.ownerDocument;
  27097. container.onmousedown = this.onContainerMouseDown.bind(this);
  27098. container.onmousemove = this.onContainerMouseMove.bind(this);
  27099. container.onclick = this.onContainerClick.bind(this);
  27100. this.eventsToUnbind.push(addEvent(container, 'mouseenter', this.onContainerMouseEnter.bind(this)));
  27101. this.eventsToUnbind.push(addEvent(container, 'mouseleave', this.onContainerMouseLeave.bind(this)));
  27102. if (!Pointer.unbindDocumentMouseUp) {
  27103. Pointer.unbindDocumentMouseUp = addEvent(ownerDoc, 'mouseup', this.onDocumentMouseUp.bind(this));
  27104. }
  27105. // In case we are dealing with overflow, reset the chart position when
  27106. // scrolling parent elements
  27107. let parent = this.chart.renderTo.parentElement;
  27108. while (parent && parent.tagName !== 'BODY') {
  27109. this.eventsToUnbind.push(addEvent(parent, 'scroll', () => {
  27110. delete this.chartPosition;
  27111. }));
  27112. parent = parent.parentElement;
  27113. }
  27114. if (H.hasTouch) {
  27115. this.eventsToUnbind.push(addEvent(container, 'touchstart', this.onContainerTouchStart.bind(this), { passive: false }));
  27116. this.eventsToUnbind.push(addEvent(container, 'touchmove', this.onContainerTouchMove.bind(this), { passive: false }));
  27117. if (!Pointer.unbindDocumentTouchEnd) {
  27118. Pointer.unbindDocumentTouchEnd = addEvent(ownerDoc, 'touchend', this.onDocumentTouchEnd.bind(this), { passive: false });
  27119. }
  27120. }
  27121. }
  27122. /**
  27123. * Sets the index of the hovered chart and leaves the previous hovered
  27124. * chart, to reset states like tooltip.
  27125. * @private
  27126. * @function Highcharts.Pointer#setHoverChartIndex
  27127. */
  27128. setHoverChartIndex() {
  27129. const chart = this.chart;
  27130. const hoverChart = H.charts[pick(Pointer.hoverChartIndex, -1)];
  27131. if (hoverChart &&
  27132. hoverChart !== chart) {
  27133. hoverChart.pointer.onContainerMouseLeave({ relatedTarget: chart.container });
  27134. }
  27135. if (!hoverChart ||
  27136. !hoverChart.mouseIsDown) {
  27137. Pointer.hoverChartIndex = chart.index;
  27138. }
  27139. }
  27140. /**
  27141. * General touch handler shared by touchstart and touchmove.
  27142. * @private
  27143. * @function Highcharts.Pointer#touch
  27144. */
  27145. touch(e, start) {
  27146. const chart = this.chart;
  27147. let hasMoved, pinchDown, isInside;
  27148. this.setHoverChartIndex();
  27149. if (e.touches.length === 1) {
  27150. e = this.normalize(e);
  27151. isInside = chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop, {
  27152. visiblePlotOnly: true
  27153. });
  27154. if (isInside && !chart.openMenu) {
  27155. // Run mouse events and display tooltip etc
  27156. if (start) {
  27157. this.runPointActions(e);
  27158. }
  27159. // Android fires touchmove events after the touchstart even if
  27160. // the finger hasn't moved, or moved only a pixel or two. In iOS
  27161. // however, the touchmove doesn't fire unless the finger moves
  27162. // more than ~4px. So we emulate this behaviour in Android by
  27163. // checking how much it moved, and cancelling on small
  27164. // distances. #3450.
  27165. if (e.type === 'touchmove') {
  27166. pinchDown = this.pinchDown;
  27167. hasMoved = pinchDown[0] ? Math.sqrt(// #5266
  27168. Math.pow(pinchDown[0].chartX - e.chartX, 2) +
  27169. Math.pow(pinchDown[0].chartY - e.chartY, 2)) >= 4 : false;
  27170. }
  27171. if (pick(hasMoved, true)) {
  27172. this.pinch(e);
  27173. }
  27174. }
  27175. else if (start) {
  27176. // Hide the tooltip on touching outside the plot area (#1203)
  27177. this.reset();
  27178. }
  27179. }
  27180. else if (e.touches.length === 2) {
  27181. this.pinch(e);
  27182. }
  27183. }
  27184. /**
  27185. * Returns true if the chart is set up for zooming by single touch and the
  27186. * event is capable
  27187. * @private
  27188. * @function Highcharts.Pointer#touchSelect
  27189. */
  27190. touchSelect(e) {
  27191. return Boolean(this.chart.zooming.singleTouch &&
  27192. e.touches &&
  27193. e.touches.length === 1);
  27194. }
  27195. /**
  27196. * Resolve the zoomType option, this is reset on all touch start and mouse
  27197. * down events.
  27198. * @private
  27199. * @function Highcharts.Pointer#zoomOption
  27200. */
  27201. zoomOption(e) {
  27202. const chart = this.chart, options = chart.options.chart, inverted = chart.inverted;
  27203. let zoomType = chart.zooming.type || '', zoomX, zoomY;
  27204. // Look for the pinchType option
  27205. if (/touch/.test(e.type)) {
  27206. zoomType = pick(chart.zooming.pinchType, zoomType);
  27207. }
  27208. this.zoomX = zoomX = /x/.test(zoomType);
  27209. this.zoomY = zoomY = /y/.test(zoomType);
  27210. this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
  27211. this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
  27212. this.hasZoom = zoomX || zoomY;
  27213. }
  27214. }
  27215. /* *
  27216. *
  27217. * Class Namespace
  27218. *
  27219. * */
  27220. (function (Pointer) {
  27221. /* *
  27222. *
  27223. * Declarations
  27224. *
  27225. * */
  27226. /* *
  27227. *
  27228. * Constants
  27229. *
  27230. * */
  27231. const composedEvents = [];
  27232. const composedMembers = [];
  27233. /* *
  27234. *
  27235. * Functions
  27236. *
  27237. * */
  27238. /**
  27239. * @private
  27240. */
  27241. function compose(ChartClass) {
  27242. if (U.pushUnique(composedMembers, ChartClass)) {
  27243. addEvent(ChartClass, 'beforeRender', function () {
  27244. /**
  27245. * The Pointer that keeps track of mouse and touch
  27246. * interaction.
  27247. *
  27248. * @memberof Highcharts.Chart
  27249. * @name pointer
  27250. * @type {Highcharts.Pointer}
  27251. * @instance
  27252. */
  27253. this.pointer = new Pointer(this, this.options);
  27254. });
  27255. }
  27256. }
  27257. Pointer.compose = compose;
  27258. /**
  27259. * @private
  27260. */
  27261. function dissolve() {
  27262. for (let i = 0, iEnd = composedEvents.length; i < iEnd; ++i) {
  27263. composedEvents[i]();
  27264. }
  27265. composedEvents.length = 0;
  27266. }
  27267. Pointer.dissolve = dissolve;
  27268. })(Pointer || (Pointer = {}));
  27269. /* *
  27270. *
  27271. * Default Export
  27272. *
  27273. * */
  27274. /* *
  27275. *
  27276. * API Declarations
  27277. *
  27278. * */
  27279. /**
  27280. * Chart position and scale.
  27281. *
  27282. * @interface Highcharts.ChartPositionObject
  27283. */ /**
  27284. * @name Highcharts.ChartPositionObject#left
  27285. * @type {number}
  27286. */ /**
  27287. * @name Highcharts.ChartPositionObject#scaleX
  27288. * @type {number}
  27289. */ /**
  27290. * @name Highcharts.ChartPositionObject#scaleY
  27291. * @type {number}
  27292. */ /**
  27293. * @name Highcharts.ChartPositionObject#top
  27294. * @type {number}
  27295. */
  27296. /**
  27297. * One position in relation to an axis.
  27298. *
  27299. * @interface Highcharts.PointerAxisCoordinateObject
  27300. */ /**
  27301. * Related axis.
  27302. *
  27303. * @name Highcharts.PointerAxisCoordinateObject#axis
  27304. * @type {Highcharts.Axis}
  27305. */ /**
  27306. * Axis value.
  27307. *
  27308. * @name Highcharts.PointerAxisCoordinateObject#value
  27309. * @type {number}
  27310. */
  27311. /**
  27312. * Positions in terms of axis values.
  27313. *
  27314. * @interface Highcharts.PointerAxisCoordinatesObject
  27315. */ /**
  27316. * Positions on the x-axis.
  27317. * @name Highcharts.PointerAxisCoordinatesObject#xAxis
  27318. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  27319. */ /**
  27320. * Positions on the y-axis.
  27321. * @name Highcharts.PointerAxisCoordinatesObject#yAxis
  27322. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  27323. */
  27324. /**
  27325. * Pointer coordinates.
  27326. *
  27327. * @interface Highcharts.PointerCoordinatesObject
  27328. */ /**
  27329. * @name Highcharts.PointerCoordinatesObject#chartX
  27330. * @type {number}
  27331. */ /**
  27332. * @name Highcharts.PointerCoordinatesObject#chartY
  27333. * @type {number}
  27334. */
  27335. /**
  27336. * A native browser mouse or touch event, extended with position information
  27337. * relative to the {@link Chart.container}.
  27338. *
  27339. * @interface Highcharts.PointerEventObject
  27340. * @extends global.PointerEvent
  27341. */ /**
  27342. * The X coordinate of the pointer interaction relative to the chart.
  27343. *
  27344. * @name Highcharts.PointerEventObject#chartX
  27345. * @type {number}
  27346. */ /**
  27347. * The Y coordinate of the pointer interaction relative to the chart.
  27348. *
  27349. * @name Highcharts.PointerEventObject#chartY
  27350. * @type {number}
  27351. */
  27352. /**
  27353. * Axis-specific data of a selection.
  27354. *
  27355. * @interface Highcharts.SelectDataObject
  27356. */ /**
  27357. * The selected Axis.
  27358. * @name Highcharts.SelectDataObject#axis
  27359. * @type {Highcharts.Axis}
  27360. */ /**
  27361. * The maximum axis value, either automatic or set manually.
  27362. * @name Highcharts.SelectDataObject#max
  27363. * @type {number}
  27364. */ /**
  27365. * The minimum axis value, either automatic or set manually.
  27366. * @name Highcharts.SelectDataObject#min
  27367. * @type {number}
  27368. */
  27369. /**
  27370. * Object for select events.
  27371. * The primary axes are `xAxis[0]` and `yAxis[0]`. Remember the unit of a
  27372. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  27373. *
  27374. * @interface Highcharts.SelectEventObject
  27375. */ /**
  27376. * The related browser event.
  27377. * @name Highcharts.SelectEventObject#originalEvent
  27378. * @type {global.Event}
  27379. */ /**
  27380. * Indicates a reset event to restore default state.
  27381. * @name Highcharts.SelectEventObject#resetSelection
  27382. * @type {boolean|undefined}
  27383. */ /**
  27384. * Arrays containing the axes of each dimension and each axis' min and max
  27385. * values.
  27386. * @name Highcharts.SelectEventObject#xAxis
  27387. * @type {Array<Highcharts.SelectDataObject>}
  27388. */ /**
  27389. * Arrays containing the axes of each dimension and each axis' min and max
  27390. * values.
  27391. * @name Highcharts.SelectEventObject#yAxis
  27392. * @type {Array<Highcharts.SelectDataObject>}
  27393. */
  27394. ''; // keeps doclets above in JS file
  27395. return Pointer;
  27396. });
  27397. _registerModule(_modules, 'Core/Legend/Legend.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Templating.js'], _modules['Core/Globals.js'], _modules['Core/Series/Point.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Utilities.js']], function (A, F, H, Point, R, U) {
  27398. /* *
  27399. *
  27400. * (c) 2010-2021 Torstein Honsi
  27401. *
  27402. * License: www.highcharts.com/license
  27403. *
  27404. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  27405. *
  27406. * */
  27407. const { animObject, setAnimation } = A;
  27408. const { format } = F;
  27409. const { marginNames } = H;
  27410. const { distribute } = R;
  27411. const { addEvent, createElement, css, defined, discardElement, find, fireEvent, isNumber, merge, pick, relativeLength, stableSort, syncTimeout } = U;
  27412. /* *
  27413. *
  27414. * Class
  27415. *
  27416. * */
  27417. /**
  27418. * The overview of the chart's series. The legend object is instanciated
  27419. * internally in the chart constructor, and is available from the `chart.legend`
  27420. * property. Each chart has only one legend.
  27421. *
  27422. * @class
  27423. * @name Highcharts.Legend
  27424. *
  27425. * @param {Highcharts.Chart} chart
  27426. * The chart instance.
  27427. *
  27428. * @param {Highcharts.LegendOptions} options
  27429. * Legend options.
  27430. */
  27431. class Legend {
  27432. /* *
  27433. *
  27434. * Constructors
  27435. *
  27436. * */
  27437. constructor(chart, options) {
  27438. /* *
  27439. *
  27440. * Properties
  27441. *
  27442. * */
  27443. this.allItems = [];
  27444. this.box = void 0;
  27445. this.contentGroup = void 0;
  27446. this.display = false;
  27447. this.group = void 0;
  27448. this.initialItemY = 0;
  27449. this.itemHeight = 0;
  27450. this.itemMarginBottom = 0;
  27451. this.itemMarginTop = 0;
  27452. this.itemX = 0;
  27453. this.itemY = 0;
  27454. this.lastItemY = 0;
  27455. this.lastLineHeight = 0;
  27456. this.legendHeight = 0;
  27457. this.legendWidth = 0;
  27458. this.maxItemWidth = 0;
  27459. this.maxLegendWidth = 0;
  27460. this.offsetWidth = 0;
  27461. this.options = void 0;
  27462. this.padding = 0;
  27463. this.pages = [];
  27464. this.proximate = false;
  27465. this.scrollGroup = void 0;
  27466. this.symbolHeight = 0;
  27467. this.symbolWidth = 0;
  27468. this.titleHeight = 0;
  27469. this.totalItemWidth = 0;
  27470. this.widthOption = 0;
  27471. this.chart = chart;
  27472. this.init(chart, options);
  27473. }
  27474. /* *
  27475. *
  27476. * Functions
  27477. *
  27478. * */
  27479. /**
  27480. * Initialize the legend.
  27481. *
  27482. * @private
  27483. * @function Highcharts.Legend#init
  27484. *
  27485. * @param {Highcharts.Chart} chart
  27486. * The chart instance.
  27487. *
  27488. * @param {Highcharts.LegendOptions} options
  27489. * Legend options.
  27490. */
  27491. init(chart, options) {
  27492. /**
  27493. * Chart of this legend.
  27494. *
  27495. * @readonly
  27496. * @name Highcharts.Legend#chart
  27497. * @type {Highcharts.Chart}
  27498. */
  27499. this.chart = chart;
  27500. this.setOptions(options);
  27501. if (options.enabled) {
  27502. // Render it
  27503. this.render();
  27504. // move checkboxes
  27505. addEvent(this.chart, 'endResize', function () {
  27506. this.legend.positionCheckboxes();
  27507. });
  27508. // On Legend.init and Legend.update, make sure that proximate layout
  27509. // events are either added or removed (#18362).
  27510. addEvent(this.chart, 'render', () => {
  27511. if (this.proximate) {
  27512. this.proximatePositions();
  27513. this.positionItems();
  27514. }
  27515. });
  27516. }
  27517. }
  27518. /**
  27519. * @private
  27520. * @function Highcharts.Legend#setOptions
  27521. * @param {Highcharts.LegendOptions} options
  27522. */
  27523. setOptions(options) {
  27524. const padding = pick(options.padding, 8);
  27525. /**
  27526. * Legend options.
  27527. *
  27528. * @readonly
  27529. * @name Highcharts.Legend#options
  27530. * @type {Highcharts.LegendOptions}
  27531. */
  27532. this.options = options;
  27533. if (!this.chart.styledMode) {
  27534. this.itemStyle = options.itemStyle;
  27535. this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle);
  27536. }
  27537. this.itemMarginTop = options.itemMarginTop;
  27538. this.itemMarginBottom = options.itemMarginBottom;
  27539. this.padding = padding;
  27540. this.initialItemY = padding - 5; // 5 is pixels above the text
  27541. this.symbolWidth = pick(options.symbolWidth, 16);
  27542. this.pages = [];
  27543. this.proximate = options.layout === 'proximate' && !this.chart.inverted;
  27544. // #12705: baseline has to be reset on every update
  27545. this.baseline = void 0;
  27546. }
  27547. /**
  27548. * Update the legend with new options. Equivalent to running `chart.update`
  27549. * with a legend configuration option.
  27550. *
  27551. * @sample highcharts/legend/legend-update/
  27552. * Legend update
  27553. *
  27554. * @function Highcharts.Legend#update
  27555. *
  27556. * @param {Highcharts.LegendOptions} options
  27557. * Legend options.
  27558. *
  27559. * @param {boolean} [redraw=true]
  27560. * Whether to redraw the chart after the axis is altered. If doing more
  27561. * operations on the chart, it is a good idea to set redraw to false and
  27562. * call {@link Chart#redraw} after. Whether to redraw the chart.
  27563. *
  27564. * @emits Highcharts.Legends#event:afterUpdate
  27565. */
  27566. update(options, redraw) {
  27567. const chart = this.chart;
  27568. this.setOptions(merge(true, this.options, options));
  27569. this.destroy();
  27570. chart.isDirtyLegend = chart.isDirtyBox = true;
  27571. if (pick(redraw, true)) {
  27572. chart.redraw();
  27573. }
  27574. fireEvent(this, 'afterUpdate');
  27575. }
  27576. /**
  27577. * Set the colors for the legend item.
  27578. *
  27579. * @private
  27580. * @function Highcharts.Legend#colorizeItem
  27581. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27582. * A Series or Point instance
  27583. * @param {boolean} [visible=false]
  27584. * Dimmed or colored
  27585. *
  27586. * @todo
  27587. * Make events official: Fires the event `afterColorizeItem`.
  27588. */
  27589. colorizeItem(item, visible) {
  27590. const { group, label, line, symbol } = item.legendItem || {};
  27591. if (group) {
  27592. group[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
  27593. }
  27594. if (!this.chart.styledMode) {
  27595. const { itemHiddenStyle } = this, hiddenColor = itemHiddenStyle.color, symbolColor = visible ?
  27596. (item.color || hiddenColor) :
  27597. hiddenColor, markerOptions = item.options && item.options.marker;
  27598. let symbolAttr = { fill: symbolColor };
  27599. label === null || label === void 0 ? void 0 : label.css(merge(visible ? this.itemStyle : itemHiddenStyle));
  27600. line === null || line === void 0 ? void 0 : line.attr({ stroke: symbolColor });
  27601. if (symbol) {
  27602. // Apply marker options
  27603. if (markerOptions && symbol.isMarker) { // #585
  27604. symbolAttr = item.pointAttribs();
  27605. if (!visible) {
  27606. // #6769
  27607. symbolAttr.stroke = symbolAttr.fill = hiddenColor;
  27608. }
  27609. }
  27610. symbol.attr(symbolAttr);
  27611. }
  27612. }
  27613. fireEvent(this, 'afterColorizeItem', { item, visible });
  27614. }
  27615. /**
  27616. * @private
  27617. * @function Highcharts.Legend#positionItems
  27618. */
  27619. positionItems() {
  27620. // Now that the legend width and height are established, put the items
  27621. // in the final position
  27622. this.allItems.forEach(this.positionItem, this);
  27623. if (!this.chart.isResizing) {
  27624. this.positionCheckboxes();
  27625. }
  27626. }
  27627. /**
  27628. * Position the legend item.
  27629. *
  27630. * @private
  27631. * @function Highcharts.Legend#positionItem
  27632. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27633. * The item to position
  27634. */
  27635. positionItem(item) {
  27636. const legend = this, { group, x = 0, y = 0 } = item.legendItem || {}, options = legend.options, symbolPadding = options.symbolPadding, ltr = !options.rtl, checkbox = item.checkbox;
  27637. if (group && group.element) {
  27638. const attribs = {
  27639. translateX: ltr ?
  27640. x :
  27641. legend.legendWidth - x - 2 * symbolPadding - 4,
  27642. translateY: y
  27643. };
  27644. const complete = () => {
  27645. fireEvent(this, 'afterPositionItem', { item });
  27646. };
  27647. group[defined(group.translateY) ? 'animate' : 'attr'](attribs, void 0, complete);
  27648. }
  27649. if (checkbox) {
  27650. checkbox.x = x;
  27651. checkbox.y = y;
  27652. }
  27653. }
  27654. /**
  27655. * Destroy a single legend item, used internally on removing series items.
  27656. *
  27657. * @private
  27658. * @function Highcharts.Legend#destroyItem
  27659. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27660. * The item to remove
  27661. */
  27662. destroyItem(item) {
  27663. const checkbox = item.checkbox, legendItem = item.legendItem || {};
  27664. // destroy SVG elements
  27665. for (const key of ['group', 'label', 'line', 'symbol']) {
  27666. if (legendItem[key]) {
  27667. legendItem[key] = legendItem[key].destroy();
  27668. }
  27669. }
  27670. if (checkbox) {
  27671. discardElement(checkbox);
  27672. }
  27673. item.legendItem = void 0;
  27674. }
  27675. /**
  27676. * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
  27677. * must be called after destruction.
  27678. *
  27679. * @private
  27680. * @function Highcharts.Legend#destroy
  27681. */
  27682. destroy() {
  27683. const legend = this;
  27684. // Destroy items
  27685. for (const item of this.getAllItems()) {
  27686. this.destroyItem(item);
  27687. }
  27688. // Destroy legend elements
  27689. for (const key of [
  27690. 'clipRect',
  27691. 'up',
  27692. 'down',
  27693. 'pager',
  27694. 'nav',
  27695. 'box',
  27696. 'title',
  27697. 'group'
  27698. ]) {
  27699. if (legend[key]) {
  27700. legend[key] = legend[key].destroy();
  27701. }
  27702. }
  27703. this.display = null; // Reset in .render on update.
  27704. }
  27705. /**
  27706. * Position the checkboxes after the width is determined.
  27707. *
  27708. * @private
  27709. * @function Highcharts.Legend#positionCheckboxes
  27710. */
  27711. positionCheckboxes() {
  27712. const alignAttr = this.group && this.group.alignAttr, clipHeight = this.clipHeight || this.legendHeight, titleHeight = this.titleHeight;
  27713. let translateY;
  27714. if (alignAttr) {
  27715. translateY = alignAttr.translateY;
  27716. this.allItems.forEach(function (item) {
  27717. const checkbox = item.checkbox;
  27718. let top;
  27719. if (checkbox) {
  27720. top = translateY + titleHeight + checkbox.y +
  27721. (this.scrollOffset || 0) + 3;
  27722. css(checkbox, {
  27723. left: (alignAttr.translateX + item.checkboxOffset +
  27724. checkbox.x - 20) + 'px',
  27725. top: top + 'px',
  27726. display: this.proximate || (top > translateY - 6 &&
  27727. top < translateY + clipHeight - 6) ?
  27728. '' :
  27729. 'none'
  27730. });
  27731. }
  27732. }, this);
  27733. }
  27734. }
  27735. /**
  27736. * Render the legend title on top of the legend.
  27737. *
  27738. * @private
  27739. * @function Highcharts.Legend#renderTitle
  27740. */
  27741. renderTitle() {
  27742. const options = this.options, padding = this.padding, titleOptions = options.title;
  27743. let bBox, titleHeight = 0;
  27744. if (titleOptions.text) {
  27745. if (!this.title) {
  27746. /**
  27747. * SVG element of the legend title.
  27748. *
  27749. * @readonly
  27750. * @name Highcharts.Legend#title
  27751. * @type {Highcharts.SVGElement}
  27752. */
  27753. this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, void 0, void 0, void 0, options.useHTML, void 0, 'legend-title')
  27754. .attr({ zIndex: 1 });
  27755. if (!this.chart.styledMode) {
  27756. this.title.css(titleOptions.style);
  27757. }
  27758. this.title.add(this.group);
  27759. }
  27760. // Set the max title width (#7253)
  27761. if (!titleOptions.width) {
  27762. this.title.css({
  27763. width: this.maxLegendWidth + 'px'
  27764. });
  27765. }
  27766. bBox = this.title.getBBox();
  27767. titleHeight = bBox.height;
  27768. this.offsetWidth = bBox.width; // #1717
  27769. this.contentGroup.attr({ translateY: titleHeight });
  27770. }
  27771. this.titleHeight = titleHeight;
  27772. }
  27773. /**
  27774. * Set the legend item text.
  27775. *
  27776. * @function Highcharts.Legend#setText
  27777. * @param {Highcharts.Point|Highcharts.Series} item
  27778. * The item for which to update the text in the legend.
  27779. */
  27780. setText(item) {
  27781. const options = this.options;
  27782. item.legendItem.label.attr({
  27783. text: options.labelFormat ?
  27784. format(options.labelFormat, item, this.chart) :
  27785. options.labelFormatter.call(item)
  27786. });
  27787. }
  27788. /**
  27789. * Render a single specific legend item. Called internally from the `render`
  27790. * function.
  27791. *
  27792. * @private
  27793. * @function Highcharts.Legend#renderItem
  27794. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27795. * The item to render.
  27796. */
  27797. renderItem(item) {
  27798. const legend = this, legendItem = item.legendItem = item.legendItem || {}, chart = legend.chart, renderer = chart.renderer, options = legend.options, horizontal = options.layout === 'horizontal', symbolWidth = legend.symbolWidth, symbolPadding = options.symbolPadding || 0, itemStyle = legend.itemStyle, itemHiddenStyle = legend.itemHiddenStyle, itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, ltr = !options.rtl, isSeries = !item.series, series = !isSeries && item.series.drawLegendSymbol ?
  27799. item.series :
  27800. item, seriesOptions = series.options, showCheckbox = (legend.createCheckboxForItem) &&
  27801. seriesOptions &&
  27802. seriesOptions.showCheckbox, useHTML = options.useHTML, itemClassName = item.options.className;
  27803. let label = legendItem.label,
  27804. // full width minus text width
  27805. itemExtraWidth = symbolWidth + symbolPadding +
  27806. itemDistance + (showCheckbox ? 20 : 0);
  27807. if (!label) { // generate it once, later move it
  27808. // Generate the group box, a group to hold the symbol and text. Text
  27809. // is to be appended in Legend class.
  27810. legendItem.group = renderer
  27811. .g('legend-item')
  27812. .addClass('highcharts-' + series.type + '-series ' +
  27813. 'highcharts-color-' + item.colorIndex +
  27814. (itemClassName ? ' ' + itemClassName : '') +
  27815. (isSeries ?
  27816. ' highcharts-series-' + item.index :
  27817. ''))
  27818. .attr({ zIndex: 1 })
  27819. .add(legend.scrollGroup);
  27820. // Generate the list item text and add it to the group
  27821. legendItem.label = label = renderer.text('', ltr ?
  27822. symbolWidth + symbolPadding :
  27823. -symbolPadding, legend.baseline || 0, useHTML);
  27824. if (!chart.styledMode) {
  27825. // merge to prevent modifying original (#1021)
  27826. label.css(merge(item.visible ?
  27827. itemStyle :
  27828. itemHiddenStyle));
  27829. }
  27830. label
  27831. .attr({
  27832. align: ltr ? 'left' : 'right',
  27833. zIndex: 2
  27834. })
  27835. .add(legendItem.group);
  27836. // Get the baseline for the first item - the font size is equal for
  27837. // all
  27838. if (!legend.baseline) {
  27839. legend.fontMetrics = renderer.fontMetrics(label);
  27840. legend.baseline =
  27841. legend.fontMetrics.f + 3 + legend.itemMarginTop;
  27842. label.attr('y', legend.baseline);
  27843. legend.symbolHeight =
  27844. pick(options.symbolHeight, legend.fontMetrics.f);
  27845. if (options.squareSymbol) {
  27846. legend.symbolWidth = pick(options.symbolWidth, Math.max(legend.symbolHeight, 16));
  27847. itemExtraWidth = legend.symbolWidth + symbolPadding +
  27848. itemDistance + (showCheckbox ? 20 : 0);
  27849. if (ltr) {
  27850. label.attr('x', legend.symbolWidth + symbolPadding);
  27851. }
  27852. }
  27853. }
  27854. // Draw the legend symbol inside the group box
  27855. series.drawLegendSymbol(legend, item);
  27856. if (legend.setItemEvents) {
  27857. legend.setItemEvents(item, label, useHTML);
  27858. }
  27859. }
  27860. // Add the HTML checkbox on top
  27861. if (showCheckbox && !item.checkbox && legend.createCheckboxForItem) {
  27862. legend.createCheckboxForItem(item);
  27863. }
  27864. // Colorize the items
  27865. legend.colorizeItem(item, item.visible);
  27866. // Take care of max width and text overflow (#6659)
  27867. if (chart.styledMode || !itemStyle.width) {
  27868. label.css({
  27869. width: ((options.itemWidth ||
  27870. legend.widthOption ||
  27871. chart.spacingBox.width) - itemExtraWidth) + 'px'
  27872. });
  27873. }
  27874. // Always update the text
  27875. legend.setText(item);
  27876. // calculate the positions for the next line
  27877. const bBox = label.getBBox();
  27878. const fontMetricsH = (legend.fontMetrics && legend.fontMetrics.h) || 0;
  27879. item.itemWidth = item.checkboxOffset =
  27880. options.itemWidth ||
  27881. legendItem.labelWidth ||
  27882. bBox.width + itemExtraWidth;
  27883. legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
  27884. legend.totalItemWidth += item.itemWidth;
  27885. legend.itemHeight = item.itemHeight = Math.round(legendItem.labelHeight ||
  27886. // use bBox for multiline (#16398)
  27887. (bBox.height > fontMetricsH * 1.5 ? bBox.height : fontMetricsH));
  27888. }
  27889. /**
  27890. * Get the position of the item in the layout. We now know the
  27891. * maxItemWidth from the previous loop.
  27892. *
  27893. * @private
  27894. * @function Highcharts.Legend#layoutItem
  27895. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27896. */
  27897. layoutItem(item) {
  27898. const options = this.options, padding = this.padding, horizontal = options.layout === 'horizontal', itemHeight = item.itemHeight, itemMarginBottom = this.itemMarginBottom, itemMarginTop = this.itemMarginTop, itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, maxLegendWidth = this.maxLegendWidth, itemWidth = (options.alignColumns &&
  27899. this.totalItemWidth > maxLegendWidth) ?
  27900. this.maxItemWidth :
  27901. item.itemWidth, legendItem = item.legendItem || {};
  27902. // If the item exceeds the width, start a new line
  27903. if (horizontal &&
  27904. this.itemX - padding + itemWidth > maxLegendWidth) {
  27905. this.itemX = padding;
  27906. if (this.lastLineHeight) { // Not for the first line (#10167)
  27907. this.itemY += (itemMarginTop +
  27908. this.lastLineHeight +
  27909. itemMarginBottom);
  27910. }
  27911. this.lastLineHeight = 0; // reset for next line (#915, #3976)
  27912. }
  27913. // Set the edge positions
  27914. this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
  27915. this.lastLineHeight = Math.max(// #915
  27916. itemHeight, this.lastLineHeight);
  27917. // cache the position of the newly generated or reordered items
  27918. legendItem.x = this.itemX;
  27919. legendItem.y = this.itemY;
  27920. // advance
  27921. if (horizontal) {
  27922. this.itemX += itemWidth;
  27923. }
  27924. else {
  27925. this.itemY +=
  27926. itemMarginTop + itemHeight + itemMarginBottom;
  27927. this.lastLineHeight = itemHeight;
  27928. }
  27929. // the width of the widest item
  27930. this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
  27931. // decrease by itemDistance only when no checkbox #4853
  27932. 0 :
  27933. itemDistance) : itemWidth) + padding, this.offsetWidth);
  27934. }
  27935. /**
  27936. * Get all items, which is one item per series for most series and one
  27937. * item per point for pie series and its derivatives. Fires the event
  27938. * `afterGetAllItems`.
  27939. *
  27940. * @private
  27941. * @function Highcharts.Legend#getAllItems
  27942. * @return {Array<(Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series)>}
  27943. * The current items in the legend.
  27944. * @emits Highcharts.Legend#event:afterGetAllItems
  27945. */
  27946. getAllItems() {
  27947. let allItems = [];
  27948. this.chart.series.forEach(function (series) {
  27949. const seriesOptions = series && series.options;
  27950. // Handle showInLegend. If the series is linked to another series,
  27951. // defaults to false.
  27952. if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
  27953. // Use points or series for the legend item depending on
  27954. // legendType
  27955. allItems = allItems.concat((series.legendItem || {}).labels ||
  27956. (seriesOptions.legendType === 'point' ?
  27957. series.data :
  27958. series));
  27959. }
  27960. });
  27961. fireEvent(this, 'afterGetAllItems', { allItems });
  27962. return allItems;
  27963. }
  27964. /**
  27965. * Get a short, three letter string reflecting the alignment and layout.
  27966. *
  27967. * @private
  27968. * @function Highcharts.Legend#getAlignment
  27969. * @return {string}
  27970. * The alignment, empty string if floating
  27971. */
  27972. getAlignment() {
  27973. const options = this.options;
  27974. // Use the first letter of each alignment option in order to detect
  27975. // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
  27976. if (this.proximate) {
  27977. return options.align.charAt(0) + 'tv';
  27978. }
  27979. return options.floating ? '' : (options.align.charAt(0) +
  27980. options.verticalAlign.charAt(0) +
  27981. options.layout.charAt(0));
  27982. }
  27983. /**
  27984. * Adjust the chart margins by reserving space for the legend on only one
  27985. * side of the chart. If the position is set to a corner, top or bottom is
  27986. * reserved for horizontal legends and left or right for vertical ones.
  27987. *
  27988. * @private
  27989. * @function Highcharts.Legend#adjustMargins
  27990. * @param {Array<number>} margin
  27991. * @param {Array<number>} spacing
  27992. */
  27993. adjustMargins(margin, spacing) {
  27994. const chart = this.chart, options = this.options, alignment = this.getAlignment();
  27995. if (alignment) {
  27996. ([
  27997. /(lth|ct|rth)/,
  27998. /(rtv|rm|rbv)/,
  27999. /(rbh|cb|lbh)/,
  28000. /(lbv|lm|ltv)/
  28001. ]).forEach(function (alignments, side) {
  28002. if (alignments.test(alignment) && !defined(margin[side])) {
  28003. // Now we have detected on which side of the chart we should
  28004. // reserve space for the legend
  28005. chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
  28006. [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
  28007. pick(options.margin, 12) +
  28008. spacing[side] +
  28009. (chart.titleOffset[side] || 0)));
  28010. }
  28011. });
  28012. }
  28013. }
  28014. /**
  28015. * @private
  28016. * @function Highcharts.Legend#proximatePositions
  28017. */
  28018. proximatePositions() {
  28019. const chart = this.chart, boxes = [], alignLeft = this.options.align === 'left';
  28020. this.allItems.forEach(function (item) {
  28021. let lastPoint, height, useFirstPoint = alignLeft, target, top;
  28022. if (item.yAxis) {
  28023. if (item.xAxis.options.reversed) {
  28024. useFirstPoint = !useFirstPoint;
  28025. }
  28026. if (item.points) {
  28027. lastPoint = find(useFirstPoint ?
  28028. item.points :
  28029. item.points.slice(0).reverse(), function (item) {
  28030. return isNumber(item.plotY);
  28031. });
  28032. }
  28033. height = this.itemMarginTop +
  28034. item.legendItem.label.getBBox().height +
  28035. this.itemMarginBottom;
  28036. top = item.yAxis.top - chart.plotTop;
  28037. if (item.visible) {
  28038. target = lastPoint ?
  28039. lastPoint.plotY :
  28040. item.yAxis.height;
  28041. target += top - 0.3 * height;
  28042. }
  28043. else {
  28044. target = top + item.yAxis.height;
  28045. }
  28046. boxes.push({
  28047. target: target,
  28048. size: height,
  28049. item
  28050. });
  28051. }
  28052. }, this);
  28053. let legendItem;
  28054. for (const box of distribute(boxes, chart.plotHeight)) {
  28055. legendItem = box.item.legendItem || {};
  28056. if (isNumber(box.pos)) {
  28057. legendItem.y = chart.plotTop - chart.spacing[0] + box.pos;
  28058. }
  28059. }
  28060. }
  28061. /**
  28062. * Render the legend. This method can be called both before and after
  28063. * `chart.render`. If called after, it will only rearrange items instead
  28064. * of creating new ones. Called internally on initial render and after
  28065. * redraws.
  28066. *
  28067. * @private
  28068. * @function Highcharts.Legend#render
  28069. */
  28070. render() {
  28071. const legend = this, chart = legend.chart, renderer = chart.renderer, options = legend.options, padding = legend.padding,
  28072. // add each series or point
  28073. allItems = legend.getAllItems();
  28074. let display, legendWidth, legendHeight, legendGroup = legend.group, allowedWidth, box = legend.box;
  28075. legend.itemX = padding;
  28076. legend.itemY = legend.initialItemY;
  28077. legend.offsetWidth = 0;
  28078. legend.lastItemY = 0;
  28079. legend.widthOption = relativeLength(options.width, chart.spacingBox.width - padding);
  28080. // Compute how wide the legend is allowed to be
  28081. allowedWidth = chart.spacingBox.width - 2 * padding - options.x;
  28082. if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
  28083. allowedWidth /= 2;
  28084. }
  28085. legend.maxLegendWidth = legend.widthOption || allowedWidth;
  28086. if (!legendGroup) {
  28087. /**
  28088. * SVG group of the legend.
  28089. *
  28090. * @readonly
  28091. * @name Highcharts.Legend#group
  28092. * @type {Highcharts.SVGElement}
  28093. */
  28094. legend.group = legendGroup = renderer
  28095. .g('legend')
  28096. .addClass(options.className || '')
  28097. .attr({ zIndex: 7 })
  28098. .add();
  28099. legend.contentGroup = renderer
  28100. .g()
  28101. .attr({ zIndex: 1 }) // above background
  28102. .add(legendGroup);
  28103. legend.scrollGroup = renderer
  28104. .g()
  28105. .add(legend.contentGroup);
  28106. }
  28107. legend.renderTitle();
  28108. // sort by legendIndex
  28109. stableSort(allItems, (a, b) => ((a.options && a.options.legendIndex) || 0) -
  28110. ((b.options && b.options.legendIndex) || 0));
  28111. // reversed legend
  28112. if (options.reversed) {
  28113. allItems.reverse();
  28114. }
  28115. /**
  28116. * All items for the legend, which is an array of series for most series
  28117. * and an array of points for pie series and its derivatives.
  28118. *
  28119. * @readonly
  28120. * @name Highcharts.Legend#allItems
  28121. * @type {Array<(Highcharts.Point|Highcharts.Series)>}
  28122. */
  28123. legend.allItems = allItems;
  28124. legend.display = display = !!allItems.length;
  28125. // Render the items. First we run a loop to set the text and properties
  28126. // and read all the bounding boxes. The next loop computes the item
  28127. // positions based on the bounding boxes.
  28128. legend.lastLineHeight = 0;
  28129. legend.maxItemWidth = 0;
  28130. legend.totalItemWidth = 0;
  28131. legend.itemHeight = 0;
  28132. allItems.forEach(legend.renderItem, legend);
  28133. allItems.forEach(legend.layoutItem, legend);
  28134. // Get the box
  28135. legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
  28136. legendHeight = legend.lastItemY + legend.lastLineHeight +
  28137. legend.titleHeight;
  28138. legendHeight = legend.handleOverflow(legendHeight);
  28139. legendHeight += padding;
  28140. // Draw the border and/or background
  28141. if (!box) {
  28142. /**
  28143. * SVG element of the legend box.
  28144. *
  28145. * @readonly
  28146. * @name Highcharts.Legend#box
  28147. * @type {Highcharts.SVGElement}
  28148. */
  28149. legend.box = box = renderer.rect()
  28150. .addClass('highcharts-legend-box')
  28151. .attr({
  28152. r: options.borderRadius
  28153. })
  28154. .add(legendGroup);
  28155. }
  28156. // Presentational
  28157. if (!chart.styledMode) {
  28158. box
  28159. .attr({
  28160. stroke: options.borderColor,
  28161. 'stroke-width': options.borderWidth || 0,
  28162. fill: options.backgroundColor || 'none'
  28163. })
  28164. .shadow(options.shadow);
  28165. }
  28166. if (legendWidth > 0 && legendHeight > 0) {
  28167. box[box.placed ? 'animate' : 'attr'](box.crisp.call({}, {
  28168. x: 0,
  28169. y: 0,
  28170. width: legendWidth,
  28171. height: legendHeight
  28172. }, box.strokeWidth()));
  28173. }
  28174. // hide the border if no items
  28175. legendGroup[display ? 'show' : 'hide']();
  28176. // Open for responsiveness
  28177. if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
  28178. legendWidth = legendHeight = 0;
  28179. }
  28180. legend.legendWidth = legendWidth;
  28181. legend.legendHeight = legendHeight;
  28182. if (display) {
  28183. legend.align();
  28184. }
  28185. if (!this.proximate) {
  28186. this.positionItems();
  28187. }
  28188. fireEvent(this, 'afterRender');
  28189. }
  28190. /**
  28191. * Align the legend to chart's box.
  28192. *
  28193. * @private
  28194. * @function Highcharts.align
  28195. * @param {Highcharts.BBoxObject} alignTo
  28196. */
  28197. align(alignTo = this.chart.spacingBox) {
  28198. const chart = this.chart, options = this.options;
  28199. // If aligning to the top and the layout is horizontal, adjust for
  28200. // the title (#7428)
  28201. let y = alignTo.y;
  28202. if (/(lth|ct|rth)/.test(this.getAlignment()) &&
  28203. chart.titleOffset[0] > 0) {
  28204. y += chart.titleOffset[0];
  28205. }
  28206. else if (/(lbh|cb|rbh)/.test(this.getAlignment()) &&
  28207. chart.titleOffset[2] > 0) {
  28208. y -= chart.titleOffset[2];
  28209. }
  28210. if (y !== alignTo.y) {
  28211. alignTo = merge(alignTo, { y });
  28212. }
  28213. if (!chart.hasRendered) {
  28214. // Avoid animation when adjusting alignment for responsiveness and
  28215. // colorAxis label layout
  28216. this.group.placed = false;
  28217. }
  28218. this.group.align(merge(options, {
  28219. width: this.legendWidth,
  28220. height: this.legendHeight,
  28221. verticalAlign: this.proximate ? 'top' : options.verticalAlign
  28222. }), true, alignTo);
  28223. }
  28224. /**
  28225. * Set up the overflow handling by adding navigation with up and down arrows
  28226. * below the legend.
  28227. *
  28228. * @private
  28229. * @function Highcharts.Legend#handleOverflow
  28230. */
  28231. handleOverflow(legendHeight) {
  28232. const legend = this, chart = this.chart, renderer = chart.renderer, options = this.options, optionsY = options.y, alignTop = options.verticalAlign === 'top', padding = this.padding, maxHeight = options.maxHeight, navOptions = options.navigation, animation = pick(navOptions.animation, true), arrowSize = navOptions.arrowSize || 12, pages = this.pages, allItems = this.allItems, clipToHeight = function (height) {
  28233. if (typeof height === 'number') {
  28234. clipRect.attr({
  28235. height: height
  28236. });
  28237. }
  28238. else if (clipRect) { // Reset (#5912)
  28239. legend.clipRect = clipRect.destroy();
  28240. legend.contentGroup.clip();
  28241. }
  28242. // useHTML
  28243. if (legend.contentGroup.div) {
  28244. legend.contentGroup.div.style.clip = height ?
  28245. 'rect(' + padding + 'px,9999px,' +
  28246. (padding + height) + 'px,0)' :
  28247. 'auto';
  28248. }
  28249. }, addTracker = function (key) {
  28250. legend[key] = renderer
  28251. .circle(0, 0, arrowSize * 1.3)
  28252. .translate(arrowSize / 2, arrowSize / 2)
  28253. .add(nav);
  28254. if (!chart.styledMode) {
  28255. legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
  28256. }
  28257. return legend[key];
  28258. };
  28259. let clipHeight, lastY, legendItem, spaceHeight = (chart.spacingBox.height +
  28260. (alignTop ? -optionsY : optionsY) - padding), nav = this.nav, clipRect = this.clipRect;
  28261. // Adjust the height
  28262. if (options.layout === 'horizontal' &&
  28263. options.verticalAlign !== 'middle' &&
  28264. !options.floating) {
  28265. spaceHeight /= 2;
  28266. }
  28267. if (maxHeight) {
  28268. spaceHeight = Math.min(spaceHeight, maxHeight);
  28269. }
  28270. // Reset the legend height and adjust the clipping rectangle
  28271. pages.length = 0;
  28272. if (legendHeight &&
  28273. spaceHeight > 0 &&
  28274. legendHeight > spaceHeight &&
  28275. navOptions.enabled !== false) {
  28276. this.clipHeight = clipHeight =
  28277. Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
  28278. this.currentPage = pick(this.currentPage, 1);
  28279. this.fullHeight = legendHeight;
  28280. // Fill pages with Y positions so that the top of each a legend item
  28281. // defines the scroll top for each page (#2098)
  28282. allItems.forEach((item, i) => {
  28283. legendItem = item.legendItem || {};
  28284. const y = legendItem.y || 0, h = Math.round(legendItem.label.getBBox().height);
  28285. let len = pages.length;
  28286. if (!len || (y - pages[len - 1] > clipHeight &&
  28287. (lastY || y) !== pages[len - 1])) {
  28288. pages.push(lastY || y);
  28289. len++;
  28290. }
  28291. // Keep track of which page each item is on
  28292. legendItem.pageIx = len - 1;
  28293. if (lastY) {
  28294. (allItems[i - 1].legendItem || {}).pageIx = len - 1;
  28295. }
  28296. // add the last page if needed (#2617, #13683)
  28297. if (
  28298. // check the last item
  28299. i === allItems.length - 1 &&
  28300. // if adding next page is needed (#18768)
  28301. y + h - pages[len - 1] > clipHeight &&
  28302. y > pages[len - 1]) {
  28303. pages.push(y);
  28304. legendItem.pageIx = len;
  28305. }
  28306. if (y !== lastY) {
  28307. lastY = y;
  28308. }
  28309. });
  28310. // Only apply clipping if needed. Clipping causes blurred legend in
  28311. // PDF export (#1787)
  28312. if (!clipRect) {
  28313. clipRect = legend.clipRect =
  28314. renderer.clipRect(0, padding - 2, 9999, 0);
  28315. legend.contentGroup.clip(clipRect);
  28316. }
  28317. clipToHeight(clipHeight);
  28318. // Add navigation elements
  28319. if (!nav) {
  28320. this.nav = nav = renderer.g()
  28321. .attr({ zIndex: 1 })
  28322. .add(this.group);
  28323. this.up = renderer
  28324. .symbol('triangle', 0, 0, arrowSize, arrowSize)
  28325. .add(nav);
  28326. addTracker('upTracker')
  28327. .on('click', function () {
  28328. legend.scroll(-1, animation);
  28329. });
  28330. this.pager = renderer.text('', 15, 10)
  28331. .addClass('highcharts-legend-navigation');
  28332. if (!chart.styledMode && navOptions.style) {
  28333. this.pager.css(navOptions.style);
  28334. }
  28335. this.pager.add(nav);
  28336. this.down = renderer
  28337. .symbol('triangle-down', 0, 0, arrowSize, arrowSize)
  28338. .add(nav);
  28339. addTracker('downTracker')
  28340. .on('click', function () {
  28341. legend.scroll(1, animation);
  28342. });
  28343. }
  28344. // Set initial position
  28345. legend.scroll(0);
  28346. legendHeight = spaceHeight;
  28347. // Reset
  28348. }
  28349. else if (nav) {
  28350. clipToHeight();
  28351. this.nav = nav.destroy(); // #6322
  28352. this.scrollGroup.attr({
  28353. translateY: 1
  28354. });
  28355. this.clipHeight = 0; // #1379
  28356. }
  28357. return legendHeight;
  28358. }
  28359. /**
  28360. * Scroll the legend by a number of pages.
  28361. *
  28362. * @private
  28363. * @function Highcharts.Legend#scroll
  28364. *
  28365. * @param {number} scrollBy
  28366. * The number of pages to scroll.
  28367. *
  28368. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  28369. * Whether and how to apply animation.
  28370. *
  28371. */
  28372. scroll(scrollBy, animation) {
  28373. const chart = this.chart, pages = this.pages, pageCount = pages.length, clipHeight = this.clipHeight, navOptions = this.options.navigation, pager = this.pager, padding = this.padding;
  28374. let currentPage = this.currentPage + scrollBy;
  28375. // When resizing while looking at the last page
  28376. if (currentPage > pageCount) {
  28377. currentPage = pageCount;
  28378. }
  28379. if (currentPage > 0) {
  28380. if (typeof animation !== 'undefined') {
  28381. setAnimation(animation, chart);
  28382. }
  28383. this.nav.attr({
  28384. translateX: padding,
  28385. translateY: clipHeight + this.padding + 7 + this.titleHeight,
  28386. visibility: 'inherit'
  28387. });
  28388. [this.up, this.upTracker].forEach(function (elem) {
  28389. elem.attr({
  28390. 'class': currentPage === 1 ?
  28391. 'highcharts-legend-nav-inactive' :
  28392. 'highcharts-legend-nav-active'
  28393. });
  28394. });
  28395. pager.attr({
  28396. text: currentPage + '/' + pageCount
  28397. });
  28398. [this.down, this.downTracker].forEach(function (elem) {
  28399. elem.attr({
  28400. // adjust to text width
  28401. x: 18 + this.pager.getBBox().width,
  28402. 'class': currentPage === pageCount ?
  28403. 'highcharts-legend-nav-inactive' :
  28404. 'highcharts-legend-nav-active'
  28405. });
  28406. }, this);
  28407. if (!chart.styledMode) {
  28408. this.up
  28409. .attr({
  28410. fill: currentPage === 1 ?
  28411. navOptions.inactiveColor :
  28412. navOptions.activeColor
  28413. });
  28414. this.upTracker
  28415. .css({
  28416. cursor: currentPage === 1 ? 'default' : 'pointer'
  28417. });
  28418. this.down
  28419. .attr({
  28420. fill: currentPage === pageCount ?
  28421. navOptions.inactiveColor :
  28422. navOptions.activeColor
  28423. });
  28424. this.downTracker
  28425. .css({
  28426. cursor: currentPage === pageCount ?
  28427. 'default' :
  28428. 'pointer'
  28429. });
  28430. }
  28431. this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
  28432. this.scrollGroup.animate({
  28433. translateY: this.scrollOffset
  28434. });
  28435. this.currentPage = currentPage;
  28436. this.positionCheckboxes();
  28437. // Fire event after scroll animation is complete
  28438. const animOptions = animObject(pick(animation, chart.renderer.globalAnimation, true));
  28439. syncTimeout(() => {
  28440. fireEvent(this, 'afterScroll', { currentPage });
  28441. }, animOptions.duration);
  28442. }
  28443. }
  28444. /**
  28445. * @private
  28446. * @function Highcharts.Legend#setItemEvents
  28447. * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
  28448. * @param {Highcharts.SVGElement} legendLabel
  28449. * @param {boolean} [useHTML=false]
  28450. * @emits Highcharts.Point#event:legendItemClick
  28451. * @emits Highcharts.Series#event:legendItemClick
  28452. */
  28453. setItemEvents(item, legendLabel, useHTML) {
  28454. const legend = this, legendItem = item.legendItem || {}, boxWrapper = legend.chart.renderer.boxWrapper, isPoint = item instanceof Point, activeClass = 'highcharts-legend-' +
  28455. (isPoint ? 'point' : 'series') + '-active', styledMode = legend.chart.styledMode,
  28456. // When `useHTML`, the symbol is rendered in other group, so
  28457. // we need to apply events listeners to both places
  28458. legendElements = useHTML ?
  28459. [legendLabel, legendItem.symbol] :
  28460. [legendItem.group];
  28461. const setOtherItemsState = (state) => {
  28462. legend.allItems.forEach((otherItem) => {
  28463. if (item !== otherItem) {
  28464. [otherItem]
  28465. .concat(otherItem.linkedSeries || [])
  28466. .forEach((otherItem) => {
  28467. otherItem.setState(state, !isPoint);
  28468. });
  28469. }
  28470. });
  28471. };
  28472. // Set the events on the item group, or in case of useHTML, the item
  28473. // itself (#1249)
  28474. for (const element of legendElements) {
  28475. if (element) {
  28476. element
  28477. .on('mouseover', function () {
  28478. if (item.visible) {
  28479. setOtherItemsState('inactive');
  28480. }
  28481. item.setState('hover');
  28482. // A CSS class to dim or hide other than the hovered
  28483. // series.
  28484. // Works only if hovered series is visible (#10071).
  28485. if (item.visible) {
  28486. boxWrapper.addClass(activeClass);
  28487. }
  28488. if (!styledMode) {
  28489. legendLabel.css(legend.options.itemHoverStyle);
  28490. }
  28491. })
  28492. .on('mouseout', function () {
  28493. if (!legend.chart.styledMode) {
  28494. legendLabel.css(merge(item.visible ?
  28495. legend.itemStyle :
  28496. legend.itemHiddenStyle));
  28497. }
  28498. setOtherItemsState('');
  28499. // A CSS class to dim or hide other than the hovered
  28500. // series.
  28501. boxWrapper.removeClass(activeClass);
  28502. item.setState();
  28503. })
  28504. .on('click', function (event) {
  28505. const strLegendItemClick = 'legendItemClick', fnLegendItemClick = function () {
  28506. if (item.setVisible) {
  28507. item.setVisible();
  28508. }
  28509. // Reset inactive state
  28510. setOtherItemsState(item.visible ? 'inactive' : '');
  28511. };
  28512. // A CSS class to dim or hide other than the hovered
  28513. // series. Event handling in iOS causes the activeClass
  28514. // to be added prior to click in some cases (#7418).
  28515. boxWrapper.removeClass(activeClass);
  28516. // Pass over the click/touch event. #4.
  28517. event = {
  28518. browserEvent: event
  28519. };
  28520. // click the name or symbol
  28521. if (item.firePointEvent) { // point
  28522. item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
  28523. }
  28524. else {
  28525. fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
  28526. }
  28527. });
  28528. }
  28529. }
  28530. }
  28531. /**
  28532. * @private
  28533. * @function Highcharts.Legend#createCheckboxForItem
  28534. * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
  28535. * @emits Highcharts.Series#event:checkboxClick
  28536. */
  28537. createCheckboxForItem(item) {
  28538. const legend = this;
  28539. item.checkbox = createElement('input', {
  28540. type: 'checkbox',
  28541. className: 'highcharts-legend-checkbox',
  28542. checked: item.selected,
  28543. defaultChecked: item.selected // required by IE7
  28544. }, legend.options.itemCheckboxStyle, legend.chart.container);
  28545. addEvent(item.checkbox, 'click', function (event) {
  28546. const target = event.target;
  28547. fireEvent(item.series || item, 'checkboxClick', {
  28548. checked: target.checked,
  28549. item: item
  28550. }, function () {
  28551. item.select();
  28552. });
  28553. });
  28554. }
  28555. }
  28556. /* *
  28557. *
  28558. * Class Namespace
  28559. *
  28560. * */
  28561. (function (Legend) {
  28562. /* *
  28563. *
  28564. * Declarations
  28565. *
  28566. * */
  28567. /* *
  28568. *
  28569. * Constants
  28570. *
  28571. * */
  28572. const composedMembers = [];
  28573. /* *
  28574. *
  28575. * Functions
  28576. *
  28577. * */
  28578. /**
  28579. * @private
  28580. */
  28581. function compose(ChartClass) {
  28582. if (U.pushUnique(composedMembers, ChartClass)) {
  28583. addEvent(ChartClass, 'beforeMargins', function () {
  28584. /**
  28585. * The legend contains an interactive overview over chart items,
  28586. * usually individual series or points depending on the series
  28587. * type. The color axis and bubble legend are also rendered in
  28588. * the chart legend.
  28589. *
  28590. * @name Highcharts.Chart#legend
  28591. * @type {Highcharts.Legend}
  28592. */
  28593. this.legend = new Legend(this, this.options.legend);
  28594. });
  28595. }
  28596. }
  28597. Legend.compose = compose;
  28598. })(Legend || (Legend = {}));
  28599. /* *
  28600. *
  28601. * Default Export
  28602. *
  28603. * */
  28604. /* *
  28605. *
  28606. * API Declarations
  28607. *
  28608. * */
  28609. /**
  28610. * @interface Highcharts.LegendItemObject
  28611. */ /**
  28612. * @name Highcharts.LegendItemObject#item
  28613. * @type {Highcharts.SVGElement|undefined}
  28614. */ /**
  28615. * @name Highcharts.LegendItemObject#line
  28616. * @type {Highcharts.SVGElement|undefined}
  28617. */ /**
  28618. * @name Highcharts.LegendItemObject#symbol
  28619. * @type {Highcharts.SVGElement|undefined}
  28620. */
  28621. /**
  28622. * Gets fired when the legend item belonging to a point is clicked. The default
  28623. * action is to toggle the visibility of the point. This can be prevented by
  28624. * returning `false` or calling `event.preventDefault()`.
  28625. *
  28626. * @callback Highcharts.PointLegendItemClickCallbackFunction
  28627. *
  28628. * @param {Highcharts.Point} this
  28629. * The point on which the event occured.
  28630. *
  28631. * @param {Highcharts.PointLegendItemClickEventObject} event
  28632. * The event that occured.
  28633. */
  28634. /**
  28635. * Information about the legend click event.
  28636. *
  28637. * @interface Highcharts.PointLegendItemClickEventObject
  28638. */ /**
  28639. * Related browser event.
  28640. * @name Highcharts.PointLegendItemClickEventObject#browserEvent
  28641. * @type {Highcharts.PointerEvent}
  28642. */ /**
  28643. * Prevent the default action of toggle the visibility of the point.
  28644. * @name Highcharts.PointLegendItemClickEventObject#preventDefault
  28645. * @type {Function}
  28646. */ /**
  28647. * Related point.
  28648. * @name Highcharts.PointLegendItemClickEventObject#target
  28649. * @type {Highcharts.Point}
  28650. */ /**
  28651. * Event type.
  28652. * @name Highcharts.PointLegendItemClickEventObject#type
  28653. * @type {"legendItemClick"}
  28654. */
  28655. /**
  28656. * Series color as used by the legend and some series types.
  28657. * @name Highcharts.Series#color
  28658. * @type {Highcharts.ColorType|undefined}
  28659. */ /**
  28660. * Legend data for the series.
  28661. * @name Highcharts.Series#legendItem
  28662. * @type {Highcharts.LegendItemObject|undefined}
  28663. * @since 10.3.0
  28664. */
  28665. /**
  28666. * Gets fired when the legend item belonging to a series is clicked. The default
  28667. * action is to toggle the visibility of the series. This can be prevented by
  28668. * returning `false` or calling `event.preventDefault()`.
  28669. *
  28670. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  28671. *
  28672. * @param {Highcharts.Series} this
  28673. * The series where the event occured.
  28674. *
  28675. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  28676. * The event that occured.
  28677. */
  28678. /**
  28679. * Information about the legend click event.
  28680. *
  28681. * @interface Highcharts.SeriesLegendItemClickEventObject
  28682. */ /**
  28683. * Related browser event.
  28684. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  28685. * @type {Highcharts.PointerEvent}
  28686. */ /**
  28687. * Prevent the default action of toggle the visibility of the series.
  28688. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  28689. * @type {Function}
  28690. */ /**
  28691. * Related series.
  28692. * @name Highcharts.SeriesLegendItemClickEventObject#target
  28693. * @type {Highcharts.Series}
  28694. */ /**
  28695. * Event type.
  28696. * @name Highcharts.SeriesLegendItemClickEventObject#type
  28697. * @type {"legendItemClick"}
  28698. */
  28699. (''); // keeps doclets above in JS file
  28700. return Legend;
  28701. });
  28702. _registerModule(_modules, 'Core/Legend/LegendSymbol.js', [_modules['Core/Utilities.js']], function (U) {
  28703. /* *
  28704. *
  28705. * (c) 2010-2021 Torstein Honsi
  28706. *
  28707. * License: www.highcharts.com/license
  28708. *
  28709. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28710. *
  28711. * */
  28712. const { extend, merge, pick } = U;
  28713. /* *
  28714. *
  28715. * Namespace
  28716. *
  28717. * */
  28718. var LegendSymbol;
  28719. (function (LegendSymbol) {
  28720. /* *
  28721. *
  28722. * Functions
  28723. *
  28724. * */
  28725. /* eslint-disable valid-jsdoc */
  28726. /**
  28727. * Get the series' symbol in the legend.
  28728. *
  28729. * This method should be overridable to create custom symbols through
  28730. * Highcharts.seriesTypes[type].prototype.drawLegendSymbol.
  28731. *
  28732. * @private
  28733. * @function Highcharts.LegendSymbolMixin.lineMarker
  28734. *
  28735. * @param {Highcharts.Legend} legend
  28736. * The legend object.
  28737. */
  28738. function lineMarker(legend, item) {
  28739. const legendItem = this.legendItem = this.legendItem || {}, options = this.options, symbolWidth = legend.symbolWidth, symbolHeight = legend.symbolHeight, generalRadius = symbolHeight / 2, renderer = this.chart.renderer, legendItemGroup = legendItem.group, verticalCenter = legend.baseline -
  28740. Math.round(legend.fontMetrics.b * 0.3);
  28741. let attr = {}, legendSymbol, markerOptions = options.marker, lineSizer = 0;
  28742. // Draw the line
  28743. if (!this.chart.styledMode) {
  28744. attr = {
  28745. 'stroke-width': Math.min(options.lineWidth || 0, 24)
  28746. };
  28747. if (options.dashStyle) {
  28748. attr.dashstyle = options.dashStyle;
  28749. }
  28750. else if (options.linecap !== 'square') {
  28751. attr['stroke-linecap'] = 'round';
  28752. }
  28753. }
  28754. legendItem.line = renderer
  28755. .path()
  28756. .addClass('highcharts-graph')
  28757. .attr(attr)
  28758. .add(legendItemGroup);
  28759. if (attr['stroke-linecap']) {
  28760. lineSizer = Math.min(legendItem.line.strokeWidth(), symbolWidth) / 2;
  28761. }
  28762. if (symbolWidth) {
  28763. legendItem.line
  28764. .attr({
  28765. d: [
  28766. ['M', lineSizer, verticalCenter],
  28767. ['L', symbolWidth - lineSizer, verticalCenter]
  28768. ]
  28769. });
  28770. }
  28771. // Draw the marker
  28772. if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
  28773. // Do not allow the marker to be larger than the symbolHeight
  28774. let radius = Math.min(pick(markerOptions.radius, generalRadius), generalRadius);
  28775. // Restrict symbol markers size
  28776. if (this.symbol.indexOf('url') === 0) {
  28777. markerOptions = merge(markerOptions, {
  28778. width: symbolHeight,
  28779. height: symbolHeight
  28780. });
  28781. radius = 0;
  28782. }
  28783. legendItem.symbol = legendSymbol = renderer
  28784. .symbol(this.symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, 2 * radius, extend({ context: 'legend' }, markerOptions))
  28785. .addClass('highcharts-point')
  28786. .add(legendItemGroup);
  28787. legendSymbol.isMarker = true;
  28788. }
  28789. }
  28790. LegendSymbol.lineMarker = lineMarker;
  28791. /**
  28792. * Get the series' symbol in the legend.
  28793. *
  28794. * This method should be overridable to create custom symbols through
  28795. * Highcharts.seriesTypes[type].prototype.drawLegendSymbol.
  28796. *
  28797. * @private
  28798. * @function Highcharts.LegendSymbolMixin.rectangle
  28799. *
  28800. * @param {Highcharts.Legend} legend
  28801. * The legend object
  28802. *
  28803. * @param {Highcharts.Point|Highcharts.Series} item
  28804. * The series (this) or point
  28805. */
  28806. function rectangle(legend, item) {
  28807. const legendItem = item.legendItem || {}, options = legend.options, symbolHeight = legend.symbolHeight, square = options.squareSymbol, symbolWidth = square ? symbolHeight : legend.symbolWidth;
  28808. legendItem.symbol = this.chart.renderer
  28809. .rect(square ? (legend.symbolWidth - symbolHeight) / 2 : 0, legend.baseline - symbolHeight + 1, // #3988
  28810. symbolWidth, symbolHeight, pick(legend.options.symbolRadius, symbolHeight / 2))
  28811. .addClass('highcharts-point')
  28812. .attr({
  28813. zIndex: 3
  28814. })
  28815. .add(legendItem.group);
  28816. }
  28817. LegendSymbol.rectangle = rectangle;
  28818. })(LegendSymbol || (LegendSymbol = {}));
  28819. /* *
  28820. *
  28821. * Default Export
  28822. *
  28823. * */
  28824. return LegendSymbol;
  28825. });
  28826. _registerModule(_modules, 'Core/Series/SeriesDefaults.js', [], function () {
  28827. /* *
  28828. *
  28829. * (c) 2010-2021 Torstein Honsi
  28830. *
  28831. * License: www.highcharts.com/license
  28832. *
  28833. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28834. *
  28835. * */
  28836. /* *
  28837. *
  28838. * API Options
  28839. *
  28840. * */
  28841. /**
  28842. * General options for all series types.
  28843. *
  28844. * @optionparent plotOptions.series
  28845. */
  28846. const seriesDefaults = {
  28847. // base series options
  28848. /**
  28849. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  28850. * of a line graph. Round means that lines are rounded in the ends and
  28851. * bends.
  28852. *
  28853. * @type {Highcharts.SeriesLinecapValue}
  28854. * @default round
  28855. * @since 3.0.7
  28856. * @apioption plotOptions.line.linecap
  28857. */
  28858. /**
  28859. * Pixel width of the graph line.
  28860. *
  28861. * @see In styled mode, the line stroke-width can be set with the
  28862. * `.highcharts-graph` class name.
  28863. *
  28864. * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
  28865. * On all series
  28866. * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
  28867. * On one single series
  28868. *
  28869. * @product highcharts highstock
  28870. */
  28871. lineWidth: 1,
  28872. /**
  28873. * For some series, there is a limit that shuts down animation
  28874. * by default when the total number of points in the chart is too high.
  28875. * For example, for a column chart and its derivatives, animation does
  28876. * not run if there is more than 250 points totally. To disable this
  28877. * cap, set `animationLimit` to `Infinity`. This option works if animation
  28878. * is fired on individual points, not on a group of points like e.g. during
  28879. * the initial animation.
  28880. *
  28881. * @sample {highcharts} highcharts/plotoptions/series-animationlimit/
  28882. * Animation limit on updating individual points
  28883. *
  28884. * @type {number}
  28885. * @apioption plotOptions.series.animationLimit
  28886. */
  28887. /**
  28888. * Allow this series' points to be selected by clicking on the graphic
  28889. * (columns, point markers, pie slices, map areas etc).
  28890. *
  28891. * The selected points can be handled by point select and unselect
  28892. * events, or collectively by the [getSelectedPoints
  28893. * ](/class-reference/Highcharts.Chart#getSelectedPoints) function.
  28894. *
  28895. * And alternative way of selecting points is through dragging.
  28896. *
  28897. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
  28898. * Line
  28899. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
  28900. * Column
  28901. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
  28902. * Pie
  28903. * @sample {highcharts} highcharts/chart/events-selection-points/
  28904. * Select a range of points through a drag selection
  28905. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28906. * Map area
  28907. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  28908. * Map bubble
  28909. *
  28910. * @since 1.2.0
  28911. *
  28912. * @private
  28913. */
  28914. allowPointSelect: false,
  28915. /**
  28916. * When true, each point or column edge is rounded to its nearest pixel
  28917. * in order to render sharp on screen. In some cases, when there are a
  28918. * lot of densely packed columns, this leads to visible difference
  28919. * in column widths or distance between columns. In these cases,
  28920. * setting `crisp` to `false` may look better, even though each column
  28921. * is rendered blurry.
  28922. *
  28923. * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
  28924. * Crisp is false
  28925. *
  28926. * @since 5.0.10
  28927. * @product highcharts highstock gantt
  28928. *
  28929. * @private
  28930. */
  28931. crisp: true,
  28932. /**
  28933. * If true, a checkbox is displayed next to the legend item to allow
  28934. * selecting the series. The state of the checkbox is determined by
  28935. * the `selected` option.
  28936. *
  28937. * @productdesc {highmaps}
  28938. * Note that if a `colorAxis` is defined, the color axis is represented
  28939. * in the legend, not the series.
  28940. *
  28941. * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
  28942. * Show select box
  28943. *
  28944. * @since 1.2.0
  28945. *
  28946. * @private
  28947. */
  28948. showCheckbox: false,
  28949. /**
  28950. * Enable or disable the initial animation when a series is displayed.
  28951. * The animation can also be set as a configuration object. Please
  28952. * note that this option only applies to the initial animation of the
  28953. * series itself. For other animations, see [chart.animation](
  28954. * #chart.animation) and the animation parameter under the API methods.
  28955. * The following properties are supported:
  28956. *
  28957. * - `defer`: The animation delay time in milliseconds.
  28958. *
  28959. * - `duration`: The duration of the animation in milliseconds. (Defaults to
  28960. * `1000`)
  28961. *
  28962. * - `easing`: Can be a string reference to an easing function set on
  28963. * the `Math` object or a function. See the _Custom easing function_
  28964. * demo below. (Defaults to `easeInOutSine`)
  28965. *
  28966. * Due to poor performance, animation is disabled in old IE browsers
  28967. * for several chart types.
  28968. *
  28969. * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
  28970. * Animation disabled
  28971. * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
  28972. * Slower animation
  28973. * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
  28974. * Custom easing function
  28975. * @sample {highstock} stock/plotoptions/animation-slower/
  28976. * Slower animation
  28977. * @sample {highstock} stock/plotoptions/animation-easing/
  28978. * Custom easing function
  28979. * @sample {highmaps} maps/plotoptions/series-animation-true/
  28980. * Animation enabled on map series
  28981. * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
  28982. * Disabled on mapbubble series
  28983. *
  28984. * @type {boolean|Highcharts.AnimationOptionsObject}
  28985. * @default {highcharts} true
  28986. * @default {highstock} true
  28987. * @default {highmaps} false
  28988. *
  28989. * @private
  28990. */
  28991. animation: {
  28992. /** @ignore-option */
  28993. duration: 1000
  28994. },
  28995. /**
  28996. * An additional class name to apply to the series' graphical elements.
  28997. * This option does not replace default class names of the graphical
  28998. * element. Changes to the series' color will also be reflected in a
  28999. * chart's legend and tooltip.
  29000. *
  29001. * @sample {highcharts} highcharts/css/point-series-classname
  29002. *
  29003. * @type {string}
  29004. * @since 5.0.0
  29005. * @apioption plotOptions.series.className
  29006. */
  29007. /**
  29008. * Disable this option to allow series rendering in the whole plotting
  29009. * area.
  29010. *
  29011. * **Note:** Clipping should be always enabled when
  29012. * [chart.zoomType](#chart.zoomType) is set
  29013. *
  29014. * @sample {highcharts} highcharts/plotoptions/series-clip/
  29015. * Disabled clipping
  29016. *
  29017. * @default true
  29018. * @type {boolean}
  29019. * @since 3.0.0
  29020. * @apioption plotOptions.series.clip
  29021. */
  29022. /**
  29023. * The main color of the series. In line type series it applies to the
  29024. * line and the point markers unless otherwise specified. In bar type
  29025. * series it applies to the bars unless a color is specified per point.
  29026. * The default value is pulled from the `options.colors` array.
  29027. *
  29028. * In styled mode, the color can be defined by the
  29029. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  29030. * color can be set with the `.highcharts-series`,
  29031. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  29032. * `.highcharts-series-{n}` class, or individual classes given by the
  29033. * `className` option.
  29034. *
  29035. * @productdesc {highmaps}
  29036. * In maps, the series color is rarely used, as most choropleth maps use
  29037. * the color to denote the value of each point. The series color can
  29038. * however be used in a map with multiple series holding categorized
  29039. * data.
  29040. *
  29041. * @sample {highcharts} highcharts/plotoptions/series-color-general/
  29042. * General plot option
  29043. * @sample {highcharts} highcharts/plotoptions/series-color-specific/
  29044. * One specific series
  29045. * @sample {highcharts} highcharts/plotoptions/series-color-area/
  29046. * Area color
  29047. * @sample {highcharts} highcharts/series/infographic/
  29048. * Pattern fill
  29049. * @sample {highmaps} maps/demo/category-map/
  29050. * Category map by multiple series
  29051. *
  29052. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29053. * @apioption plotOptions.series.color
  29054. */
  29055. /**
  29056. * Styled mode only. A specific color index to use for the series, so its
  29057. * graphic representations are given the class name `highcharts-color-{n}`.
  29058. *
  29059. * Since v11, CSS variables on the form `--highcharts-color-{n}` make
  29060. * changing the color scheme very convenient.
  29061. *
  29062. * @sample {highcharts} highcharts/css/colorindex/ Series and point color
  29063. * index
  29064. *
  29065. * @type {number}
  29066. * @since 5.0.0
  29067. * @apioption plotOptions.series.colorIndex
  29068. */
  29069. /**
  29070. * Whether to connect a graph line across null points, or render a gap
  29071. * between the two points on either side of the null.
  29072. *
  29073. * In stacked area chart, if `connectNulls` is set to true,
  29074. * null points are interpreted as 0.
  29075. *
  29076. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
  29077. * False by default
  29078. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
  29079. * True
  29080. *
  29081. * @type {boolean}
  29082. * @default false
  29083. * @product highcharts highstock
  29084. * @apioption plotOptions.series.connectNulls
  29085. */
  29086. /**
  29087. * You can set the cursor to "pointer" if you have click events attached
  29088. * to the series, to signal to the user that the points and lines can
  29089. * be clicked.
  29090. *
  29091. * In styled mode, the series cursor can be set with the same classes
  29092. * as listed under [series.color](#plotOptions.series.color).
  29093. *
  29094. * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
  29095. * On line graph
  29096. * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
  29097. * On columns
  29098. * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
  29099. * On scatter markers
  29100. * @sample {highstock} stock/plotoptions/cursor/
  29101. * Pointer on a line graph
  29102. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  29103. * Map area
  29104. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  29105. * Map bubble
  29106. *
  29107. * @type {string|Highcharts.CursorValue}
  29108. * @apioption plotOptions.series.cursor
  29109. */
  29110. /**
  29111. * A reserved subspace to store options and values for customized
  29112. * functionality. Here you can add additional data for your own event
  29113. * callbacks and formatter callbacks.
  29114. *
  29115. * @sample {highcharts} highcharts/point/custom/
  29116. * Point and series with custom data
  29117. *
  29118. * @type {Highcharts.Dictionary<*>}
  29119. * @apioption plotOptions.series.custom
  29120. */
  29121. /**
  29122. * Name of the dash style to use for the graph, or for some series types
  29123. * the outline of each shape.
  29124. *
  29125. * In styled mode, the
  29126. * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
  29127. * can be set with the same classes as listed under
  29128. * [series.color](#plotOptions.series.color).
  29129. *
  29130. * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
  29131. * Possible values demonstrated
  29132. * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
  29133. * Chart suitable for printing in black and white
  29134. * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
  29135. * Possible values demonstrated
  29136. * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
  29137. * Possible values demonstrated
  29138. * @sample {highmaps} maps/plotoptions/series-dashstyle/
  29139. * Dotted borders on a map
  29140. *
  29141. * @type {Highcharts.DashStyleValue}
  29142. * @default Solid
  29143. * @since 2.1
  29144. * @apioption plotOptions.series.dashStyle
  29145. */
  29146. /**
  29147. * A description of the series to add to the screen reader information
  29148. * about the series.
  29149. *
  29150. * @type {string}
  29151. * @since 5.0.0
  29152. * @requires modules/accessibility
  29153. * @apioption plotOptions.series.description
  29154. */
  29155. /**
  29156. * Options for the series data sorting.
  29157. *
  29158. * @type {Highcharts.DataSortingOptionsObject}
  29159. * @since 8.0.0
  29160. * @product highcharts highstock
  29161. * @apioption plotOptions.series.dataSorting
  29162. */
  29163. /**
  29164. * Enable or disable data sorting for the series. Use [xAxis.reversed](
  29165. * #xAxis.reversed) to change the sorting order.
  29166. *
  29167. * @sample {highcharts} highcharts/datasorting/animation/
  29168. * Data sorting in scatter-3d
  29169. * @sample {highcharts} highcharts/datasorting/labels-animation/
  29170. * Axis labels animation
  29171. * @sample {highcharts} highcharts/datasorting/dependent-sorting/
  29172. * Dependent series sorting
  29173. * @sample {highcharts} highcharts/datasorting/independent-sorting/
  29174. * Independent series sorting
  29175. *
  29176. * @type {boolean}
  29177. * @since 8.0.0
  29178. * @apioption plotOptions.series.dataSorting.enabled
  29179. */
  29180. /**
  29181. * Whether to allow matching points by name in an update. If this option
  29182. * is disabled, points will be matched by order.
  29183. *
  29184. * @sample {highcharts} highcharts/datasorting/match-by-name/
  29185. * Enabled match by name
  29186. *
  29187. * @type {boolean}
  29188. * @since 8.0.0
  29189. * @apioption plotOptions.series.dataSorting.matchByName
  29190. */
  29191. /**
  29192. * Determines what data value should be used to sort by.
  29193. *
  29194. * @sample {highcharts} highcharts/datasorting/sort-key/
  29195. * Sort key as `z` value
  29196. *
  29197. * @type {string}
  29198. * @since 8.0.0
  29199. * @default y
  29200. * @apioption plotOptions.series.dataSorting.sortKey
  29201. */
  29202. /**
  29203. * Enable or disable the mouse tracking for a specific series. This
  29204. * includes point tooltips and click events on graphs and points. For
  29205. * large datasets it improves performance.
  29206. *
  29207. * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
  29208. * No mouse tracking
  29209. * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
  29210. * No mouse tracking
  29211. *
  29212. * @type {boolean}
  29213. * @default true
  29214. * @apioption plotOptions.series.enableMouseTracking
  29215. */
  29216. enableMouseTracking: true,
  29217. /**
  29218. * Whether to use the Y extremes of the total chart width or only the
  29219. * zoomed area when zooming in on parts of the X axis. By default, the
  29220. * Y axis adjusts to the min and max of the visible data. Cartesian
  29221. * series only.
  29222. *
  29223. * @type {boolean}
  29224. * @default false
  29225. * @since 4.1.6
  29226. * @product highcharts highstock gantt
  29227. * @apioption plotOptions.series.getExtremesFromAll
  29228. */
  29229. /**
  29230. * An array specifying which option maps to which key in the data point
  29231. * array. This makes it convenient to work with unstructured data arrays
  29232. * from different sources.
  29233. *
  29234. * @see [series.data](#series.line.data)
  29235. *
  29236. * @sample {highcharts|highstock} highcharts/series/data-keys/
  29237. * An extended data array with keys
  29238. * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
  29239. * Nested keys used to access object properties
  29240. *
  29241. * @type {Array<string>}
  29242. * @since 4.1.6
  29243. * @apioption plotOptions.series.keys
  29244. */
  29245. /**
  29246. * The line cap used for line ends and line joins on the graph.
  29247. *
  29248. * @sample highcharts/series-line/linecap/
  29249. * Line cap comparison
  29250. *
  29251. * @type {Highcharts.SeriesLinecapValue}
  29252. * @default round
  29253. * @product highcharts highstock
  29254. * @apioption plotOptions.series.linecap
  29255. */
  29256. /**
  29257. * The [id](#series.id) of another series to link to. Additionally,
  29258. * the value can be ":previous" to link to the previous series. When
  29259. * two series are linked, only the first one appears in the legend.
  29260. * Toggling the visibility of this also toggles the linked series.
  29261. *
  29262. * If master series uses data sorting and linked series does not have
  29263. * its own sorting definition, the linked series will be sorted in the
  29264. * same order as the master one.
  29265. *
  29266. * @sample {highcharts|highstock} highcharts/demo/arearange-line/
  29267. * Linked series
  29268. *
  29269. * @type {string}
  29270. * @since 3.0
  29271. * @product highcharts highstock gantt
  29272. * @apioption plotOptions.series.linkedTo
  29273. */
  29274. /**
  29275. * Options for the corresponding navigator series if `showInNavigator`
  29276. * is `true` for this series. Available options are the same as any
  29277. * series, documented at [plotOptions](#plotOptions.series) and
  29278. * [series](#series).
  29279. *
  29280. * These options are merged with options in [navigator.series](
  29281. * #navigator.series), and will take precedence if the same option is
  29282. * defined both places.
  29283. *
  29284. * @see [navigator.series](#navigator.series)
  29285. *
  29286. * @type {Highcharts.PlotSeriesOptions}
  29287. * @since 5.0.0
  29288. * @product highstock
  29289. * @apioption plotOptions.series.navigatorOptions
  29290. */
  29291. /**
  29292. * The color for the parts of the graph or points that are below the
  29293. * [threshold](#plotOptions.series.threshold). Note that `zones` takes
  29294. * precedence over the negative color. Using `negativeColor` is
  29295. * equivalent to applying a zone with value of 0.
  29296. *
  29297. * @see In styled mode, a negative color is applied by setting this option
  29298. * to `true` combined with the `.highcharts-negative` class name.
  29299. *
  29300. * @sample {highcharts} highcharts/plotoptions/series-negative-color/
  29301. * Spline, area and column
  29302. * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
  29303. * Arearange
  29304. * @sample {highcharts} highcharts/css/series-negative-color/
  29305. * Styled mode
  29306. * @sample {highstock} highcharts/plotoptions/series-negative-color/
  29307. * Spline, area and column
  29308. * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
  29309. * Arearange
  29310. * @sample {highmaps} highcharts/plotoptions/series-negative-color/
  29311. * Spline, area and column
  29312. * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
  29313. * Arearange
  29314. *
  29315. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29316. * @since 3.0
  29317. * @apioption plotOptions.series.negativeColor
  29318. */
  29319. /**
  29320. * Same as
  29321. * [accessibility.point.descriptionFormat](#accessibility.point.descriptionFormat),
  29322. * but for an individual series. Overrides the chart wide configuration.
  29323. *
  29324. * @type {Function}
  29325. * @since 11.1.0
  29326. * @apioption plotOptions.series.pointDescriptionFormat
  29327. */
  29328. /**
  29329. * Same as
  29330. * [accessibility.series.descriptionFormatter](#accessibility.series.descriptionFormatter),
  29331. * but for an individual series. Overrides the chart wide configuration.
  29332. *
  29333. * @type {Function}
  29334. * @since 5.0.12
  29335. * @apioption plotOptions.series.pointDescriptionFormatter
  29336. */
  29337. /**
  29338. * If no x values are given for the points in a series, `pointInterval`
  29339. * defines the interval of the x values. For example, if a series
  29340. * contains one value every decade starting from year 0, set
  29341. * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
  29342. * is set in milliseconds.
  29343. *
  29344. * It can be also be combined with `pointIntervalUnit` to draw irregular
  29345. * time intervals.
  29346. *
  29347. * If combined with `relativeXValue`, an x value can be set on each
  29348. * point, and the `pointInterval` is added x times to the `pointStart`
  29349. * setting.
  29350. *
  29351. * Please note that this options applies to the _series data_, not the
  29352. * interval of the axis ticks, which is independent.
  29353. *
  29354. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29355. * Datetime X axis
  29356. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29357. * Relative x value
  29358. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29359. * Using pointStart and pointInterval
  29360. * @sample {highstock} stock/plotoptions/relativexvalue/
  29361. * Relative x value
  29362. *
  29363. * @type {number}
  29364. * @default 1
  29365. * @product highcharts highstock gantt
  29366. * @apioption plotOptions.series.pointInterval
  29367. */
  29368. /**
  29369. * On datetime series, this allows for setting the
  29370. * [pointInterval](#plotOptions.series.pointInterval) to irregular time
  29371. * units, `day`, `month` and `year`. A day is usually the same as 24
  29372. * hours, but `pointIntervalUnit` also takes the DST crossover into
  29373. * consideration when dealing with local time. Combine this option with
  29374. * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
  29375. *
  29376. * Please note that this options applies to the _series data_, not the
  29377. * interval of the axis ticks, which is independent.
  29378. *
  29379. * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
  29380. * One point a month
  29381. * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
  29382. * One point a month
  29383. *
  29384. * @type {string}
  29385. * @since 4.1.0
  29386. * @product highcharts highstock gantt
  29387. * @validvalue ["day", "month", "year"]
  29388. * @apioption plotOptions.series.pointIntervalUnit
  29389. */
  29390. /**
  29391. * Possible values: `"on"`, `"between"`, `number`.
  29392. *
  29393. * In a column chart, when pointPlacement is `"on"`, the point will not
  29394. * create any padding of the X axis. In a polar column chart this means
  29395. * that the first column points directly north. If the pointPlacement is
  29396. * `"between"`, the columns will be laid out between ticks. This is
  29397. * useful for example for visualising an amount between two points in
  29398. * time or in a certain sector of a polar chart.
  29399. *
  29400. * Since Highcharts 3.0.2, the point placement can also be numeric,
  29401. * where 0 is on the axis value, -0.5 is between this value and the
  29402. * previous, and 0.5 is between this value and the next. Unlike the
  29403. * textual options, numeric point placement options won't affect axis
  29404. * padding.
  29405. *
  29406. * Note that pointPlacement needs a [pointRange](
  29407. * #plotOptions.series.pointRange) to work. For column series this is
  29408. * computed, but for line-type series it needs to be set.
  29409. *
  29410. * For the `xrange` series type and gantt charts, if the Y axis is a
  29411. * category axis, the `pointPlacement` applies to the Y axis rather than
  29412. * the (typically datetime) X axis.
  29413. *
  29414. * Defaults to `undefined` in cartesian charts, `"between"` in polar
  29415. * charts.
  29416. *
  29417. * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
  29418. *
  29419. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
  29420. * Between in a column chart
  29421. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
  29422. * Numeric placement for custom layout
  29423. * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
  29424. * Placement in heatmap
  29425. *
  29426. * @type {string|number}
  29427. * @since 2.3.0
  29428. * @product highcharts highstock gantt
  29429. * @apioption plotOptions.series.pointPlacement
  29430. */
  29431. /**
  29432. * If no x values are given for the points in a series, pointStart
  29433. * defines on what value to start. For example, if a series contains one
  29434. * yearly value starting from 1945, set pointStart to 1945.
  29435. *
  29436. * If combined with `relativeXValue`, an x value can be set on each
  29437. * point. The x value from the point options is multiplied by
  29438. * `pointInterval` and added to `pointStart` to produce a modified x
  29439. * value.
  29440. *
  29441. * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
  29442. * Linear
  29443. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29444. * Datetime
  29445. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29446. * Relative x value
  29447. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29448. * Using pointStart and pointInterval
  29449. * @sample {highstock} stock/plotoptions/relativexvalue/
  29450. * Relative x value
  29451. *
  29452. * @type {number}
  29453. * @default 0
  29454. * @product highcharts highstock gantt
  29455. * @apioption plotOptions.series.pointStart
  29456. */
  29457. /**
  29458. * When true, X values in the data set are relative to the current
  29459. * `pointStart`, `pointInterval` and `pointIntervalUnit` settings. This
  29460. * allows compression of the data for datasets with irregular X values.
  29461. *
  29462. * The real X values are computed on the formula `f(x) = ax + b`, where
  29463. * `a` is the `pointInterval` (optionally with a time unit given by
  29464. * `pointIntervalUnit`), and `b` is the `pointStart`.
  29465. *
  29466. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29467. * Relative X value
  29468. * @sample {highstock} stock/plotoptions/relativexvalue/
  29469. * Relative X value
  29470. *
  29471. * @type {boolean}
  29472. * @default false
  29473. * @product highcharts highstock
  29474. * @apioption plotOptions.series.relativeXValue
  29475. */
  29476. /**
  29477. * Whether to select the series initially. If `showCheckbox` is true,
  29478. * the checkbox next to the series name in the legend will be checked
  29479. * for a selected series.
  29480. *
  29481. * @sample {highcharts} highcharts/plotoptions/series-selected/
  29482. * One out of two series selected
  29483. *
  29484. * @type {boolean}
  29485. * @default false
  29486. * @since 1.2.0
  29487. * @apioption plotOptions.series.selected
  29488. */
  29489. /**
  29490. * Whether to apply a drop shadow to the graph line. Since 2.3 the
  29491. * shadow can be an object configuration containing `color`, `offsetX`,
  29492. * `offsetY`, `opacity` and `width`.
  29493. *
  29494. * Note that in some cases, like stacked columns or other dense layouts, the
  29495. * series may cast shadows on each other. In that case, the
  29496. * `chart.seriesGroupShadow` allows applying a common drop shadow to the
  29497. * whole series group.
  29498. *
  29499. * @sample {highcharts} highcharts/plotoptions/series-shadow/
  29500. * Shadow enabled
  29501. *
  29502. * @type {boolean|Highcharts.ShadowOptionsObject}
  29503. * @default false
  29504. * @apioption plotOptions.series.shadow
  29505. */
  29506. /**
  29507. * Whether to display this particular series or series type in the
  29508. * legend. Standalone series are shown in legend by default, and linked
  29509. * series are not. Since v7.2.0 it is possible to show series that use
  29510. * colorAxis by setting this option to `true`.
  29511. *
  29512. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  29513. * One series in the legend, one hidden
  29514. *
  29515. * @type {boolean}
  29516. * @apioption plotOptions.series.showInLegend
  29517. */
  29518. /**
  29519. * Whether or not to show the series in the navigator. Takes precedence
  29520. * over [navigator.baseSeries](#navigator.baseSeries) if defined.
  29521. *
  29522. * @type {boolean}
  29523. * @since 5.0.0
  29524. * @product highstock
  29525. * @apioption plotOptions.series.showInNavigator
  29526. */
  29527. /**
  29528. * If set to `true`, the accessibility module will skip past the points
  29529. * in this series for keyboard navigation.
  29530. *
  29531. * @type {boolean}
  29532. * @since 5.0.12
  29533. * @apioption plotOptions.series.skipKeyboardNavigation
  29534. */
  29535. /**
  29536. * Whether to stack the values of each series on top of each other.
  29537. * Possible values are `undefined` to disable, `"normal"` to stack by
  29538. * value or `"percent"`.
  29539. *
  29540. * When stacking is enabled, data must be sorted
  29541. * in ascending X order.
  29542. *
  29543. * Some stacking options are related to specific series types. In the
  29544. * streamgraph series type, the stacking option is set to `"stream"`.
  29545. * The second one is `"overlap"`, which only applies to waterfall
  29546. * series.
  29547. *
  29548. * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
  29549. *
  29550. * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
  29551. * Line
  29552. * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
  29553. * Column
  29554. * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
  29555. * Bar
  29556. * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
  29557. * Area
  29558. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
  29559. * Line
  29560. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
  29561. * Column
  29562. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
  29563. * Bar
  29564. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
  29565. * Area
  29566. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
  29567. * Waterfall with normal stacking
  29568. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
  29569. * Waterfall with overlap stacking
  29570. * @sample {highstock} stock/plotoptions/stacking/
  29571. * Area
  29572. *
  29573. * @type {string}
  29574. * @product highcharts highstock
  29575. * @validvalue ["normal", "overlap", "percent", "stream"]
  29576. * @apioption plotOptions.series.stacking
  29577. */
  29578. /**
  29579. * Whether to apply steps to the line. Possible values are `left`,
  29580. * `center` and `right`.
  29581. *
  29582. * @sample {highcharts} highcharts/plotoptions/line-step/
  29583. * Different step line options
  29584. * @sample {highcharts} highcharts/plotoptions/area-step/
  29585. * Stepped, stacked area
  29586. * @sample {highstock} stock/plotoptions/line-step/
  29587. * Step line
  29588. *
  29589. * @type {string}
  29590. * @since 1.2.5
  29591. * @product highcharts highstock
  29592. * @validvalue ["left", "center", "right"]
  29593. * @apioption plotOptions.series.step
  29594. */
  29595. /**
  29596. * The threshold, also called zero level or base level. For line type
  29597. * series this is only used in conjunction with
  29598. * [negativeColor](#plotOptions.series.negativeColor).
  29599. *
  29600. * @see [softThreshold](#plotOptions.series.softThreshold).
  29601. *
  29602. * @type {number|null}
  29603. * @default 0
  29604. * @since 3.0
  29605. * @product highcharts highstock
  29606. * @apioption plotOptions.series.threshold
  29607. */
  29608. /**
  29609. * Set the initial visibility of the series.
  29610. *
  29611. * @sample {highcharts} highcharts/plotoptions/series-visible/
  29612. * Two series, one hidden and one visible
  29613. * @sample {highstock} stock/plotoptions/series-visibility/
  29614. * Hidden series
  29615. *
  29616. * @type {boolean}
  29617. * @default true
  29618. * @apioption plotOptions.series.visible
  29619. */
  29620. /**
  29621. * Defines the Axis on which the zones are applied.
  29622. *
  29623. * @see [zones](#plotOptions.series.zones)
  29624. *
  29625. * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
  29626. * Zones on the X-Axis
  29627. * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
  29628. * Zones on the X-Axis
  29629. *
  29630. * @type {string}
  29631. * @default y
  29632. * @since 4.1.0
  29633. * @product highcharts highstock
  29634. * @apioption plotOptions.series.zoneAxis
  29635. */
  29636. /**
  29637. * General event handlers for the series items. These event hooks can
  29638. * also be attached to the series at run time using the
  29639. * `Highcharts.addEvent` function.
  29640. *
  29641. * @declare Highcharts.SeriesEventsOptionsObject
  29642. *
  29643. * @private
  29644. */
  29645. events: {},
  29646. /**
  29647. * Fires after the series has finished its initial animation, or in case
  29648. * animation is disabled, immediately as the series is displayed.
  29649. *
  29650. * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
  29651. * Show label after animate
  29652. * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
  29653. * Show label after animate
  29654. *
  29655. * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
  29656. * @since 4.0
  29657. * @product highcharts highstock gantt
  29658. * @context Highcharts.Series
  29659. * @apioption plotOptions.series.events.afterAnimate
  29660. */
  29661. /**
  29662. * Fires when the checkbox next to the series' name in the legend is
  29663. * clicked. One parameter, `event`, is passed to the function. The state
  29664. * of the checkbox is found by `event.checked`. The checked item is
  29665. * found by `event.item`. Return `false` to prevent the default action
  29666. * which is to toggle the select state of the series.
  29667. *
  29668. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  29669. * Alert checkbox status
  29670. *
  29671. * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
  29672. * @since 1.2.0
  29673. * @context Highcharts.Series
  29674. * @apioption plotOptions.series.events.checkboxClick
  29675. */
  29676. /**
  29677. * Fires when the series is clicked. One parameter, `event`, is passed
  29678. * to the function, containing common event information. Additionally,
  29679. * `event.point` holds a pointer to the nearest point on the graph.
  29680. *
  29681. * @sample {highcharts} highcharts/plotoptions/series-events-click/
  29682. * Alert click info
  29683. * @sample {highstock} stock/plotoptions/series-events-click/
  29684. * Alert click info
  29685. * @sample {highmaps} maps/plotoptions/series-events-click/
  29686. * Display click info in subtitle
  29687. *
  29688. * @type {Highcharts.SeriesClickCallbackFunction}
  29689. * @context Highcharts.Series
  29690. * @apioption plotOptions.series.events.click
  29691. */
  29692. /**
  29693. * Fires when the series is hidden after chart generation time, either
  29694. * by clicking the legend item or by calling `.hide()`.
  29695. *
  29696. * @sample {highcharts} highcharts/plotoptions/series-events-hide/
  29697. * Alert when the series is hidden by clicking the legend item
  29698. *
  29699. * @type {Highcharts.SeriesHideCallbackFunction}
  29700. * @since 1.2.0
  29701. * @context Highcharts.Series
  29702. * @apioption plotOptions.series.events.hide
  29703. */
  29704. /**
  29705. * Fires when the legend item belonging to the series is clicked. One
  29706. * parameter, `event`, is passed to the function. The default action
  29707. * is to toggle the visibility of the series. This can be prevented
  29708. * by returning `false` or calling `event.preventDefault()`.
  29709. *
  29710. * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
  29711. * Confirm hiding and showing
  29712. *
  29713. * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
  29714. * @context Highcharts.Series
  29715. * @apioption plotOptions.series.events.legendItemClick
  29716. */
  29717. /**
  29718. * Fires when the mouse leaves the graph. One parameter, `event`, is
  29719. * passed to the function, containing common event information. If the
  29720. * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
  29721. * doesn't happen before the mouse enters another graph or leaves the
  29722. * plot area.
  29723. *
  29724. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29725. * With sticky tracking by default
  29726. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29727. * Without sticky tracking
  29728. *
  29729. * @type {Highcharts.SeriesMouseOutCallbackFunction}
  29730. * @context Highcharts.Series
  29731. * @apioption plotOptions.series.events.mouseOut
  29732. */
  29733. /**
  29734. * Fires when the mouse enters the graph. One parameter, `event`, is
  29735. * passed to the function, containing common event information.
  29736. *
  29737. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29738. * With sticky tracking by default
  29739. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29740. * Without sticky tracking
  29741. *
  29742. * @type {Highcharts.SeriesMouseOverCallbackFunction}
  29743. * @context Highcharts.Series
  29744. * @apioption plotOptions.series.events.mouseOver
  29745. */
  29746. /**
  29747. * Fires when the series is shown after chart generation time, either
  29748. * by clicking the legend item or by calling `.show()`.
  29749. *
  29750. * @sample {highcharts} highcharts/plotoptions/series-events-show/
  29751. * Alert when the series is shown by clicking the legend item.
  29752. *
  29753. * @type {Highcharts.SeriesShowCallbackFunction}
  29754. * @since 1.2.0
  29755. * @context Highcharts.Series
  29756. * @apioption plotOptions.series.events.show
  29757. */
  29758. /**
  29759. * Options for the point markers of line and scatter-like series. Properties
  29760. * like `fillColor`, `lineColor` and `lineWidth` define the visual
  29761. * appearance of the markers. The `symbol` option defines the shape. Other
  29762. * series types, like column series, don't have markers, but have visual
  29763. * options on the series level instead.
  29764. *
  29765. * In styled mode, the markers can be styled with the `.highcharts-point`,
  29766. * `.highcharts-point-hover` and `.highcharts-point-select` class names.
  29767. *
  29768. * @declare Highcharts.PointMarkerOptionsObject
  29769. *
  29770. * @sample {highmaps} maps/demo/mappoint-mapmarker
  29771. * Using the mapmarker symbol for points
  29772. *
  29773. * @private
  29774. */
  29775. marker: {
  29776. /**
  29777. * Enable or disable the point marker. If `undefined`, the markers
  29778. * are hidden when the data is dense, and shown for more widespread
  29779. * data points.
  29780. *
  29781. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
  29782. * Disabled markers
  29783. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
  29784. * Disabled in normal state but enabled on hover
  29785. * @sample {highstock} stock/plotoptions/series-marker/
  29786. * Enabled markers
  29787. *
  29788. * @type {boolean}
  29789. * @default {highcharts} undefined
  29790. * @default {highstock} false
  29791. * @apioption plotOptions.series.marker.enabled
  29792. */
  29793. /**
  29794. * The threshold for how dense the point markers should be before
  29795. * they are hidden, given that `enabled` is not defined. The number
  29796. * indicates the horizontal distance between the two closest points
  29797. * in the series, as multiples of the `marker.radius`. In other
  29798. * words, the default value of 2 means points are hidden if
  29799. * overlapping horizontally.
  29800. *
  29801. * @sample highcharts/plotoptions/series-marker-enabledthreshold
  29802. * A higher threshold
  29803. *
  29804. * @since 6.0.5
  29805. */
  29806. enabledThreshold: 2,
  29807. /**
  29808. * The fill color of the point marker. When `undefined`, the series'
  29809. * or point's color is used.
  29810. *
  29811. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29812. * White fill
  29813. *
  29814. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29815. * @apioption plotOptions.series.marker.fillColor
  29816. */
  29817. /**
  29818. * Image markers only. Set the image width explicitly. When using
  29819. * this option, a `width` must also be set.
  29820. *
  29821. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29822. * Fixed width and height
  29823. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29824. * Fixed width and height
  29825. *
  29826. * @type {number}
  29827. * @since 4.0.4
  29828. * @apioption plotOptions.series.marker.height
  29829. */
  29830. /**
  29831. * The color of the point marker's outline. When `undefined`, the
  29832. * series' or point's color is used.
  29833. *
  29834. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29835. * Inherit from series color (undefined)
  29836. *
  29837. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29838. */
  29839. lineColor: "#ffffff" /* Palette.backgroundColor */,
  29840. /**
  29841. * The width of the point marker's outline.
  29842. *
  29843. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29844. * 2px blue marker
  29845. */
  29846. lineWidth: 0,
  29847. /**
  29848. * The radius of the point marker.
  29849. *
  29850. * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
  29851. * Bigger markers
  29852. *
  29853. * @default {highstock} 2
  29854. * @default {highcharts} 4
  29855. *
  29856. */
  29857. radius: 4,
  29858. /**
  29859. * A predefined shape or symbol for the marker. When undefined, the
  29860. * symbol is pulled from options.symbols. Other possible values are
  29861. * `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
  29862. * `'triangle-down'`.
  29863. *
  29864. * Additionally, the URL to a graphic can be given on this form:
  29865. * `'url(graphic.png)'`. Note that for the image to be applied to
  29866. * exported charts, its URL needs to be accessible by the export
  29867. * server.
  29868. *
  29869. * Custom callbacks for symbol path generation can also be added to
  29870. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  29871. * used by its method name, as shown in the demo.
  29872. *
  29873. * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
  29874. * Predefined, graphic and custom markers
  29875. * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
  29876. * Predefined, graphic and custom markers
  29877. * @sample {highmaps} maps/demo/mappoint-mapmarker
  29878. * Using the mapmarker symbol for points
  29879. *
  29880. * @type {string}
  29881. * @apioption plotOptions.series.marker.symbol
  29882. */
  29883. /**
  29884. * Image markers only. Set the image width explicitly. When using
  29885. * this option, a `height` must also be set.
  29886. *
  29887. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29888. * Fixed width and height
  29889. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29890. * Fixed width and height
  29891. *
  29892. * @type {number}
  29893. * @since 4.0.4
  29894. * @apioption plotOptions.series.marker.width
  29895. */
  29896. /**
  29897. * States for a single point marker.
  29898. *
  29899. * @declare Highcharts.PointStatesOptionsObject
  29900. */
  29901. states: {
  29902. /**
  29903. * The normal state of a single point marker. Currently only
  29904. * used for setting animation when returning to normal state
  29905. * from hover.
  29906. *
  29907. * @declare Highcharts.PointStatesNormalOptionsObject
  29908. */
  29909. normal: {
  29910. /**
  29911. * Animation when returning to normal state after hovering.
  29912. *
  29913. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  29914. */
  29915. animation: true
  29916. },
  29917. /**
  29918. * The hover state for a single point marker.
  29919. *
  29920. * @declare Highcharts.PointStatesHoverOptionsObject
  29921. */
  29922. hover: {
  29923. /**
  29924. * Animation when hovering over the marker.
  29925. *
  29926. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  29927. */
  29928. animation: {
  29929. /** @internal */
  29930. duration: 150
  29931. },
  29932. /**
  29933. * Enable or disable the point marker.
  29934. *
  29935. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
  29936. * Disabled hover state
  29937. */
  29938. enabled: true,
  29939. /**
  29940. * The fill color of the marker in hover state. When
  29941. * `undefined`, the series' or point's fillColor for normal
  29942. * state is used.
  29943. *
  29944. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29945. * @apioption plotOptions.series.marker.states.hover.fillColor
  29946. */
  29947. /**
  29948. * The color of the point marker's outline. When
  29949. * `undefined`, the series' or point's lineColor for normal
  29950. * state is used.
  29951. *
  29952. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
  29953. * White fill color, black line color
  29954. *
  29955. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29956. * @apioption plotOptions.series.marker.states.hover.lineColor
  29957. */
  29958. /**
  29959. * The width of the point marker's outline. When
  29960. * `undefined`, the series' or point's lineWidth for normal
  29961. * state is used.
  29962. *
  29963. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
  29964. * 3px line width
  29965. *
  29966. * @type {number}
  29967. * @apioption plotOptions.series.marker.states.hover.lineWidth
  29968. */
  29969. /**
  29970. * The radius of the point marker. In hover state, it
  29971. * defaults to the normal state's radius + 2 as per the
  29972. * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
  29973. * option.
  29974. *
  29975. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
  29976. * 10px radius
  29977. *
  29978. * @type {number}
  29979. * @apioption plotOptions.series.marker.states.hover.radius
  29980. */
  29981. /**
  29982. * The number of pixels to increase the radius of the
  29983. * hovered point.
  29984. *
  29985. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29986. * 5 pixels greater radius on hover
  29987. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29988. * 5 pixels greater radius on hover
  29989. *
  29990. * @since 4.0.3
  29991. */
  29992. radiusPlus: 2,
  29993. /**
  29994. * The additional line width for a hovered point.
  29995. *
  29996. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29997. * 2 pixels wider on hover
  29998. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29999. * 2 pixels wider on hover
  30000. *
  30001. * @since 4.0.3
  30002. */
  30003. lineWidthPlus: 1
  30004. },
  30005. /**
  30006. * The appearance of the point marker when selected. In order to
  30007. * allow a point to be selected, set the
  30008. * `series.allowPointSelect` option to true.
  30009. *
  30010. * @declare Highcharts.PointStatesSelectOptionsObject
  30011. */
  30012. select: {
  30013. /**
  30014. * Enable or disable visible feedback for selection.
  30015. *
  30016. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
  30017. * Disabled select state
  30018. *
  30019. * @type {boolean}
  30020. * @default true
  30021. * @apioption plotOptions.series.marker.states.select.enabled
  30022. */
  30023. /**
  30024. * The radius of the point marker. In hover state, it
  30025. * defaults to the normal state's radius + 2.
  30026. *
  30027. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
  30028. * 10px radius for selected points
  30029. *
  30030. * @type {number}
  30031. * @apioption plotOptions.series.marker.states.select.radius
  30032. */
  30033. /**
  30034. * The fill color of the point marker.
  30035. *
  30036. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
  30037. * Solid red discs for selected points
  30038. *
  30039. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30040. */
  30041. fillColor: "#cccccc" /* Palette.neutralColor20 */,
  30042. /**
  30043. * The color of the point marker's outline. When
  30044. * `undefined`, the series' or point's color is used.
  30045. *
  30046. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
  30047. * Red line color for selected points
  30048. *
  30049. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30050. */
  30051. lineColor: "#000000" /* Palette.neutralColor100 */,
  30052. /**
  30053. * The width of the point marker's outline.
  30054. *
  30055. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
  30056. * 3px line width for selected points
  30057. */
  30058. lineWidth: 2
  30059. }
  30060. }
  30061. },
  30062. /**
  30063. * Properties for each single point.
  30064. *
  30065. * @declare Highcharts.PlotSeriesPointOptions
  30066. *
  30067. * @private
  30068. */
  30069. point: {
  30070. /**
  30071. * Fires when a point is clicked. One parameter, `event`, is passed
  30072. * to the function, containing common event information.
  30073. *
  30074. * If the `series.allowPointSelect` option is true, the default
  30075. * action for the point's click event is to toggle the point's
  30076. * select state. Returning `false` cancels this action.
  30077. *
  30078. * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
  30079. * Click marker to alert values
  30080. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
  30081. * Click column
  30082. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
  30083. * Go to URL
  30084. * @sample {highmaps} maps/plotoptions/series-point-events-click/
  30085. * Click marker to display values
  30086. * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
  30087. * Go to URL
  30088. *
  30089. * @type {Highcharts.PointClickCallbackFunction}
  30090. * @context Highcharts.Point
  30091. * @apioption plotOptions.series.point.events.click
  30092. */
  30093. /**
  30094. * Fires when the mouse leaves the area close to the point. One
  30095. * parameter, `event`, is passed to the function, containing common
  30096. * event information.
  30097. *
  30098. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  30099. * Show values in the chart's corner on mouse over
  30100. *
  30101. * @type {Highcharts.PointMouseOutCallbackFunction}
  30102. * @context Highcharts.Point
  30103. * @apioption plotOptions.series.point.events.mouseOut
  30104. */
  30105. /**
  30106. * Fires when the mouse enters the area close to the point. One
  30107. * parameter, `event`, is passed to the function, containing common
  30108. * event information.
  30109. *
  30110. * Returning `false` cancels the default behavior, which is to show a
  30111. * tooltip for the point.
  30112. *
  30113. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  30114. * Show values in the chart's corner on mouse over
  30115. *
  30116. * @type {Highcharts.PointMouseOverCallbackFunction}
  30117. * @context Highcharts.Point
  30118. * @apioption plotOptions.series.point.events.mouseOver
  30119. */
  30120. /**
  30121. * Fires when the point is removed using the `.remove()` method. One
  30122. * parameter, `event`, is passed to the function. Returning `false`
  30123. * cancels the operation.
  30124. *
  30125. * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
  30126. * Remove point and confirm
  30127. *
  30128. * @type {Highcharts.PointRemoveCallbackFunction}
  30129. * @since 1.2.0
  30130. * @context Highcharts.Point
  30131. * @apioption plotOptions.series.point.events.remove
  30132. */
  30133. /**
  30134. * Fires when the point is selected either programmatically or
  30135. * following a click on the point. One parameter, `event`, is passed
  30136. * to the function. Returning `false` cancels the operation.
  30137. *
  30138. * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
  30139. * Report the last selected point
  30140. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30141. * Report select and unselect
  30142. *
  30143. * @type {Highcharts.PointSelectCallbackFunction}
  30144. * @since 1.2.0
  30145. * @context Highcharts.Point
  30146. * @apioption plotOptions.series.point.events.select
  30147. */
  30148. /**
  30149. * Fires when the point is unselected either programmatically or
  30150. * following a click on the point. One parameter, `event`, is passed
  30151. * to the function.
  30152. * Returning `false` cancels the operation.
  30153. *
  30154. * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
  30155. * Report the last unselected point
  30156. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30157. * Report select and unselect
  30158. *
  30159. * @type {Highcharts.PointUnselectCallbackFunction}
  30160. * @since 1.2.0
  30161. * @context Highcharts.Point
  30162. * @apioption plotOptions.series.point.events.unselect
  30163. */
  30164. /**
  30165. * Fires when the point is updated programmatically through the
  30166. * `.update()` method. One parameter, `event`, is passed to the
  30167. * function. The new point options can be accessed through
  30168. * `event.options`. Returning `false` cancels the operation.
  30169. *
  30170. * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
  30171. * Confirm point updating
  30172. *
  30173. * @type {Highcharts.PointUpdateCallbackFunction}
  30174. * @since 1.2.0
  30175. * @context Highcharts.Point
  30176. * @apioption plotOptions.series.point.events.update
  30177. */
  30178. /**
  30179. * Events for each single point.
  30180. *
  30181. * @declare Highcharts.PointEventsOptionsObject
  30182. */
  30183. events: {}
  30184. },
  30185. /**
  30186. * Options for the series data labels, appearing next to each data
  30187. * point.
  30188. *
  30189. * Since v6.2.0, multiple data labels can be applied to each single
  30190. * point by defining them as an array of configs.
  30191. *
  30192. * In styled mode, the data labels can be styled with the
  30193. * `.highcharts-data-label-box` and `.highcharts-data-label` class names
  30194. * ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
  30195. *
  30196. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
  30197. * Data labels enabled
  30198. * @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
  30199. * Multiple data labels on a bar series
  30200. * @sample {highcharts} highcharts/css/series-datalabels
  30201. * Styled mode example
  30202. * @sample {highmaps} maps/demo/color-axis
  30203. * Choropleth map with data labels
  30204. * @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
  30205. * Using data labels as map markers
  30206. *
  30207. * @type {*|Array<*>}
  30208. * @product highcharts highstock highmaps gantt
  30209. *
  30210. * @private
  30211. */
  30212. dataLabels: {
  30213. /**
  30214. * Enable or disable the initial animation when a series is displayed
  30215. * for the `dataLabels`. The animation can also be set as a
  30216. * configuration object. Please note that this option only applies to
  30217. * the initial animation.
  30218. *
  30219. * For other animations, see [chart.animation](#chart.animation) and the
  30220. * animation parameter under the API methods. The following properties
  30221. * are supported:
  30222. *
  30223. * - `defer`: The animation delay time in milliseconds.
  30224. *
  30225. * @sample {highcharts} highcharts/plotoptions/animation-defer/
  30226. * Animation defer settings
  30227. *
  30228. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30229. * @since 8.2.0
  30230. * @apioption plotOptions.series.dataLabels.animation
  30231. */
  30232. animation: {},
  30233. /**
  30234. * The animation delay time in milliseconds. Set to `0` to render the
  30235. * data labels immediately. As `undefined` inherits defer time from the
  30236. * [series.animation.defer](#plotOptions.series.animation.defer).
  30237. *
  30238. * @type {number}
  30239. * @since 8.2.0
  30240. * @apioption plotOptions.series.dataLabels.animation.defer
  30241. */
  30242. /**
  30243. * The alignment of the data label compared to the point. If `right`,
  30244. * the right side of the label should be touching the point. For points
  30245. * with an extent, like columns, the alignments also dictates how to
  30246. * align it inside the box, as given with the
  30247. * [inside](#plotOptions.column.dataLabels.inside) option. Can be one of
  30248. * `left`, `center` or `right`.
  30249. *
  30250. * @sample {highcharts}
  30251. * highcharts/plotoptions/series-datalabels-align-left/ Left
  30252. * aligned
  30253. * @sample {highcharts}
  30254. * highcharts/plotoptions/bar-datalabels-align-inside-bar/ Data
  30255. * labels inside the bar
  30256. *
  30257. * @type {Highcharts.AlignValue|null}
  30258. */
  30259. align: 'center',
  30260. /**
  30261. * Whether to allow data labels to overlap. To make the labels less
  30262. * sensitive for overlapping, the
  30263. * [dataLabels.padding](#plotOptions.series.dataLabels.padding)
  30264. * can be set to 0.
  30265. *
  30266. * @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
  30267. * Don't allow overlap
  30268. *
  30269. * @type {boolean}
  30270. * @default false
  30271. * @since 4.1.0
  30272. * @apioption plotOptions.series.dataLabels.allowOverlap
  30273. */
  30274. /**
  30275. * The background color or gradient for the data label. Setting it to
  30276. * `auto` will use the point's color.
  30277. *
  30278. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30279. * Data labels box options
  30280. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30281. * Data labels box options
  30282. * @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
  30283. * Data labels as map markers
  30284. *
  30285. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30286. * @since 2.2.1
  30287. * @apioption plotOptions.series.dataLabels.backgroundColor
  30288. */
  30289. /**
  30290. * The border color for the data label. Setting it to `auto` will use
  30291. * the point's color. Defaults to `undefined`.
  30292. *
  30293. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30294. * Data labels box options
  30295. *
  30296. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30297. * @since 2.2.1
  30298. * @apioption plotOptions.series.dataLabels.borderColor
  30299. */
  30300. /**
  30301. * The border radius in pixels for the data label.
  30302. *
  30303. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30304. * Data labels box options
  30305. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30306. * Data labels box options
  30307. *
  30308. * @type {number}
  30309. * @default 0
  30310. * @since 2.2.1
  30311. * @apioption plotOptions.series.dataLabels.borderRadius
  30312. */
  30313. /**
  30314. * The border width in pixels for the data label.
  30315. *
  30316. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30317. * Data labels box options
  30318. *
  30319. * @type {number}
  30320. * @default 0
  30321. * @since 2.2.1
  30322. * @apioption plotOptions.series.dataLabels.borderWidth
  30323. */
  30324. borderWidth: 0,
  30325. /**
  30326. * A class name for the data label. Particularly in styled mode,
  30327. * this can be used to give each series' or point's data label
  30328. * unique styling. In addition to this option, a default color class
  30329. * name is added so that we can give the labels a contrast text
  30330. * shadow.
  30331. *
  30332. * @sample {highcharts} highcharts/css/data-label-contrast/
  30333. * Contrast text shadow
  30334. * @sample {highcharts} highcharts/css/series-datalabels/
  30335. * Styling by CSS
  30336. *
  30337. * @type {string}
  30338. * @since 5.0.0
  30339. * @apioption plotOptions.series.dataLabels.className
  30340. */
  30341. /**
  30342. * This options is deprecated.
  30343. * Use [style.color](#plotOptions.series.dataLabels.style) instead.
  30344. *
  30345. * The text color for the data labels. Defaults to `undefined`. For
  30346. * certain series types, like column or map, the data labels can be
  30347. * drawn inside the points. In this case the data label will be
  30348. * drawn with maximum contrast by default. Additionally, it will be
  30349. * given a `text-outline` style with the opposite color, to further
  30350. * increase the contrast. This can be overridden by setting the
  30351. * `text-outline` style to `none` in the `dataLabels.style` option.
  30352. *
  30353. * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
  30354. * Red data labels
  30355. * @sample {highmaps} maps/demo/color-axis/
  30356. * White data labels
  30357. *
  30358. * @see [style.color](#plotOptions.series.dataLabels.style)
  30359. *
  30360. * @type {Highcharts.ColorType}
  30361. * @deprecated 10.3
  30362. * @apioption plotOptions.series.dataLabels.color
  30363. */
  30364. /**
  30365. * Whether to hide data labels that are outside the plot area. By
  30366. * default, the data label is moved inside the plot area according
  30367. * to the
  30368. * [overflow](#plotOptions.series.dataLabels.overflow)
  30369. * option.
  30370. *
  30371. * @type {boolean}
  30372. * @default true
  30373. * @since 2.3.3
  30374. * @apioption plotOptions.series.dataLabels.crop
  30375. */
  30376. /**
  30377. * Whether to defer displaying the data labels until the initial
  30378. * series animation has finished. Setting to `false` renders the
  30379. * data label immediately. If set to `true` inherits the defer
  30380. * time set in [plotOptions.series.animation](#plotOptions.series.animation).
  30381. *
  30382. * @since 4.0.0
  30383. * @type {boolean}
  30384. * @product highcharts highstock gantt
  30385. */
  30386. defer: true,
  30387. /**
  30388. * Enable or disable the data labels.
  30389. *
  30390. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
  30391. * Data labels enabled
  30392. * @sample {highmaps} maps/demo/color-axis/
  30393. * Data labels enabled
  30394. *
  30395. * @type {boolean}
  30396. * @default false
  30397. * @apioption plotOptions.series.dataLabels.enabled
  30398. */
  30399. /**
  30400. * A declarative filter to control of which data labels to display.
  30401. * The declarative filter is designed for use when callback
  30402. * functions are not available, like when the chart options require
  30403. * a pure JSON structure or for use with graphical editors. For
  30404. * programmatic control, use the `formatter` instead, and return
  30405. * `undefined` to disable a single data label.
  30406. *
  30407. * @example
  30408. * filter: {
  30409. * property: 'percentage',
  30410. * operator: '>',
  30411. * value: 4
  30412. * }
  30413. *
  30414. * @sample {highcharts} highcharts/demo/pie-monochrome
  30415. * Data labels filtered by percentage
  30416. *
  30417. * @declare Highcharts.DataLabelsFilterOptionsObject
  30418. * @since 6.0.3
  30419. * @apioption plotOptions.series.dataLabels.filter
  30420. */
  30421. /**
  30422. * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
  30423. * `==`, and `===`.
  30424. *
  30425. * @type {string}
  30426. * @validvalue [">", "<", ">=", "<=", "==", "==="]
  30427. * @apioption plotOptions.series.dataLabels.filter.operator
  30428. */
  30429. /**
  30430. * The point property to filter by. Point options are passed
  30431. * directly to properties, additionally there are `y` value,
  30432. * `percentage` and others listed under {@link Highcharts.Point}
  30433. * members.
  30434. *
  30435. * @type {string}
  30436. * @apioption plotOptions.series.dataLabels.filter.property
  30437. */
  30438. /**
  30439. * The value to compare against.
  30440. *
  30441. * @type {number}
  30442. * @apioption plotOptions.series.dataLabels.filter.value
  30443. */
  30444. /**
  30445. * A
  30446. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  30447. * for the data label. Available variables are the same as for
  30448. * `formatter`.
  30449. *
  30450. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  30451. * Add a unit
  30452. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format-subexpression/
  30453. * Complex logic in the format string
  30454. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30455. * Formatted value in the data label
  30456. *
  30457. * @type {string}
  30458. * @default y
  30459. * @default point.value
  30460. * @since 3.0
  30461. * @apioption plotOptions.series.dataLabels.format
  30462. */
  30463. // eslint-disable-next-line valid-jsdoc
  30464. /**
  30465. * Callback JavaScript function to format the data label. Note that if a
  30466. * `format` is defined, the format takes precedence and the formatter is
  30467. * ignored.
  30468. *
  30469. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30470. * Formatted value
  30471. *
  30472. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30473. */
  30474. formatter: function () {
  30475. const { numberFormatter } = this.series.chart;
  30476. return typeof this.y !== 'number' ?
  30477. '' : numberFormatter(this.y, -1);
  30478. },
  30479. /**
  30480. * For points with an extent, like columns or map areas, whether to
  30481. * align the data label inside the box or to the actual value point.
  30482. * Defaults to `false` in most cases, `true` in stacked columns.
  30483. *
  30484. * @type {boolean}
  30485. * @since 3.0
  30486. * @apioption plotOptions.series.dataLabels.inside
  30487. */
  30488. /**
  30489. * Format for points with the value of null. Works analogously to
  30490. * [format](#plotOptions.series.dataLabels.format). `nullFormat` can
  30491. * be applied only to series which support displaying null points
  30492. * i.e `heatmap` or `tilemap`. Does not work with series that don't
  30493. * display null points, like `line`, `column`, `bar` or `pie`.
  30494. *
  30495. * @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
  30496. * Format data label for null points in heat map
  30497. *
  30498. * @type {boolean|string}
  30499. * @since 7.1.0
  30500. * @apioption plotOptions.series.dataLabels.nullFormat
  30501. */
  30502. /**
  30503. * Callback JavaScript function that defines formatting for points
  30504. * with the value of null. Works analogously to
  30505. * [formatter](#plotOptions.series.dataLabels.formatter).
  30506. * `nullFormatter` can be applied only to series which support
  30507. * displaying null points i.e `heatmap` or `tilemap`. Does not work
  30508. * with series that don't display null points, like `line`, `column`,
  30509. * `bar` or `pie`.
  30510. *
  30511. * @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
  30512. * Format data label for null points in heat map
  30513. *
  30514. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30515. * @since 7.1.0
  30516. * @apioption plotOptions.series.dataLabels.nullFormatter
  30517. */
  30518. /**
  30519. * How to handle data labels that flow outside the plot area. The
  30520. * default is `"justify"`, which aligns them inside the plot area.
  30521. * For columns and bars, this means it will be moved inside the bar.
  30522. * To display data labels outside the plot area, set `crop` to
  30523. * `false` and `overflow` to `"allow"`.
  30524. *
  30525. * @type {Highcharts.DataLabelsOverflowValue}
  30526. * @default justify
  30527. * @since 3.0.6
  30528. * @apioption plotOptions.series.dataLabels.overflow
  30529. */
  30530. /**
  30531. * When either the `borderWidth` or the `backgroundColor` is set,
  30532. * this is the padding within the box.
  30533. *
  30534. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30535. * Data labels box options
  30536. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30537. * Data labels box options
  30538. *
  30539. * @since 2.2.1
  30540. */
  30541. padding: 5,
  30542. /**
  30543. * Aligns data labels relative to points. If `center` alignment is
  30544. * not possible, it defaults to `right`.
  30545. *
  30546. * @type {Highcharts.AlignValue}
  30547. * @default center
  30548. * @apioption plotOptions.series.dataLabels.position
  30549. */
  30550. /**
  30551. * Text rotation in degrees. Note that due to a more complex
  30552. * structure, backgrounds, borders and padding will be lost on a
  30553. * rotated data label.
  30554. *
  30555. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30556. * Vertical labels
  30557. *
  30558. * @type {number}
  30559. * @default 0
  30560. * @apioption plotOptions.series.dataLabels.rotation
  30561. */
  30562. /**
  30563. * The shadow of the box. Works best with `borderWidth` or
  30564. * `backgroundColor`. Since 2.3 the shadow can be an object
  30565. * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
  30566. * and `width`.
  30567. *
  30568. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30569. * Data labels box options
  30570. *
  30571. * @type {boolean|Highcharts.ShadowOptionsObject}
  30572. * @default false
  30573. * @since 2.2.1
  30574. * @apioption plotOptions.series.dataLabels.shadow
  30575. */
  30576. /**
  30577. * The name of a symbol to use for the border around the label.
  30578. * Symbols are predefined functions on the Renderer object.
  30579. *
  30580. * @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
  30581. * A callout for annotations
  30582. *
  30583. * @type {string}
  30584. * @default square
  30585. * @since 4.1.2
  30586. * @apioption plotOptions.series.dataLabels.shape
  30587. */
  30588. /**
  30589. * Styles for the label. The default `color` setting is
  30590. * `"contrast"`, which is a pseudo color that Highcharts picks up
  30591. * and applies the maximum contrast to the underlying point item,
  30592. * for example the bar in a bar chart.
  30593. *
  30594. * The `textOutline` is a pseudo property that applies an outline of
  30595. * the given width with the given color, which by default is the
  30596. * maximum contrast to the text. So a bright text color will result
  30597. * in a black text outline for maximum readability on a mixed
  30598. * background. In some cases, especially with grayscale text, the
  30599. * text outline doesn't work well, in which cases it can be disabled
  30600. * by setting it to `"none"`. When `useHTML` is true, the
  30601. * `textOutline` will not be picked up. In this, case, the same
  30602. * effect can be acheived through the `text-shadow` CSS property.
  30603. *
  30604. * For some series types, where each point has an extent, like for
  30605. * example tree maps, the data label may overflow the point. There
  30606. * are two strategies for handling overflow. By default, the text
  30607. * will wrap to multiple lines. The other strategy is to set
  30608. * `style.textOverflow` to `ellipsis`, which will keep the text on
  30609. * one line plus it will break inside long words.
  30610. *
  30611. * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
  30612. * Bold labels
  30613. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow/
  30614. * Long labels truncated with an ellipsis in a pie
  30615. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap/
  30616. * Long labels are wrapped in a pie
  30617. * @sample {highmaps} maps/demo/color-axis/
  30618. * Bold labels
  30619. *
  30620. * @type {Highcharts.CSSObject}
  30621. * @since 4.1.0
  30622. * @apioption plotOptions.series.dataLabels.style
  30623. */
  30624. style: {
  30625. /** @internal */
  30626. fontSize: '0.7em',
  30627. /** @internal */
  30628. fontWeight: 'bold',
  30629. /** @internal */
  30630. color: 'contrast',
  30631. /** @internal */
  30632. textOutline: '1px contrast'
  30633. },
  30634. /**
  30635. * Options for a label text which should follow marker's shape.
  30636. * Border and background are disabled for a label that follows a
  30637. * path.
  30638. *
  30639. * **Note:** Only SVG-based renderer supports this option. Setting
  30640. * `useHTML` to true will disable this option.
  30641. *
  30642. * @declare Highcharts.DataLabelsTextPathOptionsObject
  30643. * @since 7.1.0
  30644. * @apioption plotOptions.series.dataLabels.textPath
  30645. */
  30646. /**
  30647. * Presentation attributes for the text path.
  30648. *
  30649. * @type {Highcharts.SVGAttributes}
  30650. * @since 7.1.0
  30651. * @apioption plotOptions.series.dataLabels.textPath.attributes
  30652. */
  30653. /**
  30654. * Enable or disable `textPath` option for link's or marker's data
  30655. * labels.
  30656. *
  30657. * @type {boolean}
  30658. * @since 7.1.0
  30659. * @apioption plotOptions.series.dataLabels.textPath.enabled
  30660. */
  30661. /**
  30662. * Whether to
  30663. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  30664. * to render the labels.
  30665. *
  30666. * @type {boolean}
  30667. * @default false
  30668. * @apioption plotOptions.series.dataLabels.useHTML
  30669. */
  30670. /**
  30671. * The vertical alignment of a data label. Can be one of `top`,
  30672. * `middle` or `bottom`. The default value depends on the data, for
  30673. * instance in a column chart, the label is above positive values
  30674. * and below negative values.
  30675. *
  30676. * @type {Highcharts.VerticalAlignValue|null}
  30677. * @since 2.3.3
  30678. */
  30679. verticalAlign: 'bottom',
  30680. /**
  30681. * The x position offset of the label relative to the point in
  30682. * pixels.
  30683. *
  30684. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30685. * Vertical and positioned
  30686. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  30687. * Data labels inside the bar
  30688. */
  30689. x: 0,
  30690. /**
  30691. * The z index of the data labels. Use a `zIndex` of 6 to display it above
  30692. * the series, or use a `zIndex` of 2 to display it behind the series.
  30693. *
  30694. * @type {number}
  30695. * @default 6
  30696. * @since 2.3.5
  30697. * @apioption plotOptions.series.dataLabels.zIndex
  30698. */
  30699. /**
  30700. * The y position offset of the label relative to the point in
  30701. * pixels.
  30702. *
  30703. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30704. * Vertical and positioned
  30705. */
  30706. y: 0
  30707. },
  30708. /**
  30709. * When the series contains less points than the crop threshold, all
  30710. * points are drawn, even if the points fall outside the visible plot
  30711. * area at the current zoom. The advantage of drawing all points
  30712. * (including markers and columns), is that animation is performed on
  30713. * updates. On the other hand, when the series contains more points than
  30714. * the crop threshold, the series data is cropped to only contain points
  30715. * that fall within the plot area. The advantage of cropping away
  30716. * invisible points is to increase performance on large series.
  30717. *
  30718. * @since 2.2
  30719. * @product highcharts highstock
  30720. *
  30721. * @private
  30722. */
  30723. cropThreshold: 300,
  30724. /**
  30725. * Opacity of a series parts: line, fill (e.g. area) and dataLabels.
  30726. *
  30727. * @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
  30728. *
  30729. * @since 7.1.0
  30730. *
  30731. * @private
  30732. */
  30733. opacity: 1,
  30734. /**
  30735. * The width of each point on the x axis. For example in a column chart
  30736. * with one value each day, the pointRange would be 1 day (= 24 * 3600
  30737. * * 1000 milliseconds). This is normally computed automatically, but
  30738. * this option can be used to override the automatic value.
  30739. *
  30740. * @product highstock
  30741. *
  30742. * @private
  30743. */
  30744. pointRange: 0,
  30745. /**
  30746. * When this is true, the series will not cause the Y axis to cross
  30747. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  30748. * unless the data actually crosses the plane.
  30749. *
  30750. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  30751. * 3 will make the Y axis show negative values according to the
  30752. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  30753. * at 0.
  30754. *
  30755. * @since 4.1.9
  30756. * @product highcharts highstock
  30757. *
  30758. * @private
  30759. */
  30760. softThreshold: true,
  30761. /**
  30762. * @declare Highcharts.SeriesStatesOptionsObject
  30763. *
  30764. * @private
  30765. */
  30766. states: {
  30767. /**
  30768. * The normal state of a series, or for point items in column, pie
  30769. * and similar series. Currently only used for setting animation
  30770. * when returning to normal state from hover.
  30771. *
  30772. * @declare Highcharts.SeriesStatesNormalOptionsObject
  30773. */
  30774. normal: {
  30775. /**
  30776. * Animation when returning to normal state after hovering.
  30777. *
  30778. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30779. */
  30780. animation: true
  30781. },
  30782. /**
  30783. * Options for the hovered series. These settings override the
  30784. * normal state options when a series is moused over or touched.
  30785. *
  30786. * @declare Highcharts.SeriesStatesHoverOptionsObject
  30787. */
  30788. hover: {
  30789. /**
  30790. * Enable separate styles for the hovered series to visualize
  30791. * that the user hovers either the series itself or the legend.
  30792. *
  30793. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
  30794. * Line
  30795. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
  30796. * Column
  30797. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
  30798. * Pie
  30799. *
  30800. * @type {boolean}
  30801. * @default true
  30802. * @since 1.2
  30803. * @apioption plotOptions.series.states.hover.enabled
  30804. */
  30805. /**
  30806. * Animation setting for hovering the graph in line-type series.
  30807. *
  30808. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30809. * @since 5.0.8
  30810. * @product highcharts highstock
  30811. */
  30812. animation: {
  30813. /**
  30814. * The duration of the hover animation in milliseconds. By
  30815. * default the hover state animates quickly in, and slowly
  30816. * back to normal.
  30817. *
  30818. * @internal
  30819. */
  30820. duration: 150
  30821. },
  30822. /**
  30823. * Pixel width of the graph line. By default this property is
  30824. * undefined, and the `lineWidthPlus` property dictates how much
  30825. * to increase the linewidth from normal state.
  30826. *
  30827. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
  30828. * 5px line on hover
  30829. *
  30830. * @type {number}
  30831. * @product highcharts highstock
  30832. * @apioption plotOptions.series.states.hover.lineWidth
  30833. */
  30834. /**
  30835. * The additional line width for the graph of a hovered series.
  30836. *
  30837. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  30838. * 5 pixels wider
  30839. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  30840. * 5 pixels wider
  30841. *
  30842. * @since 4.0.3
  30843. * @product highcharts highstock
  30844. */
  30845. lineWidthPlus: 1,
  30846. /**
  30847. * In Highcharts 1.0, the appearance of all markers belonging
  30848. * to the hovered series. For settings on the hover state of the
  30849. * individual point, see
  30850. * [marker.states.hover](#plotOptions.series.marker.states.hover).
  30851. *
  30852. * @deprecated
  30853. *
  30854. * @extends plotOptions.series.marker
  30855. * @excluding states, symbol
  30856. * @product highcharts highstock
  30857. */
  30858. marker: {
  30859. // lineWidth: base + 1,
  30860. // radius: base + 1
  30861. },
  30862. /**
  30863. * Options for the halo appearing around the hovered point in
  30864. * line-type series as well as outside the hovered slice in pie
  30865. * charts. By default the halo is filled by the current point or
  30866. * series color with an opacity of 0.25\. The halo can be
  30867. * disabled by setting the `halo` option to `null`.
  30868. *
  30869. * In styled mode, the halo is styled with the
  30870. * `.highcharts-halo` class, with colors inherited from
  30871. * `.highcharts-color-{n}`.
  30872. *
  30873. * @sample {highcharts} highcharts/plotoptions/halo/
  30874. * Halo options
  30875. * @sample {highstock} highcharts/plotoptions/halo/
  30876. * Halo options
  30877. *
  30878. * @declare Highcharts.SeriesStatesHoverHaloOptionsObject
  30879. * @type {null|*}
  30880. * @since 4.0
  30881. * @product highcharts highstock
  30882. */
  30883. halo: {
  30884. /**
  30885. * A collection of SVG attributes to override the appearance
  30886. * of the halo, for example `fill`, `stroke` and
  30887. * `stroke-width`.
  30888. *
  30889. * @type {Highcharts.SVGAttributes}
  30890. * @since 4.0
  30891. * @product highcharts highstock
  30892. * @apioption plotOptions.series.states.hover.halo.attributes
  30893. */
  30894. /**
  30895. * The pixel size of the halo. For point markers this is the
  30896. * radius of the halo. For pie slices it is the width of the
  30897. * halo outside the slice. For bubbles it defaults to 5 and
  30898. * is the width of the halo outside the bubble.
  30899. *
  30900. * @since 4.0
  30901. * @product highcharts highstock
  30902. */
  30903. size: 10,
  30904. /**
  30905. * Opacity for the halo unless a specific fill is overridden
  30906. * using the `attributes` setting. Note that Highcharts is
  30907. * only able to apply opacity to colors of hex or rgb(a)
  30908. * formats.
  30909. *
  30910. * @since 4.0
  30911. * @product highcharts highstock
  30912. */
  30913. opacity: 0.25
  30914. }
  30915. },
  30916. /**
  30917. * Specific options for point in selected states, after being
  30918. * selected by
  30919. * [allowPointSelect](#plotOptions.series.allowPointSelect)
  30920. * or programmatically.
  30921. *
  30922. * @sample maps/plotoptions/series-allowpointselect/
  30923. * Allow point select demo
  30924. *
  30925. * @declare Highcharts.SeriesStatesSelectOptionsObject
  30926. * @extends plotOptions.series.states.hover
  30927. * @excluding brightness
  30928. */
  30929. select: {
  30930. animation: {
  30931. /** @internal */
  30932. duration: 0
  30933. }
  30934. },
  30935. /**
  30936. * The opposite state of a hover for series.
  30937. *
  30938. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30939. * Disabled inactive state
  30940. *
  30941. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  30942. */
  30943. inactive: {
  30944. /**
  30945. * Enable or disable the inactive state for a series
  30946. *
  30947. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30948. * Disabled inactive state
  30949. *
  30950. * @type {boolean}
  30951. * @default true
  30952. * @apioption plotOptions.series.states.inactive.enabled
  30953. */
  30954. /**
  30955. * The animation for entering the inactive state.
  30956. *
  30957. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30958. */
  30959. animation: {
  30960. /** @internal */
  30961. duration: 150
  30962. },
  30963. /**
  30964. * Opacity of series elements (dataLabels, line, area).
  30965. *
  30966. * @type {number}
  30967. */
  30968. opacity: 0.2
  30969. }
  30970. },
  30971. /**
  30972. * Sticky tracking of mouse events. When true, the `mouseOut` event on a
  30973. * series isn't triggered until the mouse moves over another series, or
  30974. * out of the plot area. When false, the `mouseOut` event on a series is
  30975. * triggered when the mouse leaves the area around the series' graph or
  30976. * markers. This also implies the tooltip when not shared. When
  30977. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  30978. * will be hidden when moving the mouse between series. Defaults to true
  30979. * for line and area type series, but to false for columns, pies etc.
  30980. *
  30981. * **Note:** The boost module will force this option because of
  30982. * technical limitations.
  30983. *
  30984. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
  30985. * True by default
  30986. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
  30987. * False
  30988. *
  30989. * @default {highcharts} true
  30990. * @default {highstock} true
  30991. * @default {highmaps} false
  30992. * @since 2.0
  30993. *
  30994. * @private
  30995. */
  30996. stickyTracking: true,
  30997. /**
  30998. * A configuration object for the tooltip rendering of each single
  30999. * series. Properties are inherited from [tooltip](#tooltip), but only
  31000. * the following properties can be defined on a series level.
  31001. *
  31002. * @declare Highcharts.SeriesTooltipOptionsObject
  31003. * @since 2.3
  31004. * @extends tooltip
  31005. * @excluding animation, backgroundColor, borderColor, borderRadius,
  31006. * borderWidth, className, crosshairs, enabled, formatter,
  31007. * headerShape, hideDelay, outside, padding, positioner,
  31008. * shadow, shape, shared, snap, split, stickOnContact,
  31009. * style, useHTML
  31010. * @apioption plotOptions.series.tooltip
  31011. */
  31012. /**
  31013. * When a series contains a data array that is longer than this, only
  31014. * one dimensional arrays of numbers, or two dimensional arrays with
  31015. * x and y values are allowed. Also, only the first point is tested,
  31016. * and the rest are assumed to be the same format. This saves expensive
  31017. * data checking and indexing in long series. Set it to `0` disable.
  31018. *
  31019. * Note:
  31020. * In boost mode turbo threshold is forced. Only array of numbers or
  31021. * two dimensional arrays are allowed.
  31022. *
  31023. * @since 2.2
  31024. * @product highcharts highstock gantt
  31025. *
  31026. * @private
  31027. */
  31028. turboThreshold: 1000,
  31029. /**
  31030. * An array defining zones within a series. Zones can be applied to the
  31031. * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
  31032. * option. The zone definitions have to be in ascending order regarding
  31033. * to the value.
  31034. *
  31035. * In styled mode, the color zones are styled with the
  31036. * `.highcharts-zone-{n}` class, or custom classed from the `className`
  31037. * option
  31038. * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
  31039. *
  31040. * @see [zoneAxis](#plotOptions.series.zoneAxis)
  31041. *
  31042. * @sample {highcharts} highcharts/series/color-zones-simple/
  31043. * Color zones
  31044. * @sample {highstock} highcharts/series/color-zones-simple/
  31045. * Color zones
  31046. *
  31047. * @declare Highcharts.SeriesZonesOptionsObject
  31048. * @type {Array<*>}
  31049. * @since 4.1.0
  31050. * @product highcharts highstock
  31051. * @apioption plotOptions.series.zones
  31052. */
  31053. /**
  31054. * Styled mode only. A custom class name for the zone.
  31055. *
  31056. * @sample highcharts/css/color-zones/
  31057. * Zones styled by class name
  31058. *
  31059. * @type {string}
  31060. * @since 5.0.0
  31061. * @apioption plotOptions.series.zones.className
  31062. */
  31063. /**
  31064. * Defines the color of the series.
  31065. *
  31066. * @see [series color](#plotOptions.series.color)
  31067. *
  31068. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31069. * @since 4.1.0
  31070. * @product highcharts highstock
  31071. * @apioption plotOptions.series.zones.color
  31072. */
  31073. /**
  31074. * A name for the dash style to use for the graph.
  31075. *
  31076. * @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  31077. *
  31078. * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
  31079. * Dashed line indicates prognosis
  31080. *
  31081. * @type {Highcharts.DashStyleValue}
  31082. * @since 4.1.0
  31083. * @product highcharts highstock
  31084. * @apioption plotOptions.series.zones.dashStyle
  31085. */
  31086. /**
  31087. * Defines the fill color for the series (in area type series)
  31088. *
  31089. * @see [fillColor](#plotOptions.area.fillColor)
  31090. *
  31091. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31092. * @since 4.1.0
  31093. * @product highcharts highstock
  31094. * @apioption plotOptions.series.zones.fillColor
  31095. */
  31096. /**
  31097. * The value up to where the zone extends, if undefined the zones
  31098. * stretches to the last value in the series.
  31099. *
  31100. * @type {number}
  31101. * @since 4.1.0
  31102. * @product highcharts highstock
  31103. * @apioption plotOptions.series.zones.value
  31104. */
  31105. /**
  31106. * When using dual or multiple color axes, this number defines which
  31107. * colorAxis the particular series is connected to. It refers to
  31108. * either the
  31109. * {@link #colorAxis.id|axis id}
  31110. * or the index of the axis in the colorAxis array, with 0 being the
  31111. * first. Set this option to false to prevent a series from connecting
  31112. * to the default color axis.
  31113. *
  31114. * Since v7.2.0 the option can also be an axis id or an axis index
  31115. * instead of a boolean flag.
  31116. *
  31117. * @sample highcharts/coloraxis/coloraxis-with-pie/
  31118. * Color axis with pie series
  31119. * @sample highcharts/coloraxis/multiple-coloraxis/
  31120. * Multiple color axis
  31121. *
  31122. * @type {number|string|boolean}
  31123. * @default 0
  31124. * @product highcharts highstock highmaps
  31125. * @apioption plotOptions.series.colorAxis
  31126. */
  31127. /**
  31128. * Determines what data value should be used to calculate point color
  31129. * if `colorAxis` is used. Requires to set `min` and `max` if some
  31130. * custom point property is used or if approximation for data grouping
  31131. * is set to `'sum'`.
  31132. *
  31133. * @sample highcharts/coloraxis/custom-color-key/
  31134. * Custom color key
  31135. * @sample highcharts/coloraxis/color-key-with-stops/
  31136. * Custom colorKey with color axis stops
  31137. * @sample highcharts/coloraxis/changed-default-color-key/
  31138. * Changed default color key
  31139. *
  31140. * @type {string}
  31141. * @default y
  31142. * @since 7.2.0
  31143. * @product highcharts highstock highmaps
  31144. * @apioption plotOptions.series.colorKey
  31145. */
  31146. /**
  31147. * What type of legend symbol to render for this series. Can be one of
  31148. * `lineMarker` or `rectangle`.
  31149. *
  31150. * @validvalue ["lineMarker", "rectangle"]
  31151. *
  31152. * @sample {highcharts} highcharts/series/legend-symbol/
  31153. * Change the legend symbol
  31154. *
  31155. * @type {string}
  31156. * @default rectangle
  31157. * @since 11.0.1
  31158. * @apioption plotOptions.series.legendSymbol
  31159. */
  31160. /**
  31161. * Determines whether the series should look for the nearest point
  31162. * in both dimensions or just the x-dimension when hovering the series.
  31163. * Defaults to `'xy'` for scatter series and `'x'` for most other
  31164. * series. If the data has duplicate x-values, it is recommended to
  31165. * set this to `'xy'` to allow hovering over all points.
  31166. *
  31167. * Applies only to series types using nearest neighbor search (not
  31168. * direct hover) for tooltip.
  31169. *
  31170. * @sample {highcharts} highcharts/series/findnearestpointby/
  31171. * Different hover behaviors
  31172. * @sample {highstock} highcharts/series/findnearestpointby/
  31173. * Different hover behaviors
  31174. * @sample {highmaps} highcharts/series/findnearestpointby/
  31175. * Different hover behaviors
  31176. *
  31177. * @since 5.0.10
  31178. * @validvalue ["x", "xy"]
  31179. *
  31180. * @private
  31181. */
  31182. findNearestPointBy: 'x'
  31183. };
  31184. /* *
  31185. *
  31186. * Default Export
  31187. *
  31188. * */
  31189. return seriesDefaults;
  31190. });
  31191. _registerModule(_modules, 'Core/Series/SeriesRegistry.js', [_modules['Core/Globals.js'], _modules['Core/Defaults.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (H, D, Point, U) {
  31192. /* *
  31193. *
  31194. * (c) 2010-2021 Torstein Honsi
  31195. *
  31196. * License: www.highcharts.com/license
  31197. *
  31198. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  31199. *
  31200. * */
  31201. const { defaultOptions } = D;
  31202. const { extendClass, merge } = U;
  31203. /* *
  31204. *
  31205. * Namespace
  31206. *
  31207. * */
  31208. var SeriesRegistry;
  31209. (function (SeriesRegistry) {
  31210. /* *
  31211. *
  31212. * Properties
  31213. *
  31214. * */
  31215. /**
  31216. * @internal
  31217. * @todo Move `Globals.seriesTypes` code to her.
  31218. */
  31219. SeriesRegistry.seriesTypes = H.seriesTypes;
  31220. /* *
  31221. *
  31222. * Functions
  31223. *
  31224. * */
  31225. /**
  31226. * Registers class pattern of a series.
  31227. *
  31228. * @private
  31229. */
  31230. function registerSeriesType(seriesType, SeriesClass) {
  31231. const defaultPlotOptions = defaultOptions.plotOptions || {}, seriesOptions = SeriesClass.defaultOptions, seriesProto = SeriesClass.prototype;
  31232. seriesProto.type = seriesType;
  31233. if (!seriesProto.pointClass) {
  31234. seriesProto.pointClass = Point;
  31235. }
  31236. if (seriesOptions) {
  31237. defaultPlotOptions[seriesType] = seriesOptions;
  31238. }
  31239. SeriesRegistry.seriesTypes[seriesType] = SeriesClass;
  31240. }
  31241. SeriesRegistry.registerSeriesType = registerSeriesType;
  31242. /**
  31243. * Old factory to create new series prototypes.
  31244. *
  31245. * @deprecated
  31246. * @function Highcharts.seriesType
  31247. *
  31248. * @param {string} type
  31249. * The series type name.
  31250. *
  31251. * @param {string} parent
  31252. * The parent series type name. Use `line` to inherit from the basic
  31253. * {@link Series} object.
  31254. *
  31255. * @param {Highcharts.SeriesOptionsType|Highcharts.Dictionary<*>} options
  31256. * The additional default options that are merged with the parent's options.
  31257. *
  31258. * @param {Highcharts.Dictionary<*>} [props]
  31259. * The properties (functions and primitives) to set on the new prototype.
  31260. *
  31261. * @param {Highcharts.Dictionary<*>} [pointProps]
  31262. * Members for a series-specific extension of the {@link Point} prototype if
  31263. * needed.
  31264. *
  31265. * @return {Highcharts.Series}
  31266. * The newly created prototype as extended from {@link Series} or its
  31267. * derivatives.
  31268. */
  31269. function seriesType(type, parent, options, seriesProto, pointProto) {
  31270. const defaultPlotOptions = defaultOptions.plotOptions || {};
  31271. parent = parent || '';
  31272. // Merge the options
  31273. defaultPlotOptions[type] = merge(defaultPlotOptions[parent], options);
  31274. // Create the class
  31275. registerSeriesType(type, extendClass(SeriesRegistry.seriesTypes[parent] || function () { }, seriesProto));
  31276. SeriesRegistry.seriesTypes[type].prototype.type = type;
  31277. // Create the point class if needed
  31278. if (pointProto) {
  31279. SeriesRegistry.seriesTypes[type].prototype.pointClass = extendClass(Point, pointProto);
  31280. }
  31281. return SeriesRegistry.seriesTypes[type];
  31282. }
  31283. SeriesRegistry.seriesType = seriesType;
  31284. })(SeriesRegistry || (SeriesRegistry = {}));
  31285. /* *
  31286. *
  31287. * Default Export
  31288. *
  31289. * */
  31290. return SeriesRegistry;
  31291. });
  31292. _registerModule(_modules, 'Core/Series/Series.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Defaults.js'], _modules['Core/Foundation.js'], _modules['Core/Globals.js'], _modules['Core/Legend/LegendSymbol.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/SeriesDefaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (A, D, F, H, LegendSymbol, Point, SeriesDefaults, SeriesRegistry, SVGElement, U) {
  31293. /* *
  31294. *
  31295. * (c) 2010-2021 Torstein Honsi
  31296. *
  31297. * License: www.highcharts.com/license
  31298. *
  31299. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  31300. *
  31301. * */
  31302. const { animObject, setAnimation } = A;
  31303. const { defaultOptions } = D;
  31304. const { registerEventOptions } = F;
  31305. const { hasTouch, svg, win } = H;
  31306. const { seriesTypes } = SeriesRegistry;
  31307. const { addEvent, arrayMax, arrayMin, clamp, correctFloat, defined, diffObjects, erase, error, extend, find, fireEvent, getClosestDistance, getNestedProperty, insertItem, isArray, isNumber, isString, merge, objectEach, pick, removeEvent, splat, syncTimeout } = U;
  31308. /* *
  31309. *
  31310. * Class
  31311. *
  31312. * */
  31313. /**
  31314. * This is the base series prototype that all other series types inherit from.
  31315. * A new series is initialized either through the
  31316. * [series](https://api.highcharts.com/highcharts/series)
  31317. * option structure, or after the chart is initialized, through
  31318. * {@link Highcharts.Chart#addSeries}.
  31319. *
  31320. * The object can be accessed in a number of ways. All series and point event
  31321. * handlers give a reference to the `series` object. The chart object has a
  31322. * {@link Highcharts.Chart#series|series} property that is a collection of all
  31323. * the chart's series. The point objects and axis objects also have the same
  31324. * reference.
  31325. *
  31326. * Another way to reference the series programmatically is by `id`. Add an id
  31327. * in the series configuration options, and get the series object by
  31328. * {@link Highcharts.Chart#get}.
  31329. *
  31330. * Configuration options for the series are given in three levels. Options for
  31331. * all series in a chart are given in the
  31332. * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
  31333. * object. Then options for all series of a specific type
  31334. * are given in the plotOptions of that type, for example `plotOptions.line`.
  31335. * Next, options for one single series are given in the series array, or as
  31336. * arguments to `chart.addSeries`.
  31337. *
  31338. * The data in the series is stored in various arrays.
  31339. *
  31340. * - First, `series.options.data` contains all the original config options for
  31341. * each point whether added by options or methods like `series.addPoint`.
  31342. *
  31343. * - Next, `series.data` contains those values converted to points, but in case
  31344. * the series data length exceeds the `cropThreshold`, or if the data is
  31345. * grouped, `series.data` doesn't contain all the points. It only contains the
  31346. * points that have been created on demand.
  31347. *
  31348. * - Then there's `series.points` that contains all currently visible point
  31349. * objects. In case of cropping, the cropped-away points are not part of this
  31350. * array. The `series.points` array starts at `series.cropStart` compared to
  31351. * `series.data` and `series.options.data`. If however the series data is
  31352. * grouped, these can't be correlated one to one.
  31353. *
  31354. * - `series.xData` and `series.processedXData` contain clean x values,
  31355. * equivalent to `series.data` and `series.points`.
  31356. *
  31357. * - `series.yData` and `series.processedYData` contain clean y values,
  31358. * equivalent to `series.data` and `series.points`.
  31359. *
  31360. * @class
  31361. * @name Highcharts.Series
  31362. *
  31363. * @param {Highcharts.Chart} chart
  31364. * The chart instance.
  31365. *
  31366. * @param {Highcharts.SeriesOptionsType|object} options
  31367. * The series options.
  31368. */
  31369. class Series {
  31370. constructor() {
  31371. /* *
  31372. *
  31373. * Static Properties
  31374. *
  31375. * */
  31376. this._i = void 0;
  31377. this.chart = void 0;
  31378. this.data = void 0;
  31379. this.eventOptions = void 0;
  31380. this.eventsToUnbind = void 0;
  31381. this.index = void 0;
  31382. this.linkedSeries = void 0;
  31383. this.options = void 0;
  31384. this.points = void 0;
  31385. this.processedXData = void 0;
  31386. this.processedYData = void 0;
  31387. this.tooltipOptions = void 0;
  31388. this.userOptions = void 0;
  31389. this.xAxis = void 0;
  31390. this.yAxis = void 0;
  31391. this.zones = void 0;
  31392. /** eslint-enable valid-jsdoc */
  31393. }
  31394. /* *
  31395. *
  31396. * Functions
  31397. *
  31398. * */
  31399. /* eslint-disable valid-jsdoc */
  31400. init(chart, userOptions) {
  31401. fireEvent(this, 'init', { options: userOptions });
  31402. const series = this, chartSeries = chart.series;
  31403. // The 'eventsToUnbind' property moved from prototype into the
  31404. // Series init to avoid reference to the same array between
  31405. // the different series and charts. #12959, #13937
  31406. this.eventsToUnbind = [];
  31407. /**
  31408. * Read only. The chart that the series belongs to.
  31409. *
  31410. * @name Highcharts.Series#chart
  31411. * @type {Highcharts.Chart}
  31412. */
  31413. series.chart = chart;
  31414. /**
  31415. * Read only. The series' type, like "line", "area", "column" etc.
  31416. * The type in the series options anc can be altered using
  31417. * {@link Series#update}.
  31418. *
  31419. * @name Highcharts.Series#type
  31420. * @type {string}
  31421. */
  31422. /**
  31423. * Read only. The series' current options. To update, use
  31424. * {@link Series#update}.
  31425. *
  31426. * @name Highcharts.Series#options
  31427. * @type {Highcharts.SeriesOptionsType}
  31428. */
  31429. series.options = series.setOptions(userOptions);
  31430. const options = series.options;
  31431. series.linkedSeries = [];
  31432. // bind the axes
  31433. series.bindAxes();
  31434. extend(series, {
  31435. /**
  31436. * The series name as given in the options. Defaults to
  31437. * "Series {n}".
  31438. *
  31439. * @name Highcharts.Series#name
  31440. * @type {string}
  31441. */
  31442. name: options.name,
  31443. state: '',
  31444. /**
  31445. * Read only. The series' visibility state as set by {@link
  31446. * Series#show}, {@link Series#hide}, or in the initial
  31447. * configuration.
  31448. *
  31449. * @name Highcharts.Series#visible
  31450. * @type {boolean}
  31451. */
  31452. visible: options.visible !== false,
  31453. /**
  31454. * Read only. The series' selected state as set by {@link
  31455. * Highcharts.Series#select}.
  31456. *
  31457. * @name Highcharts.Series#selected
  31458. * @type {boolean}
  31459. */
  31460. selected: options.selected === true // false by default
  31461. });
  31462. registerEventOptions(this, options);
  31463. const events = options.events;
  31464. if ((events && events.click) ||
  31465. (options.point &&
  31466. options.point.events &&
  31467. options.point.events.click) ||
  31468. options.allowPointSelect) {
  31469. chart.runTrackerClick = true;
  31470. }
  31471. series.getColor();
  31472. series.getSymbol();
  31473. // Initialize the parallel data arrays
  31474. series.parallelArrays.forEach(function (key) {
  31475. if (!series[key + 'Data']) {
  31476. series[key + 'Data'] = [];
  31477. }
  31478. });
  31479. // Mark cartesian
  31480. if (series.isCartesian) {
  31481. chart.hasCartesianSeries = true;
  31482. }
  31483. // Get the index and register the series in the chart. The index is
  31484. // one more than the current latest series index (#5960).
  31485. let lastSeries;
  31486. if (chartSeries.length) {
  31487. lastSeries = chartSeries[chartSeries.length - 1];
  31488. }
  31489. series._i = pick(lastSeries && lastSeries._i, -1) + 1;
  31490. series.opacity = series.options.opacity;
  31491. // Insert the series and re-order all series above the insertion
  31492. // point.
  31493. chart.orderItems('series', insertItem(this, chartSeries));
  31494. // Set options for series with sorting and set data later.
  31495. if (options.dataSorting && options.dataSorting.enabled) {
  31496. series.setDataSortingOptions();
  31497. }
  31498. else if (!series.points && !series.data) {
  31499. series.setData(options.data, false);
  31500. }
  31501. fireEvent(this, 'afterInit');
  31502. }
  31503. /**
  31504. * Check whether the series item is itself or inherits from a certain
  31505. * series type.
  31506. *
  31507. * @function Highcharts.Series#is
  31508. * @param {string} type The type of series to check for, can be either
  31509. * featured or custom series types. For example `column`, `pie`,
  31510. * `ohlc` etc.
  31511. *
  31512. * @return {boolean}
  31513. * True if this item is or inherits from the given type.
  31514. */
  31515. is(type) {
  31516. return seriesTypes[type] && this instanceof seriesTypes[type];
  31517. }
  31518. /**
  31519. * Set the xAxis and yAxis properties of cartesian series, and register
  31520. * the series in the `axis.series` array.
  31521. *
  31522. * @private
  31523. * @function Highcharts.Series#bindAxes
  31524. */
  31525. bindAxes() {
  31526. const series = this, seriesOptions = series.options, chart = series.chart;
  31527. let axisOptions;
  31528. fireEvent(this, 'bindAxes', null, function () {
  31529. // repeat for xAxis and yAxis
  31530. (series.axisTypes || []).forEach(function (coll) {
  31531. // loop through the chart's axis objects
  31532. chart[coll].forEach(function (axis) {
  31533. axisOptions = axis.options;
  31534. // apply if the series xAxis or yAxis option mathches
  31535. // the number of the axis, or if undefined, use the
  31536. // first axis
  31537. if (pick(seriesOptions[coll], 0) === axis.index ||
  31538. (typeof seriesOptions[coll] !==
  31539. 'undefined' &&
  31540. seriesOptions[coll] === axisOptions.id)) {
  31541. // register this series in the axis.series lookup
  31542. insertItem(series, axis.series);
  31543. // set this series.xAxis or series.yAxis reference
  31544. /**
  31545. * Read only. The unique xAxis object associated
  31546. * with the series.
  31547. *
  31548. * @name Highcharts.Series#xAxis
  31549. * @type {Highcharts.Axis}
  31550. */
  31551. /**
  31552. * Read only. The unique yAxis object associated
  31553. * with the series.
  31554. *
  31555. * @name Highcharts.Series#yAxis
  31556. * @type {Highcharts.Axis}
  31557. */
  31558. series[coll] = axis;
  31559. // mark dirty for redraw
  31560. axis.isDirty = true;
  31561. }
  31562. });
  31563. // The series needs an X and an Y axis
  31564. if (!series[coll] &&
  31565. series.optionalAxis !== coll) {
  31566. error(18, true, chart);
  31567. }
  31568. });
  31569. });
  31570. fireEvent(this, 'afterBindAxes');
  31571. }
  31572. /**
  31573. * For simple series types like line and column, the data values are
  31574. * held in arrays like xData and yData for quick lookup to find extremes
  31575. * and more. For multidimensional series like bubble and map, this can
  31576. * be extended with arrays like zData and valueData by adding to the
  31577. * `series.parallelArrays` array.
  31578. *
  31579. * @private
  31580. * @function Highcharts.Series#updateParallelArrays
  31581. */
  31582. updateParallelArrays(point, i, iArgs) {
  31583. const series = point.series, fn = isNumber(i) ?
  31584. // Insert the value in the given position
  31585. function (key) {
  31586. const val = key === 'y' && series.toYData ?
  31587. series.toYData(point) :
  31588. point[key];
  31589. series[key + 'Data'][i] = val;
  31590. } :
  31591. // Apply the method specified in i with the following
  31592. // arguments as arguments
  31593. function (key) {
  31594. Array.prototype[i].apply(series[key + 'Data'], iArgs);
  31595. };
  31596. series.parallelArrays.forEach(fn);
  31597. }
  31598. /**
  31599. * Define hasData functions for series. These return true if there
  31600. * are data points on this series within the plot area.
  31601. *
  31602. * @private
  31603. * @function Highcharts.Series#hasData
  31604. */
  31605. hasData() {
  31606. return ((this.visible &&
  31607. typeof this.dataMax !== 'undefined' &&
  31608. typeof this.dataMin !== 'undefined') || ( // #3703
  31609. this.visible &&
  31610. this.yData &&
  31611. this.yData.length > 0) // #9758
  31612. );
  31613. }
  31614. /**
  31615. * Return an auto incremented x value based on the pointStart and
  31616. * pointInterval options. This is only used if an x value is not given
  31617. * for the point that calls autoIncrement.
  31618. *
  31619. * @private
  31620. * @function Highcharts.Series#autoIncrement
  31621. */
  31622. autoIncrement(x) {
  31623. const options = this.options, pointIntervalUnit = options.pointIntervalUnit, relativeXValue = options.relativeXValue, time = this.chart.time;
  31624. let xIncrement = this.xIncrement, date, pointInterval;
  31625. xIncrement = pick(xIncrement, options.pointStart, 0);
  31626. this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
  31627. if (relativeXValue && isNumber(x)) {
  31628. pointInterval *= x;
  31629. }
  31630. // Added code for pointInterval strings
  31631. if (pointIntervalUnit) {
  31632. date = new time.Date(xIncrement);
  31633. if (pointIntervalUnit === 'day') {
  31634. time.set('Date', date, time.get('Date', date) + pointInterval);
  31635. }
  31636. else if (pointIntervalUnit === 'month') {
  31637. time.set('Month', date, time.get('Month', date) + pointInterval);
  31638. }
  31639. else if (pointIntervalUnit === 'year') {
  31640. time.set('FullYear', date, time.get('FullYear', date) + pointInterval);
  31641. }
  31642. pointInterval = date.getTime() - xIncrement;
  31643. }
  31644. if (relativeXValue && isNumber(x)) {
  31645. return xIncrement + pointInterval;
  31646. }
  31647. this.xIncrement = xIncrement + pointInterval;
  31648. return xIncrement;
  31649. }
  31650. /**
  31651. * Internal function to set properties for series if data sorting is
  31652. * enabled.
  31653. *
  31654. * @private
  31655. * @function Highcharts.Series#setDataSortingOptions
  31656. */
  31657. setDataSortingOptions() {
  31658. const options = this.options;
  31659. extend(this, {
  31660. requireSorting: false,
  31661. sorted: false,
  31662. enabledDataSorting: true,
  31663. allowDG: false
  31664. });
  31665. // To allow unsorted data for column series.
  31666. if (!defined(options.pointRange)) {
  31667. options.pointRange = 1;
  31668. }
  31669. }
  31670. /**
  31671. * Set the series options by merging from the options tree. Called
  31672. * internally on initializing and updating series. This function will
  31673. * not redraw the series. For API usage, use {@link Series#update}.
  31674. * @private
  31675. * @function Highcharts.Series#setOptions
  31676. * @param {Highcharts.SeriesOptionsType} itemOptions
  31677. * The series options.
  31678. * @emits Highcharts.Series#event:afterSetOptions
  31679. */
  31680. setOptions(itemOptions) {
  31681. var _a,
  31682. _b;
  31683. const chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, userOptions = chart.userOptions || {}, seriesUserOptions = merge(itemOptions), styledMode = chart.styledMode, e = {
  31684. plotOptions: plotOptions,
  31685. userOptions: seriesUserOptions
  31686. };
  31687. let zone;
  31688. fireEvent(this, 'setOptions', e);
  31689. // These may be modified by the event
  31690. const typeOptions = e.plotOptions[this.type], userPlotOptions = (userOptions.plotOptions || {}), userPlotOptionsSeries = userPlotOptions.series || {}, defaultPlotOptionsType = (defaultOptions.plotOptions[this.type] || {}), userPlotOptionsType = userPlotOptions[this.type] || {};
  31691. // use copy to prevent undetected changes (#9762)
  31692. /**
  31693. * Contains series options by the user without defaults.
  31694. * @name Highcharts.Series#userOptions
  31695. * @type {Highcharts.SeriesOptionsType}
  31696. */
  31697. this.userOptions = e.userOptions;
  31698. const options = merge(typeOptions, plotOptions.series,
  31699. // #3881, chart instance plotOptions[type] should trump
  31700. // plotOptions.series
  31701. userPlotOptionsType, seriesUserOptions);
  31702. // The tooltip options are merged between global and series specific
  31703. // options. Importance order asscendingly:
  31704. // globals: (1)tooltip, (2)plotOptions.series,
  31705. // (3)plotOptions[this.type]
  31706. // init userOptions with possible later updates: 4-6 like 1-3 and
  31707. // (7)this series options
  31708. this.tooltipOptions = merge(defaultOptions.tooltip, // 1
  31709. (_a = defaultOptions.plotOptions.series) === null || _a === void 0 ? void 0 : _a.tooltip, // 2
  31710. defaultPlotOptionsType === null || defaultPlotOptionsType === void 0 ? void 0 : defaultPlotOptionsType.tooltip, // 3
  31711. chart.userOptions.tooltip, // 4
  31712. (_b = userPlotOptions.series) === null || _b === void 0 ? void 0 : _b.tooltip, // 5
  31713. userPlotOptionsType.tooltip, // 6
  31714. seriesUserOptions.tooltip // 7
  31715. );
  31716. // When shared tooltip, stickyTracking is true by default,
  31717. // unless user says otherwise.
  31718. this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptionsType.stickyTracking, userPlotOptionsSeries.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
  31719. true :
  31720. options.stickyTracking));
  31721. // Delete marker object if not allowed (#1125)
  31722. if (typeOptions.marker === null) {
  31723. delete options.marker;
  31724. }
  31725. // Handle color zones
  31726. this.zoneAxis = options.zoneAxis;
  31727. const zones = this.zones = (options.zones || []).slice();
  31728. if ((options.negativeColor || options.negativeFillColor) &&
  31729. !options.zones) {
  31730. zone = {
  31731. value: options[this.zoneAxis + 'Threshold'] ||
  31732. options.threshold ||
  31733. 0,
  31734. className: 'highcharts-negative'
  31735. };
  31736. if (!styledMode) {
  31737. zone.color = options.negativeColor;
  31738. zone.fillColor = options.negativeFillColor;
  31739. }
  31740. zones.push(zone);
  31741. }
  31742. if (zones.length) { // Push one extra zone for the rest
  31743. if (defined(zones[zones.length - 1].value)) {
  31744. zones.push(styledMode ? {} : {
  31745. color: this.color,
  31746. fillColor: this.fillColor
  31747. });
  31748. }
  31749. }
  31750. fireEvent(this, 'afterSetOptions', { options: options });
  31751. return options;
  31752. }
  31753. /**
  31754. * Return series name in "Series {Number}" format or the one defined by
  31755. * a user. This method can be simply overridden as series name format
  31756. * can vary (e.g. technical indicators).
  31757. *
  31758. * @function Highcharts.Series#getName
  31759. *
  31760. * @return {string}
  31761. * The series name.
  31762. */
  31763. getName() {
  31764. // #4119
  31765. return pick(this.options.name, 'Series ' + (this.index + 1));
  31766. }
  31767. /**
  31768. * @private
  31769. * @function Highcharts.Series#getCyclic
  31770. */
  31771. getCyclic(prop, value, defaults) {
  31772. const chart = this.chart, indexName = `${prop}Index`, counterName = `${prop}Counter`, len = (
  31773. // Symbol count
  31774. (defaults === null || defaults === void 0 ? void 0 : defaults.length) ||
  31775. // Color count
  31776. chart.options.chart.colorCount);
  31777. let i, setting;
  31778. if (!value) {
  31779. // Pick up either the colorIndex option, or the series.colorIndex
  31780. // after Series.update()
  31781. setting = pick(prop === 'color' ? this.options.colorIndex : void 0, this[indexName]);
  31782. if (defined(setting)) { // after Series.update()
  31783. i = setting;
  31784. }
  31785. else {
  31786. // #6138
  31787. if (!chart.series.length) {
  31788. chart[counterName] = 0;
  31789. }
  31790. i = chart[counterName] % len;
  31791. chart[counterName] += 1;
  31792. }
  31793. if (defaults) {
  31794. value = defaults[i];
  31795. }
  31796. }
  31797. // Set the colorIndex
  31798. if (typeof i !== 'undefined') {
  31799. this[indexName] = i;
  31800. }
  31801. this[prop] = value;
  31802. }
  31803. /**
  31804. * Get the series' color based on either the options or pulled from
  31805. * global options.
  31806. *
  31807. * @private
  31808. * @function Highcharts.Series#getColor
  31809. */
  31810. getColor() {
  31811. if (this.chart.styledMode) {
  31812. this.getCyclic('color');
  31813. }
  31814. else if (this.options.colorByPoint) {
  31815. this.color = "#cccccc" /* Palette.neutralColor20 */;
  31816. }
  31817. else {
  31818. this.getCyclic('color', this.options.color ||
  31819. defaultOptions.plotOptions[this.type].color, this.chart.options.colors);
  31820. }
  31821. }
  31822. /**
  31823. * Get all points' instances created for this series.
  31824. *
  31825. * @private
  31826. * @function Highcharts.Series#getPointsCollection
  31827. */
  31828. getPointsCollection() {
  31829. return (this.hasGroupedData ? this.points : this.data) || [];
  31830. }
  31831. /**
  31832. * Get the series' symbol based on either the options or pulled from
  31833. * global options.
  31834. *
  31835. * @private
  31836. * @function Highcharts.Series#getSymbol
  31837. */
  31838. getSymbol() {
  31839. const seriesMarkerOption = this.options.marker;
  31840. this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
  31841. }
  31842. /**
  31843. * Finds the index of an existing point that matches the given point
  31844. * options.
  31845. *
  31846. * @private
  31847. * @function Highcharts.Series#findPointIndex
  31848. * @param {Highcharts.PointOptionsObject} optionsObject
  31849. * The options of the point.
  31850. * @param {number} fromIndex
  31851. * The index to start searching from, used for optimizing series with
  31852. * required sorting.
  31853. * @return {number|undefined}
  31854. * Returns the index of a matching point, or undefined if no match is found.
  31855. */
  31856. findPointIndex(optionsObject, fromIndex) {
  31857. const id = optionsObject.id, x = optionsObject.x, oldData = this.points, dataSorting = this.options.dataSorting;
  31858. let matchingPoint, matchedById, pointIndex;
  31859. if (id) {
  31860. const item = this.chart.get(id);
  31861. if (item instanceof Point) {
  31862. matchingPoint = item;
  31863. }
  31864. }
  31865. else if (this.linkedParent ||
  31866. this.enabledDataSorting ||
  31867. this.options.relativeXValue) {
  31868. let matcher = (oldPoint) => !oldPoint.touched &&
  31869. oldPoint.index === optionsObject.index;
  31870. if (dataSorting && dataSorting.matchByName) {
  31871. matcher = (oldPoint) => !oldPoint.touched &&
  31872. oldPoint.name === optionsObject.name;
  31873. }
  31874. else if (this.options.relativeXValue) {
  31875. matcher = (oldPoint) => !oldPoint.touched &&
  31876. oldPoint.options.x === optionsObject.x;
  31877. }
  31878. matchingPoint = find(oldData, matcher);
  31879. // Add unmatched point as a new point
  31880. if (!matchingPoint) {
  31881. return void 0;
  31882. }
  31883. }
  31884. if (matchingPoint) {
  31885. pointIndex = matchingPoint && matchingPoint.index;
  31886. if (typeof pointIndex !== 'undefined') {
  31887. matchedById = true;
  31888. }
  31889. }
  31890. // Search for the same X in the existing data set
  31891. if (typeof pointIndex === 'undefined' && isNumber(x)) {
  31892. pointIndex = this.xData.indexOf(x, fromIndex);
  31893. }
  31894. // Reduce pointIndex if data is cropped
  31895. if (pointIndex !== -1 &&
  31896. typeof pointIndex !== 'undefined' &&
  31897. this.cropped) {
  31898. pointIndex = (pointIndex >= this.cropStart) ?
  31899. pointIndex - this.cropStart : pointIndex;
  31900. }
  31901. if (!matchedById &&
  31902. isNumber(pointIndex) &&
  31903. oldData[pointIndex] && oldData[pointIndex].touched) {
  31904. pointIndex = void 0;
  31905. }
  31906. return pointIndex;
  31907. }
  31908. /**
  31909. * Internal function called from setData. If the point count is the same
  31910. * as it was, or if there are overlapping X values, just run
  31911. * Point.update which is cheaper, allows animation, and keeps references
  31912. * to points. This also allows adding or removing points if the X-es
  31913. * don't match.
  31914. *
  31915. * @private
  31916. * @function Highcharts.Series#updateData
  31917. */
  31918. updateData(data, animation) {
  31919. const options = this.options, dataSorting = options.dataSorting, oldData = this.points, pointsToAdd = [], requireSorting = this.requireSorting, equalLength = data.length === oldData.length;
  31920. let hasUpdatedByKey, i, point, lastIndex, succeeded = true;
  31921. this.xIncrement = null;
  31922. // Iterate the new data
  31923. data.forEach(function (pointOptions, i) {
  31924. const optionsObject = (defined(pointOptions) &&
  31925. this.pointClass.prototype.optionsToObject.call({ series: this }, pointOptions)) || {};
  31926. let pointIndex;
  31927. // Get the x of the new data point
  31928. const x = optionsObject.x, id = optionsObject.id;
  31929. if (id || isNumber(x)) {
  31930. pointIndex = this.findPointIndex(optionsObject, lastIndex);
  31931. // Matching X not found
  31932. // or used already due to ununique x values (#8995),
  31933. // add point (but later)
  31934. if (pointIndex === -1 ||
  31935. typeof pointIndex === 'undefined') {
  31936. pointsToAdd.push(pointOptions);
  31937. // Matching X found, update
  31938. }
  31939. else if (oldData[pointIndex] &&
  31940. pointOptions !== options.data[pointIndex]) {
  31941. oldData[pointIndex].update(pointOptions, false, null, false);
  31942. // Mark it touched, below we will remove all points that
  31943. // are not touched.
  31944. oldData[pointIndex].touched = true;
  31945. // Speed optimize by only searching after last known
  31946. // index. Performs ~20% bettor on large data sets.
  31947. if (requireSorting) {
  31948. lastIndex = pointIndex + 1;
  31949. }
  31950. // Point exists, no changes, don't remove it
  31951. }
  31952. else if (oldData[pointIndex]) {
  31953. oldData[pointIndex].touched = true;
  31954. }
  31955. // If the length is equal and some of the nodes had a
  31956. // match in the same position, we don't want to remove
  31957. // non-matches.
  31958. if (!equalLength ||
  31959. i !== pointIndex ||
  31960. (dataSorting && dataSorting.enabled) ||
  31961. this.hasDerivedData) {
  31962. hasUpdatedByKey = true;
  31963. }
  31964. }
  31965. else {
  31966. // Gather all points that are not matched
  31967. pointsToAdd.push(pointOptions);
  31968. }
  31969. }, this);
  31970. // Remove points that don't exist in the updated data set
  31971. if (hasUpdatedByKey) {
  31972. i = oldData.length;
  31973. while (i--) {
  31974. point = oldData[i];
  31975. if (point && !point.touched && point.remove) {
  31976. point.remove(false, animation);
  31977. }
  31978. }
  31979. // If we did not find keys (ids or x-values), and the length is the
  31980. // same, update one-to-one
  31981. }
  31982. else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
  31983. data.forEach(function (point, i) {
  31984. // .update doesn't exist on a linked, hidden series (#3709)
  31985. // (#10187)
  31986. if (point !== oldData[i].y && !oldData[i].destroyed) {
  31987. oldData[i].update(point, false, null, false);
  31988. }
  31989. });
  31990. // Don't add new points since those configs are used above
  31991. pointsToAdd.length = 0;
  31992. // Did not succeed in updating data
  31993. }
  31994. else {
  31995. succeeded = false;
  31996. }
  31997. oldData.forEach(function (point) {
  31998. if (point) {
  31999. point.touched = false;
  32000. }
  32001. });
  32002. if (!succeeded) {
  32003. return false;
  32004. }
  32005. // Add new points
  32006. pointsToAdd.forEach(function (point) {
  32007. this.addPoint(point, false, null, null, false);
  32008. }, this);
  32009. if (this.xIncrement === null &&
  32010. this.xData &&
  32011. this.xData.length) {
  32012. this.xIncrement = arrayMax(this.xData);
  32013. this.autoIncrement();
  32014. }
  32015. return true;
  32016. }
  32017. /**
  32018. * Apply a new set of data to the series and optionally redraw it. The
  32019. * new data array is passed by reference (except in case of
  32020. * `updatePoints`), and may later be mutated when updating the chart
  32021. * data.
  32022. *
  32023. * Note the difference in behaviour when setting the same amount of
  32024. * points, or a different amount of points, as handled by the
  32025. * `updatePoints` parameter.
  32026. *
  32027. * @sample highcharts/members/series-setdata/
  32028. * Set new data from a button
  32029. * @sample highcharts/members/series-setdata-pie/
  32030. * Set data in a pie
  32031. * @sample stock/members/series-setdata/
  32032. * Set new data in Highcharts Stock
  32033. * @sample maps/members/series-setdata/
  32034. * Set new data in Highmaps
  32035. *
  32036. * @function Highcharts.Series#setData
  32037. *
  32038. * @param {Array<Highcharts.PointOptionsType>} data
  32039. * Takes an array of data in the same format as described under
  32040. * `series.{type}.data` for the given series type, for example a
  32041. * line series would take data in the form described under
  32042. * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
  32043. *
  32044. * @param {boolean} [redraw=true]
  32045. * Whether to redraw the chart after the series is altered. If
  32046. * doing more operations on the chart, it is a good idea to set
  32047. * redraw to false and call {@link Chart#redraw} after.
  32048. *
  32049. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  32050. * When the updated data is the same length as the existing data,
  32051. * points will be updated by default, and animation visualizes
  32052. * how the points are changed. Set false to disable animation, or
  32053. * a configuration object to set duration or easing.
  32054. *
  32055. * @param {boolean} [updatePoints=true]
  32056. * When this is true, points will be updated instead of replaced
  32057. * whenever possible. This occurs a) when the updated data is the
  32058. * same length as the existing data, b) when points are matched
  32059. * by their id's, or c) when points can be matched by X values.
  32060. * This allows updating with animation and performs better. In
  32061. * this case, the original array is not passed by reference. Set
  32062. * `false` to prevent.
  32063. */
  32064. setData(data, redraw = true, animation, updatePoints) {
  32065. var _a;
  32066. const series = this, oldData = series.points, oldDataLength = (oldData && oldData.length) || 0, options = series.options, chart = series.chart, dataSorting = options.dataSorting, xAxis = series.xAxis, turboThreshold = options.turboThreshold, xData = this.xData, yData = this.yData, pointArrayMap = series.pointArrayMap, valueCount = pointArrayMap && pointArrayMap.length, keys = options.keys;
  32067. let i, pt, updatedData, indexOfX = 0, indexOfY = 1, firstPoint = null, copiedData;
  32068. if (!chart.options.chart.allowMutatingData) { // #4259
  32069. // Remove old reference
  32070. if (options.data) {
  32071. delete series.options.data;
  32072. }
  32073. if (series.userOptions.data) {
  32074. delete series.userOptions.data;
  32075. }
  32076. copiedData = merge(true, data);
  32077. }
  32078. data = copiedData || data || [];
  32079. const dataLength = data.length;
  32080. if (dataSorting && dataSorting.enabled) {
  32081. data = this.sortData(data);
  32082. }
  32083. // First try to run Point.update which is cheaper, allows animation,
  32084. // and keeps references to points.
  32085. if (chart.options.chart.allowMutatingData &&
  32086. updatePoints !== false &&
  32087. dataLength &&
  32088. oldDataLength &&
  32089. !series.cropped &&
  32090. !series.hasGroupedData &&
  32091. series.visible &&
  32092. // Soft updating has no benefit in boost, and causes JS error
  32093. // (#8355)
  32094. !series.boosted) {
  32095. updatedData = this.updateData(data, animation);
  32096. }
  32097. if (!updatedData) {
  32098. // Reset properties
  32099. series.xIncrement = null;
  32100. series.colorCounter = 0; // for series with colorByPoint (#1547)
  32101. // Update parallel arrays
  32102. this.parallelArrays.forEach(function (key) {
  32103. series[key + 'Data'].length = 0;
  32104. });
  32105. // In turbo mode, only one- or twodimensional arrays of numbers
  32106. // are allowed. The first value is tested, and we assume that
  32107. // all the rest are defined the same way. Although the 'for'
  32108. // loops are similar, they are repeated inside each if-else
  32109. // conditional for max performance.
  32110. if (turboThreshold && dataLength > turboThreshold) {
  32111. firstPoint = series.getFirstValidPoint(data);
  32112. if (isNumber(firstPoint)) { // assume all points are numbers
  32113. for (i = 0; i < dataLength; i++) {
  32114. xData[i] = this.autoIncrement();
  32115. yData[i] = data[i];
  32116. }
  32117. // Assume all points are arrays when first point is
  32118. }
  32119. else if (isArray(firstPoint)) {
  32120. if (valueCount) { // [x, low, high] or [x, o, h, l, c]
  32121. if (firstPoint.length === valueCount) {
  32122. for (i = 0; i < dataLength; i++) {
  32123. xData[i] = this.autoIncrement();
  32124. yData[i] = data[i];
  32125. }
  32126. }
  32127. else {
  32128. for (i = 0; i < dataLength; i++) {
  32129. pt = data[i];
  32130. xData[i] = pt[0];
  32131. yData[i] =
  32132. pt.slice(1, valueCount + 1);
  32133. }
  32134. }
  32135. }
  32136. else { // [x, y]
  32137. if (keys) {
  32138. indexOfX = keys.indexOf('x');
  32139. indexOfY = keys.indexOf('y');
  32140. indexOfX = indexOfX >= 0 ? indexOfX : 0;
  32141. indexOfY = indexOfY >= 0 ? indexOfY : 1;
  32142. }
  32143. if (firstPoint.length === 1) {
  32144. indexOfY = 0;
  32145. }
  32146. if (indexOfX === indexOfY) {
  32147. for (i = 0; i < dataLength; i++) {
  32148. xData[i] = this.autoIncrement();
  32149. yData[i] = data[i][indexOfY];
  32150. }
  32151. }
  32152. else {
  32153. for (i = 0; i < dataLength; i++) {
  32154. pt = data[i];
  32155. xData[i] = pt[indexOfX];
  32156. yData[i] = pt[indexOfY];
  32157. }
  32158. }
  32159. }
  32160. }
  32161. else {
  32162. // Highcharts expects configs to be numbers or arrays in
  32163. // turbo mode
  32164. error(12, false, chart);
  32165. }
  32166. }
  32167. else {
  32168. for (i = 0; i < dataLength; i++) {
  32169. pt = { series: series };
  32170. series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
  32171. series.updateParallelArrays(pt, i);
  32172. }
  32173. }
  32174. // Forgetting to cast strings to numbers is a common caveat when
  32175. // handling CSV or JSON
  32176. if (yData && isString(yData[0])) {
  32177. error(14, true, chart);
  32178. }
  32179. series.data = [];
  32180. series.options.data = series.userOptions.data = data;
  32181. // destroy old points
  32182. i = oldDataLength;
  32183. while (i--) {
  32184. (_a = oldData[i]) === null || _a === void 0 ? void 0 : _a.destroy();
  32185. }
  32186. // reset minRange (#878)
  32187. if (xAxis) {
  32188. xAxis.minRange = xAxis.userMinRange;
  32189. }
  32190. // redraw
  32191. series.isDirty = chart.isDirtyBox = true;
  32192. series.isDirtyData = !!oldData;
  32193. animation = false;
  32194. }
  32195. // Typically for pie series, points need to be processed and
  32196. // generated prior to rendering the legend
  32197. if (options.legendType === 'point') {
  32198. this.processData();
  32199. this.generatePoints();
  32200. }
  32201. if (redraw) {
  32202. chart.redraw(animation);
  32203. }
  32204. }
  32205. /**
  32206. * Internal function to sort series data
  32207. *
  32208. * @private
  32209. * @function Highcharts.Series#sortData
  32210. * @param {Array<Highcharts.PointOptionsType>} data
  32211. * Force data grouping.
  32212. */
  32213. sortData(data) {
  32214. const series = this, options = series.options, dataSorting = options.dataSorting, sortKey = dataSorting.sortKey || 'y', getPointOptionsObject = function (series, pointOptions) {
  32215. return (defined(pointOptions) &&
  32216. series.pointClass.prototype.optionsToObject.call({
  32217. series: series
  32218. }, pointOptions)) || {};
  32219. };
  32220. data.forEach(function (pointOptions, i) {
  32221. data[i] = getPointOptionsObject(series, pointOptions);
  32222. data[i].index = i;
  32223. }, this);
  32224. // Sorting
  32225. const sortedData = data.concat().sort((a, b) => {
  32226. const aValue = getNestedProperty(sortKey, a);
  32227. const bValue = getNestedProperty(sortKey, b);
  32228. return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
  32229. });
  32230. // Set x value depending on the position in the array
  32231. sortedData.forEach(function (point, i) {
  32232. point.x = i;
  32233. }, this);
  32234. // Set the same x for linked series points if they don't have their
  32235. // own sorting
  32236. if (series.linkedSeries) {
  32237. series.linkedSeries.forEach(function (linkedSeries) {
  32238. const options = linkedSeries.options, seriesData = options.data;
  32239. if ((!options.dataSorting ||
  32240. !options.dataSorting.enabled) &&
  32241. seriesData) {
  32242. seriesData.forEach(function (pointOptions, i) {
  32243. seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
  32244. if (data[i]) {
  32245. seriesData[i].x = data[i].x;
  32246. seriesData[i].index = i;
  32247. }
  32248. });
  32249. linkedSeries.setData(seriesData, false);
  32250. }
  32251. });
  32252. }
  32253. return data;
  32254. }
  32255. /**
  32256. * Internal function to process the data by cropping away unused data
  32257. * points if the series is longer than the crop threshold. This saves
  32258. * computing time for large series.
  32259. *
  32260. * @private
  32261. * @function Highcharts.Series#getProcessedData
  32262. * @param {boolean} [forceExtremesFromAll]
  32263. * Force getting extremes of a total series data range.
  32264. */
  32265. getProcessedData(forceExtremesFromAll) {
  32266. const series = this, xAxis = series.xAxis, options = series.options, cropThreshold = options.cropThreshold, getExtremesFromAll = forceExtremesFromAll ||
  32267. series.getExtremesFromAll ||
  32268. options.getExtremesFromAll, // #4599
  32269. logarithmic = xAxis === null || xAxis === void 0 ? void 0 : xAxis.logarithmic, isCartesian = series.isCartesian;
  32270. let croppedData, cropped, cropStart = 0, xExtremes, min, max,
  32271. // copied during slice operation:
  32272. processedXData = series.xData, processedYData = series.yData, updatingNames = false;
  32273. const dataLength = processedXData.length;
  32274. if (xAxis) {
  32275. // corrected for log axis (#3053)
  32276. xExtremes = xAxis.getExtremes();
  32277. min = xExtremes.min;
  32278. max = xExtremes.max;
  32279. updatingNames = !!(xAxis.categories && !xAxis.names.length);
  32280. }
  32281. // optionally filter out points outside the plot area
  32282. if (isCartesian &&
  32283. series.sorted &&
  32284. !getExtremesFromAll &&
  32285. (!cropThreshold ||
  32286. dataLength > cropThreshold ||
  32287. series.forceCrop)) {
  32288. // it's outside current extremes
  32289. if (processedXData[dataLength - 1] < min ||
  32290. processedXData[0] > max) {
  32291. processedXData = [];
  32292. processedYData = [];
  32293. // only crop if it's actually spilling out
  32294. }
  32295. else if (series.yData && (processedXData[0] < min ||
  32296. processedXData[dataLength - 1] > max)) {
  32297. croppedData = this.cropData(series.xData, series.yData, min, max);
  32298. processedXData = croppedData.xData;
  32299. processedYData = croppedData.yData;
  32300. cropStart = croppedData.start;
  32301. cropped = true;
  32302. }
  32303. }
  32304. // Find the closest distance between processed points
  32305. const closestPointRange = getClosestDistance([
  32306. logarithmic ?
  32307. processedXData.map(logarithmic.log2lin) :
  32308. processedXData
  32309. ],
  32310. // Unsorted data is not supported by the line tooltip, as well as
  32311. // data grouping and navigation in Stock charts (#725) and width
  32312. // calculation of columns (#1900). Avoid warning during the
  32313. // premature processing pass in updateNames (#16104).
  32314. () => (series.requireSorting &&
  32315. !updatingNames &&
  32316. error(15, false, series.chart)));
  32317. return {
  32318. xData: processedXData,
  32319. yData: processedYData,
  32320. cropped: cropped,
  32321. cropStart: cropStart,
  32322. closestPointRange: closestPointRange
  32323. };
  32324. }
  32325. /**
  32326. * Internal function to apply processed data.
  32327. * In Highcharts Stock, this function is extended to provide data grouping.
  32328. *
  32329. * @private
  32330. * @function Highcharts.Series#processData
  32331. * @param {boolean} [force]
  32332. * Force data grouping.
  32333. */
  32334. processData(force) {
  32335. const series = this, xAxis = series.xAxis;
  32336. // If the series data or axes haven't changed, don't go through
  32337. // this. Return false to pass the message on to override methods
  32338. // like in data grouping.
  32339. if (series.isCartesian &&
  32340. !series.isDirty &&
  32341. !xAxis.isDirty &&
  32342. !series.yAxis.isDirty &&
  32343. !force) {
  32344. return false;
  32345. }
  32346. const processedData = series.getProcessedData();
  32347. // Record the properties
  32348. series.cropped = processedData.cropped; // undefined or true
  32349. series.cropStart = processedData.cropStart;
  32350. series.processedXData = processedData.xData;
  32351. series.processedYData = processedData.yData;
  32352. series.closestPointRange = (series.basePointRange = processedData.closestPointRange);
  32353. fireEvent(series, 'afterProcessData');
  32354. }
  32355. /**
  32356. * Iterate over xData and crop values between min and max. Returns
  32357. * object containing crop start/end cropped xData with corresponding
  32358. * part of yData, dataMin and dataMax within the cropped range.
  32359. *
  32360. * @private
  32361. * @function Highcharts.Series#cropData
  32362. */
  32363. cropData(xData, yData, min, max, cropShoulder) {
  32364. const dataLength = xData.length;
  32365. let i, j, cropStart = 0, cropEnd = dataLength;
  32366. // line-type series need one point outside
  32367. cropShoulder = pick(cropShoulder, this.cropShoulder);
  32368. // iterate up to find slice start
  32369. for (i = 0; i < dataLength; i++) {
  32370. if (xData[i] >= min) {
  32371. cropStart = Math.max(0, i - cropShoulder);
  32372. break;
  32373. }
  32374. }
  32375. // proceed to find slice end
  32376. for (j = i; j < dataLength; j++) {
  32377. if (xData[j] > max) {
  32378. cropEnd = j + cropShoulder;
  32379. break;
  32380. }
  32381. }
  32382. return {
  32383. xData: xData.slice(cropStart, cropEnd),
  32384. yData: yData.slice(cropStart, cropEnd),
  32385. start: cropStart,
  32386. end: cropEnd
  32387. };
  32388. }
  32389. /**
  32390. * Generate the data point after the data has been processed by cropping
  32391. * away unused points and optionally grouped in Highcharts Stock.
  32392. *
  32393. * @private
  32394. * @function Highcharts.Series#generatePoints
  32395. */
  32396. generatePoints() {
  32397. const series = this, options = series.options, dataOptions = (series.processedData || options.data), processedXData = series.processedXData, processedYData = series.processedYData, PointClass = series.pointClass, processedDataLength = processedXData.length, cropStart = series.cropStart || 0, hasGroupedData = series.hasGroupedData, keys = options.keys, points = [], groupCropStartIndex = (options.dataGrouping &&
  32398. options.dataGrouping.groupAll ?
  32399. cropStart :
  32400. 0);
  32401. let dataLength, cursor, point, i, data = series.data;
  32402. if (!data && !hasGroupedData) {
  32403. const arr = [];
  32404. arr.length = dataOptions.length;
  32405. data = series.data = arr;
  32406. }
  32407. if (keys && hasGroupedData) {
  32408. // grouped data has already applied keys (#6590)
  32409. series.options.keys = false;
  32410. }
  32411. for (i = 0; i < processedDataLength; i++) {
  32412. cursor = cropStart + i;
  32413. if (!hasGroupedData) {
  32414. point = data[cursor];
  32415. // #970:
  32416. if (!point &&
  32417. typeof dataOptions[cursor] !== 'undefined') {
  32418. data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
  32419. }
  32420. }
  32421. else {
  32422. // splat the y data in case of ohlc data array
  32423. point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
  32424. point.dataGroup = series.groupMap[groupCropStartIndex + i];
  32425. if (point.dataGroup.options) {
  32426. point.options = point.dataGroup.options;
  32427. extend(point, point.dataGroup.options);
  32428. // Collision of props and options (#9770)
  32429. delete point.dataLabels;
  32430. }
  32431. }
  32432. if (point) { // #6279
  32433. /**
  32434. * Contains the point's index in the `Series.points` array.
  32435. *
  32436. * @name Highcharts.Point#index
  32437. * @type {number}
  32438. * @readonly
  32439. */
  32440. // For faster access in Point.update
  32441. point.index = hasGroupedData ?
  32442. (groupCropStartIndex + i) : cursor;
  32443. points[i] = point;
  32444. }
  32445. }
  32446. // restore keys options (#6590)
  32447. series.options.keys = keys;
  32448. // Hide cropped-away points - this only runs when the number of
  32449. // points is above cropThreshold, or when swithching view from
  32450. // non-grouped data to grouped data (#637)
  32451. if (data &&
  32452. (processedDataLength !== (dataLength = data.length) ||
  32453. hasGroupedData)) {
  32454. for (i = 0; i < dataLength; i++) {
  32455. // when has grouped data, clear all points
  32456. if (i === cropStart && !hasGroupedData) {
  32457. i += processedDataLength;
  32458. }
  32459. if (data[i]) {
  32460. data[i].destroyElements();
  32461. data[i].plotX = void 0; // #1003
  32462. }
  32463. }
  32464. }
  32465. /**
  32466. * Read only. An array containing those values converted to points.
  32467. * In case the series data length exceeds the `cropThreshold`, or if
  32468. * the data is grouped, `series.data` doesn't contain all the
  32469. * points. Also, in case a series is hidden, the `data` array may be
  32470. * empty. To access raw values, `series.options.data` will always be
  32471. * up to date. `Series.data` only contains the points that have been
  32472. * created on demand. To modify the data, use
  32473. * {@link Highcharts.Series#setData} or
  32474. * {@link Highcharts.Point#update}.
  32475. *
  32476. * @see Series.points
  32477. *
  32478. * @name Highcharts.Series#data
  32479. * @type {Array<Highcharts.Point>}
  32480. */
  32481. series.data = data;
  32482. /**
  32483. * An array containing all currently visible point objects. In case
  32484. * of cropping, the cropped-away points are not part of this array.
  32485. * The `series.points` array starts at `series.cropStart` compared
  32486. * to `series.data` and `series.options.data`. If however the series
  32487. * data is grouped, these can't be correlated one to one. To modify
  32488. * the data, use {@link Highcharts.Series#setData} or
  32489. * {@link Highcharts.Point#update}.
  32490. *
  32491. * @name Highcharts.Series#points
  32492. * @type {Array<Highcharts.Point>}
  32493. */
  32494. series.points = points;
  32495. fireEvent(this, 'afterGeneratePoints');
  32496. }
  32497. /**
  32498. * Get current X extremes for the visible data.
  32499. *
  32500. * @private
  32501. * @function Highcharts.Series#getXExtremes
  32502. * @param {Array<number>} xData
  32503. * The data to inspect. Defaults to the current data within the visible
  32504. * range.
  32505. */
  32506. getXExtremes(xData) {
  32507. return {
  32508. min: arrayMin(xData),
  32509. max: arrayMax(xData)
  32510. };
  32511. }
  32512. /**
  32513. * Calculate Y extremes for the visible data. The result is returned
  32514. * as an object with `dataMin` and `dataMax` properties.
  32515. *
  32516. * @private
  32517. * @function Highcharts.Series#getExtremes
  32518. * @param {Array<number>} [yData]
  32519. * The data to inspect. Defaults to the current data within the visible
  32520. * range.
  32521. * @param {boolean} [forceExtremesFromAll]
  32522. * Force getting extremes of a total series data range.
  32523. */
  32524. getExtremes(yData, forceExtremesFromAll) {
  32525. const xAxis = this.xAxis, yAxis = this.yAxis, xData = this.processedXData || this.xData, activeYData = [],
  32526. // Handle X outside the viewed area. This does not work with
  32527. // non-sorted data like scatter (#7639).
  32528. shoulder = this.requireSorting ? this.cropShoulder : 0, positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false;
  32529. // #2117, need to compensate for log X axis
  32530. let xExtremes, validValue, withinRange, x, y, i, j, xMin = 0, xMax = 0, activeCounter = 0;
  32531. yData = yData || this.stackedYData || this.processedYData || [];
  32532. const yDataLength = yData.length;
  32533. if (xAxis) {
  32534. xExtremes = xAxis.getExtremes();
  32535. xMin = xExtremes.min;
  32536. xMax = xExtremes.max;
  32537. }
  32538. for (i = 0; i < yDataLength; i++) {
  32539. x = xData[i];
  32540. y = yData[i];
  32541. // For points within the visible range, including the first
  32542. // point outside the visible range (#7061), consider y extremes.
  32543. validValue = ((isNumber(y) || isArray(y)) &&
  32544. ((y.length || y > 0) || !positiveValuesOnly));
  32545. withinRange = (forceExtremesFromAll ||
  32546. this.getExtremesFromAll ||
  32547. this.options.getExtremesFromAll ||
  32548. this.cropped ||
  32549. !xAxis || // for colorAxis support
  32550. ((xData[i + shoulder] || x) >= xMin &&
  32551. (xData[i - shoulder] || x) <= xMax));
  32552. if (validValue && withinRange) {
  32553. j = y.length;
  32554. if (j) { // array, like ohlc or range data
  32555. while (j--) {
  32556. if (isNumber(y[j])) { // #7380, #11513
  32557. activeYData[activeCounter++] = y[j];
  32558. }
  32559. }
  32560. }
  32561. else {
  32562. activeYData[activeCounter++] = y;
  32563. }
  32564. }
  32565. }
  32566. const dataExtremes = {
  32567. activeYData,
  32568. dataMin: arrayMin(activeYData),
  32569. dataMax: arrayMax(activeYData)
  32570. };
  32571. fireEvent(this, 'afterGetExtremes', { dataExtremes });
  32572. return dataExtremes;
  32573. }
  32574. /**
  32575. * Set the current data extremes as `dataMin` and `dataMax` on the
  32576. * Series item. Use this only when the series properties should be
  32577. * updated.
  32578. *
  32579. * @private
  32580. * @function Highcharts.Series#applyExtremes
  32581. */
  32582. applyExtremes() {
  32583. const dataExtremes = this.getExtremes();
  32584. /**
  32585. * Contains the minimum value of the series' data point. Some series
  32586. * types like `networkgraph` do not support this property as they
  32587. * lack a `y`-value.
  32588. * @name Highcharts.Series#dataMin
  32589. * @type {number|undefined}
  32590. * @readonly
  32591. */
  32592. this.dataMin = dataExtremes.dataMin;
  32593. /**
  32594. * Contains the maximum value of the series' data point. Some series
  32595. * types like `networkgraph` do not support this property as they
  32596. * lack a `y`-value.
  32597. * @name Highcharts.Series#dataMax
  32598. * @type {number|undefined}
  32599. * @readonly
  32600. */
  32601. this.dataMax = dataExtremes.dataMax;
  32602. return dataExtremes;
  32603. }
  32604. /**
  32605. * Find and return the first non null point in the data
  32606. *
  32607. * @private
  32608. * @function Highcharts.Series.getFirstValidPoint
  32609. * @param {Array<Highcharts.PointOptionsType>} data
  32610. * Array of options for points
  32611. */
  32612. getFirstValidPoint(data) {
  32613. const dataLength = data.length;
  32614. let i = 0, firstPoint = null;
  32615. while (firstPoint === null && i < dataLength) {
  32616. firstPoint = data[i];
  32617. i++;
  32618. }
  32619. return firstPoint;
  32620. }
  32621. /**
  32622. * Translate data points from raw data values to chart specific
  32623. * positioning data needed later in the `drawPoints` and `drawGraph`
  32624. * functions. This function can be overridden in plugins and custom
  32625. * series type implementations.
  32626. *
  32627. * @function Highcharts.Series#translate
  32628. *
  32629. * @emits Highcharts.Series#events:translate
  32630. */
  32631. translate() {
  32632. var _a;
  32633. if (!this.processedXData) { // hidden series
  32634. this.processData();
  32635. }
  32636. this.generatePoints();
  32637. const series = this, options = series.options, stacking = options.stacking, xAxis = series.xAxis, categories = xAxis.categories, enabledDataSorting = series.enabledDataSorting, yAxis = series.yAxis, points = series.points, dataLength = points.length, pointPlacement = series.pointPlacementToXValue(), // #7860
  32638. dynamicallyPlaced = Boolean(pointPlacement), threshold = options.threshold, stackThreshold = options.startFromThreshold ? threshold : 0;
  32639. let i, plotX, lastPlotX, stackIndicator, closestPointRangePx = Number.MAX_VALUE;
  32640. /**
  32641. * Plotted coordinates need to be within a limited range. Drawing
  32642. * too far outside the viewport causes various rendering issues
  32643. * (#3201, #3923, #7555).
  32644. * @private
  32645. */
  32646. function limitedRange(val) {
  32647. return clamp(val, -1e5, 1e5);
  32648. }
  32649. // Translate each point
  32650. for (i = 0; i < dataLength; i++) {
  32651. const point = points[i], xValue = point.x;
  32652. let stackItem, stackValues, yValue = point.y, lowValue = point.low;
  32653. const stacks = stacking && ((_a = yAxis.stacking) === null || _a === void 0 ? void 0 : _a.stacks[(series.negStacks &&
  32654. yValue <
  32655. (stackThreshold ? 0 : threshold) ?
  32656. '-' :
  32657. '') + series.stackKey]);
  32658. plotX = xAxis.translate(// #3923
  32659. xValue, false, false, false, true, pointPlacement);
  32660. /**
  32661. * The translated X value for the point in terms of pixels. Relative
  32662. * to the X axis position if the series has one, otherwise relative
  32663. * to the plot area. Depending on the series type this value might
  32664. * not be defined.
  32665. * @name Highcharts.Point#plotX
  32666. * @type {number|undefined}
  32667. */
  32668. point.plotX = isNumber(plotX) ? correctFloat(// #5236
  32669. limitedRange(plotX) // #3923
  32670. ) : void 0;
  32671. // Calculate the bottom y value for stacked series
  32672. if (stacking &&
  32673. series.visible &&
  32674. stacks &&
  32675. stacks[xValue]) {
  32676. stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
  32677. if (!point.isNull && stackIndicator.key) {
  32678. stackItem = stacks[xValue];
  32679. stackValues = stackItem.points[stackIndicator.key];
  32680. }
  32681. if (stackItem && isArray(stackValues)) {
  32682. lowValue = stackValues[0];
  32683. yValue = stackValues[1];
  32684. if (lowValue === stackThreshold &&
  32685. stackIndicator.key === stacks[xValue].base) {
  32686. lowValue = pick(isNumber(threshold) ? threshold : yAxis.min);
  32687. }
  32688. // #1200, #1232
  32689. if (yAxis.positiveValuesOnly &&
  32690. defined(lowValue) &&
  32691. lowValue <= 0) {
  32692. lowValue = void 0;
  32693. }
  32694. point.total = point.stackTotal = pick(stackItem.total);
  32695. point.percentage = defined(point.y) && stackItem.total ?
  32696. (point.y / stackItem.total * 100) : void 0;
  32697. point.stackY = yValue;
  32698. // in case of variwide series (where widths of points are
  32699. // different in most cases), stack labels are positioned
  32700. // wrongly, so the call of the setOffset is omited here and
  32701. // labels are correctly positioned later, at the end of the
  32702. // variwide's translate function (#10962)
  32703. if (!series.irregularWidths) {
  32704. stackItem.setOffset(series.pointXOffset || 0, series.barW || 0, void 0, void 0, void 0, series.xAxis);
  32705. }
  32706. }
  32707. }
  32708. // Set translated yBottom or remove it
  32709. point.yBottom = defined(lowValue) ?
  32710. limitedRange(yAxis.translate(lowValue, false, true, false, true)) :
  32711. void 0;
  32712. // General hook, used for Highcharts Stock compare and cumulative
  32713. if (series.dataModify) {
  32714. yValue = series.dataModify.modifyValue(yValue, i);
  32715. }
  32716. // Set the the plotY value, reset it for redraws #3201, #18422
  32717. let plotY;
  32718. if (isNumber(yValue) && point.plotX !== void 0) {
  32719. plotY = yAxis.translate(yValue, false, true, false, true);
  32720. plotY = isNumber(plotY) ? limitedRange(plotY) : void 0;
  32721. }
  32722. /**
  32723. * The translated Y value for the point in terms of pixels. Relative
  32724. * to the Y axis position if the series has one, otherwise relative
  32725. * to the plot area. Depending on the series type this value might
  32726. * not be defined.
  32727. * @name Highcharts.Point#plotY
  32728. * @type {number|undefined}
  32729. */
  32730. point.plotY = plotY;
  32731. point.isInside = this.isPointInside(point);
  32732. // Set client related positions for mouse tracking
  32733. point.clientX = dynamicallyPlaced ?
  32734. correctFloat(xAxis.translate(xValue, false, false, false, true, pointPlacement)) :
  32735. plotX; // #1514, #5383, #5518
  32736. // Negative points #19028
  32737. point.negative = (point.y || 0) < (threshold || 0);
  32738. // some API data
  32739. point.category = pick(categories && categories[point.x], point.x);
  32740. // Determine auto enabling of markers (#3635, #5099)
  32741. if (!point.isNull && point.visible !== false) {
  32742. if (typeof lastPlotX !== 'undefined') {
  32743. closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
  32744. }
  32745. lastPlotX = plotX;
  32746. }
  32747. // Find point zone
  32748. point.zone = this.zones.length ? point.getZone() : void 0;
  32749. // Animate new points with data sorting
  32750. if (!point.graphic && series.group && enabledDataSorting) {
  32751. point.isNew = true;
  32752. }
  32753. }
  32754. series.closestPointRangePx = closestPointRangePx;
  32755. fireEvent(this, 'afterTranslate');
  32756. }
  32757. /**
  32758. * Return the series points with null points filtered out.
  32759. *
  32760. * @function Highcharts.Series#getValidPoints
  32761. *
  32762. * @param {Array<Highcharts.Point>} [points]
  32763. * The points to inspect, defaults to {@link Series.points}.
  32764. *
  32765. * @param {boolean} [insideOnly=false]
  32766. * Whether to inspect only the points that are inside the visible view.
  32767. *
  32768. * @param {boolean} [allowNull=false]
  32769. * Whether to allow null points to pass as valid points.
  32770. *
  32771. * @return {Array<Highcharts.Point>}
  32772. * The valid points.
  32773. */
  32774. getValidPoints(points, insideOnly, allowNull) {
  32775. const chart = this.chart;
  32776. // #3916, #5029, #5085
  32777. return (points || this.points || []).filter(function (point) {
  32778. const { plotX, plotY } = point,
  32779. // Undefined plotY is treated as null when negative values
  32780. // in log axis (#18422)
  32781. asNull = !allowNull && (point.isNull || !isNumber(plotY));
  32782. if (asNull || (insideOnly && !chart.isInsidePlot(plotX, plotY, { inverted: chart.inverted }))) {
  32783. return false;
  32784. }
  32785. return point.visible !== false;
  32786. });
  32787. }
  32788. /**
  32789. * Get the clipping for the series. Could be called for a series to
  32790. * initiate animating the clip or to set the final clip (only width
  32791. * and x).
  32792. *
  32793. * @private
  32794. * @function Highcharts.Series#getClip
  32795. */
  32796. getClipBox() {
  32797. const { chart, xAxis, yAxis } = this;
  32798. // If no axes on the series, use global clipBox
  32799. const seriesBox = merge(chart.clipBox);
  32800. // Otherwise, use clipBox.width which is corrected for plotBorderWidth
  32801. // and clipOffset
  32802. if (xAxis && xAxis.len !== chart.plotSizeX) {
  32803. seriesBox.width = xAxis.len;
  32804. }
  32805. if (yAxis && yAxis.len !== chart.plotSizeY) {
  32806. seriesBox.height = yAxis.len;
  32807. }
  32808. return seriesBox;
  32809. }
  32810. /**
  32811. * Get the shared clip key, creating it if it doesn't exist.
  32812. *
  32813. * @private
  32814. * @function Highcharts.Series#getSharedClipKey
  32815. */
  32816. getSharedClipKey() {
  32817. this.sharedClipKey = (this.options.xAxis || 0) + ',' +
  32818. (this.options.yAxis || 0);
  32819. return this.sharedClipKey;
  32820. }
  32821. /**
  32822. * Set the clipping for the series. For animated series the clip is later
  32823. * modified.
  32824. *
  32825. * @private
  32826. * @function Highcharts.Series#setClip
  32827. */
  32828. setClip() {
  32829. const { chart, group, markerGroup } = this, sharedClips = chart.sharedClips, renderer = chart.renderer, clipBox = this.getClipBox(), sharedClipKey = this.getSharedClipKey(); // #4526
  32830. let clipRect = sharedClips[sharedClipKey];
  32831. // If a clipping rectangle for the same set of axes does not exist,
  32832. // create it
  32833. if (!clipRect) {
  32834. sharedClips[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
  32835. // When setting chart size, or when the series is rendered again before
  32836. // starting animating, in compliance to a responsive rule
  32837. }
  32838. else {
  32839. clipRect.animate(clipBox);
  32840. }
  32841. if (group) {
  32842. // When clip is false, reset to no clip after animation
  32843. group.clip(this.options.clip === false ? void 0 : clipRect);
  32844. }
  32845. // Unclip temporary animation clip
  32846. if (markerGroup) {
  32847. markerGroup.clip();
  32848. }
  32849. }
  32850. /**
  32851. * Animate in the series. Called internally twice. First with the `init`
  32852. * parameter set to true, which sets up the initial state of the
  32853. * animation. Then when ready, it is called with the `init` parameter
  32854. * undefined, in order to perform the actual animation.
  32855. *
  32856. * @function Highcharts.Series#animate
  32857. *
  32858. * @param {boolean} [init]
  32859. * Initialize the animation.
  32860. */
  32861. animate(init) {
  32862. const { chart, group, markerGroup } = this, inverted = chart.inverted, animation = animObject(this.options.animation),
  32863. // The key for temporary animation clips
  32864. animationClipKey = [
  32865. this.getSharedClipKey(),
  32866. animation.duration,
  32867. animation.easing,
  32868. animation.defer
  32869. ].join(',');
  32870. let animationClipRect = chart.sharedClips[animationClipKey], markerAnimationClipRect = chart.sharedClips[animationClipKey + 'm'];
  32871. // Initialize the animation. Set up the clipping rectangle.
  32872. if (init && group) {
  32873. const clipBox = this.getClipBox();
  32874. // Create temporary animation clips
  32875. if (!animationClipRect) {
  32876. clipBox.width = 0;
  32877. if (inverted) {
  32878. clipBox.x = chart.plotHeight;
  32879. }
  32880. animationClipRect = chart.renderer.clipRect(clipBox);
  32881. chart.sharedClips[animationClipKey] = animationClipRect;
  32882. // The marker clip box. The number 99 is a safe margin to avoid
  32883. // markers being clipped during animation.
  32884. const markerClipBox = {
  32885. x: inverted ? -99 : -99,
  32886. y: inverted ? -99 : -99,
  32887. width: inverted ? chart.plotWidth + 199 : 99,
  32888. height: inverted ? 99 : chart.plotHeight + 199
  32889. };
  32890. markerAnimationClipRect = chart.renderer.clipRect(markerClipBox);
  32891. chart.sharedClips[animationClipKey + 'm'] = markerAnimationClipRect;
  32892. }
  32893. else {
  32894. // When height changes during animation, typically due to
  32895. // responsive settings
  32896. animationClipRect.attr('height', clipBox.height);
  32897. }
  32898. group.clip(animationClipRect);
  32899. if (markerGroup) {
  32900. markerGroup.clip(markerAnimationClipRect);
  32901. }
  32902. // Run the animation
  32903. }
  32904. else if (animationClipRect &&
  32905. // Only first series in this pane
  32906. !animationClipRect.hasClass('highcharts-animating')) {
  32907. const finalBox = this.getClipBox(), step = animation.step;
  32908. // Only do this when there are actually markers
  32909. if (markerGroup && markerGroup.element.childNodes.length) {
  32910. // To provide as smooth animation as possible, update the marker
  32911. // group clipping in steps of the main group animation
  32912. animation.step = function (val, fx) {
  32913. if (step) {
  32914. step.apply(fx, arguments);
  32915. }
  32916. if (fx.prop === 'width' &&
  32917. markerAnimationClipRect &&
  32918. markerAnimationClipRect.element) {
  32919. markerAnimationClipRect.attr(inverted ? 'height' : 'width', val + 99);
  32920. }
  32921. };
  32922. }
  32923. animationClipRect
  32924. .addClass('highcharts-animating')
  32925. .animate(finalBox, animation);
  32926. }
  32927. }
  32928. /**
  32929. * This runs after animation to land on the final plot clipping.
  32930. *
  32931. * @private
  32932. * @function Highcharts.Series#afterAnimate
  32933. *
  32934. * @emits Highcharts.Series#event:afterAnimate
  32935. */
  32936. afterAnimate() {
  32937. this.setClip();
  32938. // Destroy temporary clip rectangles that are no longer in use
  32939. objectEach(this.chart.sharedClips, (clip, key, sharedClips) => {
  32940. if (clip && !this.chart.container.querySelector(`[clip-path="url(#${clip.id})"]`)) {
  32941. clip.destroy();
  32942. delete sharedClips[key];
  32943. }
  32944. });
  32945. this.finishedAnimating = true;
  32946. fireEvent(this, 'afterAnimate');
  32947. }
  32948. /**
  32949. * Draw the markers for line-like series types, and columns or other
  32950. * graphical representation for {@link Point} objects for other series
  32951. * types. The resulting element is typically stored as
  32952. * {@link Point.graphic}, and is created on the first call and updated
  32953. * and moved on subsequent calls.
  32954. *
  32955. * @function Highcharts.Series#drawPoints
  32956. */
  32957. drawPoints(points = this.points) {
  32958. const series = this, chart = series.chart, styledMode = chart.styledMode, { colorAxis, options } = series, seriesMarkerOptions = options.marker, markerGroup = series[series.specialGroup || 'markerGroup'], xAxis = series.xAxis, globallyEnabled = pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null,
  32959. // Use larger or equal as radius is null in bubbles (#6321)
  32960. series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
  32961. seriesMarkerOptions.radius));
  32962. let i, point, graphic, verb, pointMarkerOptions, hasPointMarker, markerAttribs;
  32963. if (seriesMarkerOptions.enabled !== false ||
  32964. series._hasPointMarkers) {
  32965. for (i = 0; i < points.length; i++) {
  32966. point = points[i];
  32967. graphic = point.graphic;
  32968. verb = graphic ? 'animate' : 'attr';
  32969. pointMarkerOptions = point.marker || {};
  32970. hasPointMarker = !!point.marker;
  32971. const shouldDrawMarker = ((globallyEnabled &&
  32972. typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
  32973. // only draw the point if y is defined
  32974. if (shouldDrawMarker) {
  32975. // Shortcuts
  32976. const symbol = pick(pointMarkerOptions.symbol, series.symbol, 'rect');
  32977. markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
  32978. // Set starting position for point sliding animation.
  32979. if (series.enabledDataSorting) {
  32980. point.startXPos = xAxis.reversed ?
  32981. -(markerAttribs.width || 0) :
  32982. xAxis.width;
  32983. }
  32984. const isInside = point.isInside !== false;
  32985. if (!graphic &&
  32986. isInside &&
  32987. ((markerAttribs.width || 0) > 0 || point.hasImage)) {
  32988. /**
  32989. * SVG graphic representing the point in the chart. In
  32990. * some cases it may be a hidden graphic to improve
  32991. * accessibility.
  32992. *
  32993. * Typically this is a simple shape, like a `rect`
  32994. * for column charts or `path` for line markers, but
  32995. * for some complex series types like boxplot or 3D
  32996. * charts, the graphic may be a `g` element
  32997. * containing other shapes. The graphic is generated
  32998. * the first time {@link Series#drawPoints} runs,
  32999. * and updated and moved on subsequent runs.
  33000. *
  33001. * @see Highcharts.Point#graphics
  33002. *
  33003. * @name Highcharts.Point#graphic
  33004. * @type {Highcharts.SVGElement|undefined}
  33005. */
  33006. point.graphic = graphic = chart.renderer
  33007. .symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
  33008. pointMarkerOptions :
  33009. seriesMarkerOptions)
  33010. .add(markerGroup);
  33011. // Sliding animation for new points
  33012. if (series.enabledDataSorting &&
  33013. chart.hasRendered) {
  33014. graphic.attr({
  33015. x: point.startXPos
  33016. });
  33017. verb = 'animate';
  33018. }
  33019. }
  33020. if (graphic && verb === 'animate') { // update
  33021. // Since the marker group isn't clipped, each
  33022. // individual marker must be toggled
  33023. graphic[isInside ? 'show' : 'hide'](isInside)
  33024. .animate(markerAttribs);
  33025. }
  33026. // Presentational attributes
  33027. if (graphic) {
  33028. const pointAttr = series.pointAttribs(point, ((styledMode || !point.selected) ?
  33029. void 0 :
  33030. 'select'));
  33031. if (!styledMode) {
  33032. graphic[verb](pointAttr);
  33033. }
  33034. else if (colorAxis) { // #14114
  33035. graphic['css']({
  33036. fill: pointAttr.fill
  33037. });
  33038. }
  33039. }
  33040. if (graphic) {
  33041. graphic.addClass(point.getClassName(), true);
  33042. }
  33043. }
  33044. else if (graphic) {
  33045. point.graphic = graphic.destroy(); // #1269
  33046. }
  33047. }
  33048. }
  33049. }
  33050. /**
  33051. * Get non-presentational attributes for a point. Used internally for
  33052. * both styled mode and classic. Can be overridden for different series
  33053. * types.
  33054. *
  33055. * @see Series#pointAttribs
  33056. *
  33057. * @function Highcharts.Series#markerAttribs
  33058. *
  33059. * @param {Highcharts.Point} point
  33060. * The Point to inspect.
  33061. *
  33062. * @param {string} [state]
  33063. * The state, can be either `hover`, `select` or undefined.
  33064. *
  33065. * @return {Highcharts.SVGAttributes}
  33066. * A hash containing those attributes that are not settable from CSS.
  33067. */
  33068. markerAttribs(point, state) {
  33069. const seriesOptions = this.options, seriesMarkerOptions = seriesOptions.marker, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
  33070. seriesMarkerOptions.symbol), attribs = {};
  33071. let seriesStateOptions, pointStateOptions, radius = pick(pointMarkerOptions.radius, seriesMarkerOptions && seriesMarkerOptions.radius);
  33072. // Handle hover and select states
  33073. if (state) {
  33074. seriesStateOptions = seriesMarkerOptions.states[state];
  33075. pointStateOptions = pointMarkerOptions.states &&
  33076. pointMarkerOptions.states[state];
  33077. radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius && radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
  33078. 0));
  33079. }
  33080. point.hasImage = symbol && symbol.indexOf('url') === 0;
  33081. if (point.hasImage) {
  33082. radius = 0; // and subsequently width and height is not set
  33083. }
  33084. const pos = point.pos();
  33085. if (isNumber(radius) && pos) {
  33086. attribs.x = pos[0] - radius;
  33087. attribs.y = pos[1] - radius;
  33088. if (seriesOptions.crisp) {
  33089. // Math.floor for #1843:
  33090. attribs.x = Math.floor(attribs.x);
  33091. }
  33092. }
  33093. if (radius) {
  33094. attribs.width = attribs.height = 2 * radius;
  33095. }
  33096. return attribs;
  33097. }
  33098. /**
  33099. * Internal function to get presentational attributes for each point.
  33100. * Unlike {@link Series#markerAttribs}, this function should return
  33101. * those attributes that can also be set in CSS. In styled mode,
  33102. * `pointAttribs` won't be called.
  33103. *
  33104. * @private
  33105. * @function Highcharts.Series#pointAttribs
  33106. *
  33107. * @param {Highcharts.Point} [point]
  33108. * The point instance to inspect.
  33109. *
  33110. * @param {string} [state]
  33111. * The point state, can be either `hover`, `select` or 'normal'. If
  33112. * undefined, normal state is assumed.
  33113. *
  33114. * @return {Highcharts.SVGAttributes}
  33115. * The presentational attributes to be set on the point.
  33116. */
  33117. pointAttribs(point, state) {
  33118. const seriesMarkerOptions = this.options.marker, pointOptions = point && point.options, pointMarkerOptions = ((pointOptions && pointOptions.marker) || {}), pointColorOption = pointOptions && pointOptions.color, pointColor = point && point.color, zoneColor = point && point.zone && point.zone.color;
  33119. let seriesStateOptions, pointStateOptions, color = this.color, fill, stroke, strokeWidth = pick(pointMarkerOptions.lineWidth, seriesMarkerOptions.lineWidth), opacity = 1;
  33120. color = (pointColorOption ||
  33121. zoneColor ||
  33122. pointColor ||
  33123. color);
  33124. fill = (pointMarkerOptions.fillColor ||
  33125. seriesMarkerOptions.fillColor ||
  33126. color);
  33127. stroke = (pointMarkerOptions.lineColor ||
  33128. seriesMarkerOptions.lineColor ||
  33129. color);
  33130. // Handle hover and select states
  33131. state = state || 'normal';
  33132. if (state) {
  33133. seriesStateOptions = (seriesMarkerOptions.states[state] || {});
  33134. pointStateOptions = (pointMarkerOptions.states &&
  33135. pointMarkerOptions.states[state]) || {};
  33136. strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
  33137. fill = (pointStateOptions.fillColor ||
  33138. seriesStateOptions.fillColor ||
  33139. fill);
  33140. stroke = (pointStateOptions.lineColor ||
  33141. seriesStateOptions.lineColor ||
  33142. stroke);
  33143. opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
  33144. }
  33145. return {
  33146. 'stroke': stroke,
  33147. 'stroke-width': strokeWidth,
  33148. 'fill': fill,
  33149. 'opacity': opacity
  33150. };
  33151. }
  33152. /**
  33153. * Clear DOM objects and free up memory.
  33154. *
  33155. * @private
  33156. * @function Highcharts.Series#destroy
  33157. *
  33158. * @emits Highcharts.Series#event:destroy
  33159. */
  33160. destroy(keepEventsForUpdate) {
  33161. const series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent), data = series.data || [];
  33162. let destroy, i, point, axis;
  33163. // add event hook
  33164. fireEvent(series, 'destroy', { keepEventsForUpdate });
  33165. // remove events
  33166. this.removeEvents(keepEventsForUpdate);
  33167. // erase from axes
  33168. (series.axisTypes || []).forEach(function (AXIS) {
  33169. axis = series[AXIS];
  33170. if (axis && axis.series) {
  33171. erase(axis.series, series);
  33172. axis.isDirty = axis.forceRedraw = true;
  33173. }
  33174. });
  33175. // remove legend items
  33176. if (series.legendItem) {
  33177. series.chart.legend.destroyItem(series);
  33178. }
  33179. // destroy all points with their elements
  33180. i = data.length;
  33181. while (i--) {
  33182. point = data[i];
  33183. if (point && point.destroy) {
  33184. point.destroy();
  33185. }
  33186. }
  33187. if (series.clips) {
  33188. series.clips.forEach((clip) => clip.destroy());
  33189. }
  33190. // Clear the animation timeout if we are destroying the series
  33191. // during initial animation
  33192. U.clearTimeout(series.animationTimeout);
  33193. // Destroy all SVGElements associated to the series
  33194. objectEach(series, function (val, prop) {
  33195. // Survive provides a hook for not destroying
  33196. if (val instanceof SVGElement && !val.survive) {
  33197. // issue 134 workaround
  33198. destroy = issue134 && prop === 'group' ?
  33199. 'hide' :
  33200. 'destroy';
  33201. val[destroy]();
  33202. }
  33203. });
  33204. // remove from hoverSeries
  33205. if (chart.hoverSeries === series) {
  33206. chart.hoverSeries = void 0;
  33207. }
  33208. erase(chart.series, series);
  33209. chart.orderItems('series');
  33210. // clear all members
  33211. objectEach(series, function (val, prop) {
  33212. if (!keepEventsForUpdate || prop !== 'hcEvents') {
  33213. delete series[prop];
  33214. }
  33215. });
  33216. }
  33217. /**
  33218. * Clip the graphs into zones for colors and styling.
  33219. *
  33220. * @private
  33221. * @function Highcharts.Series#applyZones
  33222. */
  33223. applyZones() {
  33224. const series = this, chart = this.chart, renderer = chart.renderer, zones = this.zones, clips = (this.clips || []), graph = this.graph, area = this.area, plotSizeMax = Math.max(chart.plotWidth, chart.plotHeight), axis = this[(this.zoneAxis || 'y') + 'Axis'], inverted = chart.inverted;
  33225. let translatedFrom, translatedTo, clipAttr, extremes, reversed, horiz, pxRange, pxPosMin, pxPosMax, zoneArea, zoneGraph, ignoreZones = false;
  33226. if (zones.length &&
  33227. (graph || area) &&
  33228. axis &&
  33229. typeof axis.min !== 'undefined') {
  33230. reversed = axis.reversed;
  33231. horiz = axis.horiz;
  33232. // The use of the Color Threshold assumes there are no gaps
  33233. // so it is safe to hide the original graph and area
  33234. // unless it is not waterfall series, then use showLine property
  33235. // to set lines between columns to be visible (#7862)
  33236. if (graph && !this.showLine) {
  33237. graph.hide();
  33238. }
  33239. if (area) {
  33240. area.hide();
  33241. }
  33242. // Create the clips
  33243. extremes = axis.getExtremes();
  33244. zones.forEach(function (threshold, i) {
  33245. translatedFrom = reversed ?
  33246. (horiz ? chart.plotWidth : 0) :
  33247. (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
  33248. translatedFrom = clamp(pick(translatedTo, translatedFrom), 0, plotSizeMax);
  33249. translatedTo = clamp(Math.round(axis.toPixels(pick(threshold.value, extremes.max), true) || 0), 0, plotSizeMax);
  33250. if (ignoreZones) {
  33251. translatedFrom = translatedTo =
  33252. axis.toPixels(extremes.max);
  33253. }
  33254. pxRange = Math.abs(translatedFrom - translatedTo);
  33255. pxPosMin = Math.min(translatedFrom, translatedTo);
  33256. pxPosMax = Math.max(translatedFrom, translatedTo);
  33257. if (axis.isXAxis) {
  33258. clipAttr = {
  33259. x: inverted ? pxPosMax : pxPosMin,
  33260. y: 0,
  33261. width: pxRange,
  33262. height: plotSizeMax
  33263. };
  33264. if (!horiz) {
  33265. clipAttr.x = chart.plotHeight - clipAttr.x;
  33266. }
  33267. }
  33268. else {
  33269. clipAttr = {
  33270. x: 0,
  33271. y: inverted ? pxPosMax : pxPosMin,
  33272. width: plotSizeMax,
  33273. height: pxRange
  33274. };
  33275. if (horiz) {
  33276. clipAttr.y = chart.plotWidth - clipAttr.y;
  33277. }
  33278. }
  33279. if (clips[i]) {
  33280. clips[i].animate(clipAttr);
  33281. }
  33282. else {
  33283. clips[i] = renderer.clipRect(clipAttr);
  33284. }
  33285. // when no data, graph zone is not applied and after setData
  33286. // clip was ignored. As a result, it should be applied each
  33287. // time.
  33288. zoneArea = series['zone-area-' + i];
  33289. zoneGraph = series['zone-graph-' + i];
  33290. if (graph && zoneGraph) {
  33291. zoneGraph.clip(clips[i]);
  33292. }
  33293. if (area && zoneArea) {
  33294. zoneArea.clip(clips[i]);
  33295. }
  33296. // if this zone extends out of the axis, ignore the others
  33297. ignoreZones = threshold.value > extremes.max;
  33298. // Clear translatedTo for indicators
  33299. if (series.resetZones && translatedTo === 0) {
  33300. translatedTo = void 0;
  33301. }
  33302. });
  33303. this.clips = clips;
  33304. }
  33305. else if (series.visible) {
  33306. // If zones were removed, restore graph and area
  33307. if (graph) {
  33308. graph.show();
  33309. }
  33310. if (area) {
  33311. area.show();
  33312. }
  33313. }
  33314. }
  33315. /**
  33316. * General abstraction for creating plot groups like series.group,
  33317. * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
  33318. * the group will only be adjusted to the updated plot size.
  33319. *
  33320. * @private
  33321. * @function Highcharts.Series#plotGroup
  33322. */
  33323. plotGroup(prop, name, visibility, zIndex, parent) {
  33324. let group = this[prop];
  33325. const isNew = !group, attrs = {
  33326. visibility,
  33327. zIndex: zIndex || 0.1 // Pointer logic uses this
  33328. };
  33329. // Avoid setting undefined opacity, or in styled mode
  33330. if (typeof this.opacity !== 'undefined' &&
  33331. !this.chart.styledMode && this.state !== 'inactive' // #13719
  33332. ) {
  33333. attrs.opacity = this.opacity;
  33334. }
  33335. // Generate it on first call
  33336. if (isNew) {
  33337. this[prop] = group = this.chart.renderer
  33338. .g()
  33339. .add(parent);
  33340. }
  33341. // Add the class names, and replace existing ones as response to
  33342. // Series.update (#6660)
  33343. group.addClass(('highcharts-' + name +
  33344. ' highcharts-series-' + this.index +
  33345. ' highcharts-' + this.type + '-series ' +
  33346. (defined(this.colorIndex) ?
  33347. 'highcharts-color-' + this.colorIndex + ' ' :
  33348. '') +
  33349. (this.options.className || '') +
  33350. (group.hasClass('highcharts-tracker') ?
  33351. ' highcharts-tracker' :
  33352. '')), true);
  33353. // Place it on first and subsequent (redraw) calls
  33354. group.attr(attrs)[isNew ? 'attr' : 'animate'](this.getPlotBox(name));
  33355. return group;
  33356. }
  33357. /**
  33358. * Get the translation and scale for the plot area of this series.
  33359. *
  33360. * @function Highcharts.Series#getPlotBox
  33361. */
  33362. getPlotBox(name) {
  33363. let horAxis = this.xAxis, vertAxis = this.yAxis;
  33364. const chart = this.chart, inverted = (chart.inverted &&
  33365. !chart.polar &&
  33366. horAxis &&
  33367. this.invertible !== false &&
  33368. name === 'series');
  33369. // Swap axes for inverted (#2339)
  33370. if (chart.inverted) {
  33371. horAxis = vertAxis;
  33372. vertAxis = this.xAxis;
  33373. }
  33374. return {
  33375. translateX: horAxis ? horAxis.left : chart.plotLeft,
  33376. translateY: vertAxis ? vertAxis.top : chart.plotTop,
  33377. rotation: inverted ? 90 : 0,
  33378. rotationOriginX: inverted ?
  33379. (horAxis.len - vertAxis.len) / 2 :
  33380. 0,
  33381. rotationOriginY: inverted ?
  33382. (horAxis.len + vertAxis.len) / 2 :
  33383. 0,
  33384. scaleX: inverted ? -1 : 1,
  33385. scaleY: 1
  33386. };
  33387. }
  33388. /**
  33389. * Removes the event handlers attached previously with addEvents.
  33390. * @private
  33391. * @function Highcharts.Series#removeEvents
  33392. */
  33393. removeEvents(keepEventsForUpdate) {
  33394. const series = this;
  33395. if (!keepEventsForUpdate) {
  33396. // remove all events
  33397. removeEvent(series);
  33398. }
  33399. if (series.eventsToUnbind.length) {
  33400. // remove only internal events for proper update
  33401. // #12355 - solves problem with multiple destroy events
  33402. series.eventsToUnbind.forEach(function (unbind) {
  33403. unbind();
  33404. });
  33405. series.eventsToUnbind.length = 0;
  33406. }
  33407. }
  33408. /**
  33409. * Render the graph and markers. Called internally when first rendering
  33410. * and later when redrawing the chart. This function can be extended in
  33411. * plugins, but normally shouldn't be called directly.
  33412. *
  33413. * @function Highcharts.Series#render
  33414. *
  33415. * @emits Highcharts.Series#event:afterRender
  33416. */
  33417. render() {
  33418. const series = this, chart = series.chart, options = series.options, animOptions = animObject(options.animation), visibility = series.visible ?
  33419. 'inherit' : 'hidden', // #2597
  33420. zIndex = options.zIndex, hasRendered = series.hasRendered, chartSeriesGroup = chart.seriesGroup, inverted = chart.inverted;
  33421. let animDuration = (!series.finishedAnimating) ?
  33422. animOptions.duration : 0;
  33423. fireEvent(this, 'render');
  33424. // the group
  33425. const group = series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
  33426. series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
  33427. // Initial clipping, applies to columns etc. (#3839).
  33428. if (options.clip !== false) {
  33429. series.setClip();
  33430. }
  33431. // Initialize the animation
  33432. if (series.animate && animDuration) {
  33433. series.animate(true);
  33434. }
  33435. // Draw the graph if any
  33436. if (series.drawGraph) {
  33437. series.drawGraph();
  33438. series.applyZones();
  33439. }
  33440. // Draw the points
  33441. if (series.visible) {
  33442. series.drawPoints();
  33443. }
  33444. // Draw the data labels
  33445. if (series.drawDataLabels) {
  33446. series.drawDataLabels();
  33447. }
  33448. // In pie charts, slices are added to the DOM, but actual rendering
  33449. // is postponed until labels reserved their space
  33450. if (series.redrawPoints) {
  33451. series.redrawPoints();
  33452. }
  33453. // Draw the mouse tracking area
  33454. if (series.drawTracker &&
  33455. options.enableMouseTracking) {
  33456. series.drawTracker();
  33457. }
  33458. // Run the animation
  33459. if (series.animate && animDuration) {
  33460. series.animate();
  33461. }
  33462. // Call the afterAnimate function on animation complete (but don't
  33463. // overwrite the animation.complete option which should be available
  33464. // to the user).
  33465. if (!hasRendered) {
  33466. // Additional time if defer is defined before afterAnimate
  33467. // will be triggered
  33468. if (animDuration && animOptions.defer) {
  33469. animDuration += animOptions.defer;
  33470. }
  33471. series.animationTimeout = syncTimeout(function () {
  33472. series.afterAnimate();
  33473. }, animDuration || 0);
  33474. }
  33475. // Means data is in accordance with what you see
  33476. series.isDirty = false;
  33477. // (See #322) series.isDirty = series.isDirtyData = false; // means
  33478. // data is in accordance with what you see
  33479. series.hasRendered = true;
  33480. fireEvent(series, 'afterRender');
  33481. }
  33482. /**
  33483. * Redraw the series. This function is called internally from
  33484. * `chart.redraw` and normally shouldn't be called directly.
  33485. * @private
  33486. * @function Highcharts.Series#redraw
  33487. */
  33488. redraw() {
  33489. // Cache it here as it is set to false in render, but used after
  33490. const wasDirty = this.isDirty || this.isDirtyData;
  33491. this.translate();
  33492. this.render();
  33493. if (wasDirty) { // #3868, #3945
  33494. delete this.kdTree;
  33495. }
  33496. }
  33497. /**
  33498. * Find the nearest point from a pointer event. This applies to series that
  33499. * use k-d-trees to get the nearest point. Native pointer events must be
  33500. * normalized using `Pointer.normalize`, that adds `chartX` and `chartY`
  33501. * properties.
  33502. *
  33503. * @sample highcharts/demo/synchronized-charts
  33504. * Synchronized charts with tooltips
  33505. *
  33506. * @function Highcharts.Series#searchPoint
  33507. *
  33508. * @param {Highcharts.PointerEvent} e
  33509. * The normalized pointer event
  33510. * @param {boolean} [compareX=false]
  33511. * Search only by the X value, not Y
  33512. *
  33513. * @return {Point|undefined}
  33514. * The closest point to the pointer event
  33515. */
  33516. searchPoint(e, compareX) {
  33517. const series = this, xAxis = series.xAxis, yAxis = series.yAxis, inverted = series.chart.inverted;
  33518. return this.searchKDTree({
  33519. clientX: inverted ?
  33520. xAxis.len - e.chartY + xAxis.pos :
  33521. e.chartX - xAxis.pos,
  33522. plotY: inverted ?
  33523. yAxis.len - e.chartX + yAxis.pos :
  33524. e.chartY - yAxis.pos
  33525. }, compareX, e);
  33526. }
  33527. /**
  33528. * Build the k-d-tree that is used by mouse and touch interaction to get
  33529. * the closest point. Line-like series typically have a one-dimensional
  33530. * tree where points are searched along the X axis, while scatter-like
  33531. * series typically search in two dimensions, X and Y.
  33532. *
  33533. * @private
  33534. * @function Highcharts.Series#buildKDTree
  33535. */
  33536. buildKDTree(e) {
  33537. // Prevent multiple k-d-trees from being built simultaneously
  33538. // (#6235)
  33539. this.buildingKdTree = true;
  33540. const series = this, dimensions = series.options.findNearestPointBy
  33541. .indexOf('y') > -1 ? 2 : 1;
  33542. /**
  33543. * Internal function
  33544. * @private
  33545. */
  33546. function _kdtree(points, depth, dimensions) {
  33547. const length = points && points.length;
  33548. let axis, median;
  33549. if (length) {
  33550. // alternate between the axis
  33551. axis = series.kdAxisArray[depth % dimensions];
  33552. // sort point array
  33553. points.sort(function (a, b) {
  33554. return a[axis] - b[axis];
  33555. });
  33556. median = Math.floor(length / 2);
  33557. // build and return nod
  33558. return {
  33559. point: points[median],
  33560. left: _kdtree(points.slice(0, median), depth + 1, dimensions),
  33561. right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
  33562. };
  33563. }
  33564. }
  33565. /**
  33566. * Start the recursive build process with a clone of the points
  33567. * array and null points filtered out. (#3873)
  33568. * @private
  33569. */
  33570. function startRecursive() {
  33571. series.kdTree = _kdtree(series.getValidPoints(null,
  33572. // For line-type series restrict to plot area, but
  33573. // column-type series not (#3916, #4511)
  33574. !series.directTouch), dimensions, dimensions);
  33575. series.buildingKdTree = false;
  33576. }
  33577. delete series.kdTree;
  33578. // For testing tooltips, don't build async. Also if touchstart, we
  33579. // may be dealing with click events on mobile, so don't delay
  33580. // (#6817).
  33581. syncTimeout(startRecursive, series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1);
  33582. }
  33583. /**
  33584. * @private
  33585. * @function Highcharts.Series#searchKDTree
  33586. */
  33587. searchKDTree(point, compareX, e) {
  33588. const series = this, kdX = this.kdAxisArray[0], kdY = this.kdAxisArray[1], kdComparer = compareX ? 'distX' : 'dist', kdDimensions = series.options.findNearestPointBy
  33589. .indexOf('y') > -1 ? 2 : 1;
  33590. /**
  33591. * Set the one and two dimensional distance on the point object.
  33592. * @private
  33593. */
  33594. function setDistance(p1, p2) {
  33595. const x = (defined(p1[kdX]) &&
  33596. defined(p2[kdX])) ?
  33597. Math.pow(p1[kdX] - p2[kdX], 2) :
  33598. null, y = (defined(p1[kdY]) &&
  33599. defined(p2[kdY])) ?
  33600. Math.pow(p1[kdY] - p2[kdY], 2) :
  33601. null, r = (x || 0) + (y || 0);
  33602. p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
  33603. p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
  33604. }
  33605. /**
  33606. * @private
  33607. */
  33608. function _search(search, tree, depth, dimensions) {
  33609. const point = tree.point, axis = series.kdAxisArray[depth % dimensions];
  33610. let nPoint1, nPoint2, ret = point;
  33611. setDistance(search, point);
  33612. // Pick side based on distance to splitting point
  33613. const tdist = search[axis] - point[axis], sideA = tdist < 0 ? 'left' : 'right', sideB = tdist < 0 ? 'right' : 'left';
  33614. // End of tree
  33615. if (tree[sideA]) {
  33616. nPoint1 = _search(search, tree[sideA], depth + 1, dimensions);
  33617. ret = (nPoint1[kdComparer] <
  33618. ret[kdComparer] ?
  33619. nPoint1 :
  33620. point);
  33621. }
  33622. if (tree[sideB]) {
  33623. // compare distance to current best to splitting point to
  33624. // decide whether to check side B or not
  33625. if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
  33626. nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
  33627. ret = (nPoint2[kdComparer] <
  33628. ret[kdComparer] ?
  33629. nPoint2 :
  33630. ret);
  33631. }
  33632. }
  33633. return ret;
  33634. }
  33635. if (!this.kdTree && !this.buildingKdTree) {
  33636. this.buildKDTree(e);
  33637. }
  33638. if (this.kdTree) {
  33639. return _search(point, this.kdTree, kdDimensions, kdDimensions);
  33640. }
  33641. }
  33642. /**
  33643. * @private
  33644. * @function Highcharts.Series#pointPlacementToXValue
  33645. */
  33646. pointPlacementToXValue() {
  33647. const { options: { pointPlacement, pointRange }, xAxis: axis } = this;
  33648. let factor = pointPlacement;
  33649. // Point placement is relative to each series pointRange (#5889)
  33650. if (factor === 'between') {
  33651. factor = axis.reversed ? -0.5 : 0.5; // #11955
  33652. }
  33653. return isNumber(factor) ?
  33654. factor * (pointRange || axis.pointRange) :
  33655. 0;
  33656. }
  33657. /**
  33658. * @private
  33659. * @function Highcharts.Series#isPointInside
  33660. */
  33661. isPointInside(point) {
  33662. const { chart, xAxis, yAxis } = this, isInside = (typeof point.plotY !== 'undefined' &&
  33663. typeof point.plotX !== 'undefined' &&
  33664. point.plotY >= 0 &&
  33665. point.plotY <= (yAxis ? yAxis.len : chart.plotHeight) &&
  33666. point.plotX >= 0 &&
  33667. point.plotX <= (xAxis ? xAxis.len : chart.plotWidth));
  33668. return isInside;
  33669. }
  33670. /**
  33671. * Draw the tracker object that sits above all data labels and markers to
  33672. * track mouse events on the graph or points. For the line type charts
  33673. * the tracker uses the same graphPath, but with a greater stroke width
  33674. * for better control.
  33675. * @private
  33676. */
  33677. drawTracker() {
  33678. const series = this, options = series.options, trackByArea = options.trackByArea, trackerPath = [].concat(trackByArea ?
  33679. series.areaPath :
  33680. series.graphPath),
  33681. // trackerPathLength = trackerPath.length,
  33682. chart = series.chart, pointer = chart.pointer, renderer = chart.renderer, snap = chart.options.tooltip.snap, tracker = series.tracker, onMouseOver = function (e) {
  33683. if (options.enableMouseTracking &&
  33684. chart.hoverSeries !== series) {
  33685. series.onMouseOver();
  33686. }
  33687. },
  33688. /*
  33689. * Empirical lowest possible opacities for TRACKER_FILL for an
  33690. * element to stay invisible but clickable
  33691. * IE9: 0.00000000001 (unlimited)
  33692. * IE10: 0.0001 (exporting only)
  33693. * FF: 0.00000000001 (unlimited)
  33694. * Chrome: 0.000001
  33695. * Safari: 0.000001
  33696. * Opera: 0.00000000001 (unlimited)
  33697. */
  33698. TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
  33699. let i;
  33700. // Draw the tracker
  33701. if (tracker) {
  33702. tracker.attr({ d: trackerPath });
  33703. }
  33704. else if (series.graph) { // create
  33705. series.tracker = renderer.path(trackerPath)
  33706. .attr({
  33707. visibility: series.visible ? 'inherit' : 'hidden',
  33708. zIndex: 2
  33709. })
  33710. .addClass(trackByArea ?
  33711. 'highcharts-tracker-area' :
  33712. 'highcharts-tracker-line')
  33713. .add(series.group);
  33714. if (!chart.styledMode) {
  33715. series.tracker.attr({
  33716. 'stroke-linecap': 'round',
  33717. 'stroke-linejoin': 'round',
  33718. stroke: TRACKER_FILL,
  33719. fill: trackByArea ? TRACKER_FILL : 'none',
  33720. 'stroke-width': series.graph.strokeWidth() +
  33721. (trackByArea ? 0 : 2 * snap)
  33722. });
  33723. }
  33724. // The tracker is added to the series group, which is clipped, but
  33725. // is covered by the marker group. So the marker group also needs to
  33726. // capture events.
  33727. [
  33728. series.tracker,
  33729. series.markerGroup,
  33730. series.dataLabelsGroup
  33731. ].forEach(function (tracker) {
  33732. if (tracker) {
  33733. tracker.addClass('highcharts-tracker')
  33734. .on('mouseover', onMouseOver)
  33735. .on('mouseout', function (e) {
  33736. pointer.onTrackerMouseOut(e);
  33737. });
  33738. if (options.cursor && !chart.styledMode) {
  33739. tracker.css({ cursor: options.cursor });
  33740. }
  33741. if (hasTouch) {
  33742. tracker.on('touchstart', onMouseOver);
  33743. }
  33744. }
  33745. });
  33746. }
  33747. fireEvent(this, 'afterDrawTracker');
  33748. }
  33749. /**
  33750. * Add a point to the series after render time. The point can be added at
  33751. * the end, or by giving it an X value, to the start or in the middle of the
  33752. * series.
  33753. *
  33754. * @sample highcharts/members/series-addpoint-append/
  33755. * Append point
  33756. * @sample highcharts/members/series-addpoint-append-and-shift/
  33757. * Append and shift
  33758. * @sample highcharts/members/series-addpoint-x-and-y/
  33759. * Both X and Y values given
  33760. * @sample highcharts/members/series-addpoint-pie/
  33761. * Append pie slice
  33762. * @sample stock/members/series-addpoint/
  33763. * Append 100 points in Highcharts Stock
  33764. * @sample stock/members/series-addpoint-shift/
  33765. * Append and shift in Highcharts Stock
  33766. * @sample maps/members/series-addpoint/
  33767. * Add a point in Highmaps
  33768. *
  33769. * @function Highcharts.Series#addPoint
  33770. *
  33771. * @param {Highcharts.PointOptionsType} options
  33772. * The point options. If options is a single number, a point with
  33773. * that y value is appended to the series. If it is an array, it will
  33774. * be interpreted as x and y values respectively. If it is an
  33775. * object, advanced options as outlined under `series.data` are
  33776. * applied.
  33777. *
  33778. * @param {boolean} [redraw=true]
  33779. * Whether to redraw the chart after the point is added. When adding
  33780. * more than one point, it is highly recommended that the redraw
  33781. * option be set to false, and instead {@link Chart#redraw} is
  33782. * explicitly called after the adding of points is finished.
  33783. * Otherwise, the chart will redraw after adding each point.
  33784. *
  33785. * @param {boolean} [shift=false]
  33786. * If true, a point is shifted off the start of the series as one is
  33787. * appended to the end.
  33788. *
  33789. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33790. * Whether to apply animation, and optionally animation
  33791. * configuration.
  33792. *
  33793. * @param {boolean} [withEvent=true]
  33794. * Used internally, whether to fire the series `addPoint` event.
  33795. *
  33796. * @emits Highcharts.Series#event:addPoint
  33797. */
  33798. addPoint(options, redraw, shift, animation, withEvent) {
  33799. const series = this, seriesOptions = series.options, data = series.data, chart = series.chart, xAxis = series.xAxis, names = xAxis && xAxis.hasNames && xAxis.names, dataOptions = seriesOptions.data, xData = series.xData;
  33800. let isInTheMiddle, i;
  33801. // Optional redraw, defaults to true
  33802. redraw = pick(redraw, true);
  33803. // Get options and push the point to xData, yData and series.options. In
  33804. // series.generatePoints the Point instance will be created on demand
  33805. // and pushed to the series.data array.
  33806. const point = { series: series };
  33807. series.pointClass.prototype.applyOptions.apply(point, [options]);
  33808. const x = point.x;
  33809. // Get the insertion point
  33810. i = xData.length;
  33811. if (series.requireSorting && x < xData[i - 1]) {
  33812. isInTheMiddle = true;
  33813. while (i && xData[i - 1] > x) {
  33814. i--;
  33815. }
  33816. }
  33817. // Insert undefined item
  33818. series.updateParallelArrays(point, 'splice', [i, 0, 0]);
  33819. // Update it
  33820. series.updateParallelArrays(point, i);
  33821. if (names && point.name) {
  33822. names[x] = point.name;
  33823. }
  33824. dataOptions.splice(i, 0, options);
  33825. if (isInTheMiddle ||
  33826. // When processedData is present we need to splice an empty slot
  33827. // into series.data, otherwise generatePoints won't pick it up.
  33828. series.processedData) {
  33829. series.data.splice(i, 0, null);
  33830. series.processData();
  33831. }
  33832. // Generate points to be added to the legend (#1329)
  33833. if (seriesOptions.legendType === 'point') {
  33834. series.generatePoints();
  33835. }
  33836. // Shift the first point off the parallel arrays
  33837. if (shift) {
  33838. if (data[0] && (data[0].remove)) {
  33839. data[0].remove(false);
  33840. }
  33841. else {
  33842. data.shift();
  33843. series.updateParallelArrays(point, 'shift');
  33844. dataOptions.shift();
  33845. }
  33846. }
  33847. // Fire event
  33848. if (withEvent !== false) {
  33849. fireEvent(series, 'addPoint', { point: point });
  33850. }
  33851. // redraw
  33852. series.isDirty = true;
  33853. series.isDirtyData = true;
  33854. if (redraw) {
  33855. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  33856. }
  33857. }
  33858. /**
  33859. * Remove a point from the series. Unlike the
  33860. * {@link Highcharts.Point#remove} method, this can also be done on a point
  33861. * that is not instanciated because it is outside the view or subject to
  33862. * Highcharts Stock data grouping.
  33863. *
  33864. * @sample highcharts/members/series-removepoint/
  33865. * Remove cropped point
  33866. *
  33867. * @function Highcharts.Series#removePoint
  33868. *
  33869. * @param {number} i
  33870. * The index of the point in the {@link Highcharts.Series.data|data}
  33871. * array.
  33872. *
  33873. * @param {boolean} [redraw=true]
  33874. * Whether to redraw the chart after the point is added. When
  33875. * removing more than one point, it is highly recommended that the
  33876. * `redraw` option be set to `false`, and instead {@link
  33877. * Highcharts.Chart#redraw} is explicitly called after the adding of
  33878. * points is finished.
  33879. *
  33880. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33881. * Whether and optionally how the series should be animated.
  33882. *
  33883. * @emits Highcharts.Point#event:remove
  33884. */
  33885. removePoint(i, redraw, animation) {
  33886. const series = this, data = series.data, point = data[i], points = series.points, chart = series.chart, remove = function () {
  33887. if (points && points.length === data.length) { // #4935
  33888. points.splice(i, 1);
  33889. }
  33890. data.splice(i, 1);
  33891. series.options.data.splice(i, 1);
  33892. series.updateParallelArrays(point || { series: series }, 'splice', [i, 1]);
  33893. if (point) {
  33894. point.destroy();
  33895. }
  33896. // redraw
  33897. series.isDirty = true;
  33898. series.isDirtyData = true;
  33899. if (redraw) {
  33900. chart.redraw();
  33901. }
  33902. };
  33903. setAnimation(animation, chart);
  33904. redraw = pick(redraw, true);
  33905. // Fire the event with a default handler of removing the point
  33906. if (point) {
  33907. point.firePointEvent('remove', null, remove);
  33908. }
  33909. else {
  33910. remove();
  33911. }
  33912. }
  33913. /**
  33914. * Remove a series and optionally redraw the chart.
  33915. *
  33916. * @sample highcharts/members/series-remove/
  33917. * Remove first series from a button
  33918. *
  33919. * @function Highcharts.Series#remove
  33920. *
  33921. * @param {boolean} [redraw=true]
  33922. * Whether to redraw the chart or wait for an explicit call to
  33923. * {@link Highcharts.Chart#redraw}.
  33924. *
  33925. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33926. * Whether to apply animation, and optionally animation
  33927. * configuration.
  33928. *
  33929. * @param {boolean} [withEvent=true]
  33930. * Used internally, whether to fire the series `remove` event.
  33931. *
  33932. * @emits Highcharts.Series#event:remove
  33933. */
  33934. remove(redraw, animation, withEvent, keepEvents) {
  33935. const series = this, chart = series.chart;
  33936. /**
  33937. * @private
  33938. */
  33939. function remove() {
  33940. // Destroy elements
  33941. series.destroy(keepEvents);
  33942. // Redraw
  33943. chart.isDirtyLegend = chart.isDirtyBox = true;
  33944. chart.linkSeries(keepEvents);
  33945. if (pick(redraw, true)) {
  33946. chart.redraw(animation);
  33947. }
  33948. }
  33949. // Fire the event with a default handler of removing the point
  33950. if (withEvent !== false) {
  33951. fireEvent(series, 'remove', null, remove);
  33952. }
  33953. else {
  33954. remove();
  33955. }
  33956. }
  33957. /**
  33958. * Update the series with a new set of options. For a clean and precise
  33959. * handling of new options, all methods and elements from the series are
  33960. * removed, and it is initialized from scratch. Therefore, this method is
  33961. * more performance expensive than some other utility methods like {@link
  33962. * Series#setData} or {@link Series#setVisible}.
  33963. *
  33964. * Note that `Series.update` may mutate the passed `data` options.
  33965. *
  33966. * @sample highcharts/members/series-update/
  33967. * Updating series options
  33968. * @sample maps/members/series-update/
  33969. * Update series options in Highmaps
  33970. *
  33971. * @function Highcharts.Series#update
  33972. *
  33973. * @param {Highcharts.SeriesOptionsType} options
  33974. * New options that will be merged with the series' existing options.
  33975. *
  33976. * @param {boolean} [redraw=true]
  33977. * Whether to redraw the chart after the series is altered. If doing
  33978. * more operations on the chart, it is a good idea to set redraw to
  33979. * false and call {@link Chart#redraw} after.
  33980. *
  33981. * @emits Highcharts.Series#event:update
  33982. * @emits Highcharts.Series#event:afterUpdate
  33983. */
  33984. update(options, redraw) {
  33985. options = diffObjects(options, this.userOptions);
  33986. fireEvent(this, 'update', { options: options });
  33987. const series = this, chart = series.chart,
  33988. // must use user options when changing type because series.options
  33989. // is merged in with type specific plotOptions
  33990. oldOptions = series.userOptions, initialType = series.initialType || series.type, plotOptions = chart.options.plotOptions, initialSeriesProto = seriesTypes[initialType].prototype, groups = [
  33991. 'group',
  33992. 'markerGroup',
  33993. 'dataLabelsGroup',
  33994. 'transformGroup'
  33995. ],
  33996. // Animation must be enabled when calling update before the initial
  33997. // animation has first run. This happens when calling update
  33998. // directly after chart initialization, or when applying responsive
  33999. // rules (#6912).
  34000. animation = series.finishedAnimating && { animation: false }, kinds = {};
  34001. let seriesOptions, n, preserve = [
  34002. 'colorIndex',
  34003. 'eventOptions',
  34004. 'navigatorSeries',
  34005. 'symbolIndex',
  34006. 'baseSeries'
  34007. ], newType = (options.type ||
  34008. oldOptions.type ||
  34009. chart.options.chart.type);
  34010. const keepPoints = !(
  34011. // Indicators, histograms etc recalculate the data. It should be
  34012. // possible to omit this.
  34013. this.hasDerivedData ||
  34014. // New type requires new point classes
  34015. (newType && newType !== this.type) ||
  34016. // New options affecting how the data points are built
  34017. typeof options.pointStart !== 'undefined' ||
  34018. typeof options.pointInterval !== 'undefined' ||
  34019. typeof options.relativeXValue !== 'undefined' ||
  34020. options.joinBy ||
  34021. options.mapData || // #11636
  34022. // Changes to data grouping requires new points in new group
  34023. series.hasOptionChanged('dataGrouping') ||
  34024. series.hasOptionChanged('pointStart') ||
  34025. series.hasOptionChanged('pointInterval') ||
  34026. series.hasOptionChanged('pointIntervalUnit') ||
  34027. series.hasOptionChanged('keys'));
  34028. newType = newType || initialType;
  34029. if (keepPoints) {
  34030. preserve.push('data', 'isDirtyData', 'points', 'processedData', // #17057
  34031. 'processedXData', 'processedYData', 'xIncrement', 'cropped', '_hasPointMarkers', '_hasPointLabels', 'clips', // #15420
  34032. // Networkgraph (#14397)
  34033. 'nodes', 'layout',
  34034. // Treemap
  34035. 'level',
  34036. // Map specific, consider moving it to series-specific preserve-
  34037. // properties (#10617)
  34038. 'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
  34039. if (options.visible !== false) {
  34040. preserve.push('area', 'graph');
  34041. }
  34042. series.parallelArrays.forEach(function (key) {
  34043. preserve.push(key + 'Data');
  34044. });
  34045. if (options.data) {
  34046. // setData uses dataSorting options so we need to update them
  34047. // earlier
  34048. if (options.dataSorting) {
  34049. extend(series.options.dataSorting, options.dataSorting);
  34050. }
  34051. this.setData(options.data, false);
  34052. }
  34053. }
  34054. // Do the merge, with some forced options
  34055. options = merge(oldOptions, animation, {
  34056. // When oldOptions.index is null it should't be cleared.
  34057. // Otherwise navigator series will have wrong indexes (#10193).
  34058. index: typeof oldOptions.index === 'undefined' ?
  34059. series.index : oldOptions.index,
  34060. pointStart: pick(
  34061. // when updating from blank (#7933)
  34062. (plotOptions &&
  34063. plotOptions.series &&
  34064. plotOptions.series.pointStart), oldOptions.pointStart,
  34065. // when updating after addPoint
  34066. series.xData[0])
  34067. }, (!keepPoints && { data: series.options.data }), options);
  34068. // Merge does not merge arrays, but replaces them. Since points were
  34069. // updated, `series.options.data` has correct merged options, use it:
  34070. if (keepPoints && options.data) {
  34071. options.data = series.options.data;
  34072. }
  34073. // Make sure preserved properties are not destroyed (#3094)
  34074. preserve = groups.concat(preserve);
  34075. preserve.forEach(function (prop) {
  34076. preserve[prop] = series[prop];
  34077. delete series[prop];
  34078. });
  34079. let casting = false;
  34080. if (seriesTypes[newType]) {
  34081. casting = newType !== series.type;
  34082. // Destroy the series and delete all properties, it will be
  34083. // reinserted within the `init` call below
  34084. series.remove(false, false, false, true);
  34085. if (casting) {
  34086. // Modern browsers including IE11
  34087. if (Object.setPrototypeOf) {
  34088. Object.setPrototypeOf(series, seriesTypes[newType].prototype);
  34089. // Legacy (IE < 11)
  34090. }
  34091. else {
  34092. const ownEvents = Object.hasOwnProperty.call(series, 'hcEvents') && series.hcEvents;
  34093. for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
  34094. series[n] = void 0;
  34095. }
  34096. // Reinsert all methods and properties from the new type
  34097. // prototype (#2270, #3719).
  34098. extend(series, seriesTypes[newType].prototype);
  34099. // The events are tied to the prototype chain, don't copy if
  34100. // they're not the series' own
  34101. if (ownEvents) {
  34102. series.hcEvents = ownEvents;
  34103. }
  34104. else {
  34105. delete series.hcEvents;
  34106. }
  34107. }
  34108. }
  34109. }
  34110. else {
  34111. error(17, true, chart, { missingModuleFor: newType });
  34112. }
  34113. // Re-register groups (#3094) and other preserved properties
  34114. preserve.forEach(function (prop) {
  34115. series[prop] = preserve[prop];
  34116. });
  34117. series.init(chart, options);
  34118. // Remove particular elements of the points. Check `series.options`
  34119. // because we need to consider the options being set on plotOptions as
  34120. // well.
  34121. if (keepPoints && this.points) {
  34122. seriesOptions = series.options;
  34123. // What kind of elements to destroy
  34124. if (seriesOptions.visible === false) {
  34125. kinds.graphic = 1;
  34126. kinds.dataLabel = 1;
  34127. }
  34128. else if (!series._hasPointLabels) {
  34129. const { marker, dataLabels } = seriesOptions, oldMarker = oldOptions.marker || {};
  34130. // If the marker got disabled or changed its symbol, width or
  34131. // height - destroy
  34132. if (marker && (marker.enabled === false ||
  34133. oldMarker.symbol !== marker.symbol || // #10870, #15946
  34134. oldMarker.height !== marker.height || // #16274
  34135. oldMarker.width !== marker.width // #16274
  34136. )) {
  34137. kinds.graphic = 1;
  34138. }
  34139. if (dataLabels &&
  34140. dataLabels.enabled === false) {
  34141. kinds.dataLabel = 1;
  34142. }
  34143. }
  34144. for (const point of this.points) {
  34145. if (point && point.series) {
  34146. point.resolveColor();
  34147. // Destroy elements in order to recreate based on updated
  34148. // series options.
  34149. if (Object.keys(kinds).length) {
  34150. point.destroyElements(kinds);
  34151. }
  34152. if (seriesOptions.showInLegend === false &&
  34153. point.legendItem) {
  34154. chart.legend.destroyItem(point);
  34155. }
  34156. }
  34157. }
  34158. }
  34159. series.initialType = initialType;
  34160. chart.linkSeries(); // Links are lost in series.remove (#3028)
  34161. // #15383: Fire updatedData if the type has changed to keep linked
  34162. // series such as indicators updated
  34163. if (casting && series.linkedSeries.length) {
  34164. series.isDirtyData = true;
  34165. }
  34166. fireEvent(this, 'afterUpdate');
  34167. if (pick(redraw, true)) {
  34168. chart.redraw(keepPoints ? void 0 : false);
  34169. }
  34170. }
  34171. /**
  34172. * Used from within series.update
  34173. * @private
  34174. */
  34175. setName(name) {
  34176. this.name = this.options.name = this.userOptions.name = name;
  34177. this.chart.isDirtyLegend = true;
  34178. }
  34179. /**
  34180. * Check if the option has changed.
  34181. * @private
  34182. */
  34183. hasOptionChanged(optionName) {
  34184. const chart = this.chart, option = this.options[optionName], plotOptions = chart.options.plotOptions, oldOption = this.userOptions[optionName];
  34185. if (oldOption) {
  34186. return option !== oldOption;
  34187. }
  34188. return option !==
  34189. pick(plotOptions &&
  34190. plotOptions[this.type] &&
  34191. plotOptions[this.type][optionName], plotOptions &&
  34192. plotOptions.series &&
  34193. plotOptions.series[optionName], option);
  34194. }
  34195. /**
  34196. * Runs on mouse over the series graphical items.
  34197. *
  34198. * @function Highcharts.Series#onMouseOver
  34199. * @emits Highcharts.Series#event:mouseOver
  34200. */
  34201. onMouseOver() {
  34202. const series = this, chart = series.chart, hoverSeries = chart.hoverSeries, pointer = chart.pointer;
  34203. pointer.setHoverChartIndex();
  34204. // set normal state to previous series
  34205. if (hoverSeries && hoverSeries !== series) {
  34206. hoverSeries.onMouseOut();
  34207. }
  34208. // trigger the event, but to save processing time,
  34209. // only if defined
  34210. if (series.options.events.mouseOver) {
  34211. fireEvent(series, 'mouseOver');
  34212. }
  34213. // hover this
  34214. series.setState('hover');
  34215. /**
  34216. * Contains the original hovered series.
  34217. *
  34218. * @name Highcharts.Chart#hoverSeries
  34219. * @type {Highcharts.Series|null}
  34220. */
  34221. chart.hoverSeries = series;
  34222. }
  34223. /**
  34224. * Runs on mouse out of the series graphical items.
  34225. *
  34226. * @function Highcharts.Series#onMouseOut
  34227. *
  34228. * @emits Highcharts.Series#event:mouseOut
  34229. */
  34230. onMouseOut() {
  34231. // trigger the event only if listeners exist
  34232. const series = this, options = series.options, chart = series.chart, tooltip = chart.tooltip, hoverPoint = chart.hoverPoint;
  34233. // #182, set to null before the mouseOut event fires
  34234. chart.hoverSeries = null;
  34235. // trigger mouse out on the point, which must be in this series
  34236. if (hoverPoint) {
  34237. hoverPoint.onMouseOut();
  34238. }
  34239. // fire the mouse out event
  34240. if (series && options.events.mouseOut) {
  34241. fireEvent(series, 'mouseOut');
  34242. }
  34243. // hide the tooltip
  34244. if (tooltip &&
  34245. !series.stickyTracking &&
  34246. (!tooltip.shared || series.noSharedTooltip)) {
  34247. tooltip.hide();
  34248. }
  34249. // Reset all inactive states
  34250. chart.series.forEach(function (s) {
  34251. s.setState('', true);
  34252. });
  34253. }
  34254. /**
  34255. * Set the state of the series. Called internally on mouse interaction
  34256. * operations, but it can also be called directly to visually
  34257. * highlight a series.
  34258. *
  34259. * @function Highcharts.Series#setState
  34260. *
  34261. * @param {Highcharts.SeriesStateValue|""} [state]
  34262. * The new state, can be either `'hover'`, `'inactive'`, `'select'`,
  34263. * or `''` (an empty string), `'normal'` or `undefined` to set to
  34264. * normal state.
  34265. * @param {boolean} [inherit]
  34266. * Determines if state should be inherited by points too.
  34267. */
  34268. setState(state, inherit) {
  34269. const series = this, options = series.options, graph = series.graph, inactiveOtherPoints = options.inactiveOtherPoints, stateOptions = options.states,
  34270. // By default a quick animation to hover/inactive,
  34271. // slower to un-hover
  34272. stateAnimation = pick((stateOptions[state || 'normal'] &&
  34273. stateOptions[state || 'normal'].animation), series.chart.options.chart.animation);
  34274. let attribs, lineWidth = options.lineWidth, i = 0, opacity = options.opacity;
  34275. state = state || '';
  34276. if (series.state !== state) {
  34277. // Toggle class names
  34278. [
  34279. series.group,
  34280. series.markerGroup,
  34281. series.dataLabelsGroup
  34282. ].forEach(function (group) {
  34283. if (group) {
  34284. // Old state
  34285. if (series.state) {
  34286. group.removeClass('highcharts-series-' + series.state);
  34287. }
  34288. // New state
  34289. if (state) {
  34290. group.addClass('highcharts-series-' + state);
  34291. }
  34292. }
  34293. });
  34294. series.state = state;
  34295. if (!series.chart.styledMode) {
  34296. if (stateOptions[state] &&
  34297. stateOptions[state].enabled === false) {
  34298. return;
  34299. }
  34300. if (state) {
  34301. lineWidth = (stateOptions[state].lineWidth ||
  34302. lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
  34303. opacity = pick(stateOptions[state].opacity, opacity);
  34304. }
  34305. if (graph && !graph.dashstyle && isNumber(lineWidth)) {
  34306. attribs = {
  34307. 'stroke-width': lineWidth
  34308. };
  34309. // Animate the graph stroke-width.
  34310. graph.animate(attribs, stateAnimation);
  34311. while (series['zone-graph-' + i]) {
  34312. series['zone-graph-' + i].animate(attribs, stateAnimation);
  34313. i = i + 1;
  34314. }
  34315. }
  34316. // For some types (pie, networkgraph, sankey) opacity is
  34317. // resolved on a point level
  34318. if (!inactiveOtherPoints) {
  34319. [
  34320. series.group,
  34321. series.markerGroup,
  34322. series.dataLabelsGroup,
  34323. series.labelBySeries
  34324. ].forEach(function (group) {
  34325. if (group) {
  34326. group.animate({
  34327. opacity: opacity
  34328. }, stateAnimation);
  34329. }
  34330. });
  34331. }
  34332. }
  34333. }
  34334. // Don't loop over points on a series that doesn't apply inactive state
  34335. // to siblings markers (e.g. line, column)
  34336. if (inherit && inactiveOtherPoints && series.points) {
  34337. series.setAllPointsToState(state || void 0);
  34338. }
  34339. }
  34340. /**
  34341. * Set the state for all points in the series.
  34342. *
  34343. * @function Highcharts.Series#setAllPointsToState
  34344. *
  34345. * @private
  34346. *
  34347. * @param {string} [state]
  34348. * Can be either `hover` or undefined to set to normal state.
  34349. */
  34350. setAllPointsToState(state) {
  34351. this.points.forEach(function (point) {
  34352. if (point.setState) {
  34353. point.setState(state);
  34354. }
  34355. });
  34356. }
  34357. /**
  34358. * Show or hide the series.
  34359. *
  34360. * @function Highcharts.Series#setVisible
  34361. *
  34362. * @param {boolean} [visible]
  34363. * True to show the series, false to hide. If undefined, the visibility is
  34364. * toggled.
  34365. *
  34366. * @param {boolean} [redraw=true]
  34367. * Whether to redraw the chart after the series is altered. If doing more
  34368. * operations on the chart, it is a good idea to set redraw to false and
  34369. * call {@link Chart#redraw|chart.redraw()} after.
  34370. *
  34371. * @emits Highcharts.Series#event:hide
  34372. * @emits Highcharts.Series#event:show
  34373. */
  34374. setVisible(vis, redraw) {
  34375. const series = this, chart = series.chart, ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, oldVisibility = series.visible;
  34376. // if called without an argument, toggle visibility
  34377. series.visible =
  34378. vis =
  34379. series.options.visible =
  34380. series.userOptions.visible =
  34381. typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
  34382. const showOrHide = vis ? 'show' : 'hide';
  34383. // show or hide elements
  34384. [
  34385. 'group',
  34386. 'dataLabelsGroup',
  34387. 'markerGroup',
  34388. 'tracker',
  34389. 'tt'
  34390. ].forEach(function (key) {
  34391. if (series[key]) {
  34392. series[key][showOrHide]();
  34393. }
  34394. });
  34395. // hide tooltip (#1361)
  34396. if (chart.hoverSeries === series ||
  34397. (chart.hoverPoint && chart.hoverPoint.series) === series) {
  34398. series.onMouseOut();
  34399. }
  34400. if (series.legendItem) {
  34401. chart.legend.colorizeItem(series, vis);
  34402. }
  34403. // rescale or adapt to resized chart
  34404. series.isDirty = true;
  34405. // in a stack, all other series are affected
  34406. if (series.options.stacking) {
  34407. chart.series.forEach(function (otherSeries) {
  34408. if (otherSeries.options.stacking && otherSeries.visible) {
  34409. otherSeries.isDirty = true;
  34410. }
  34411. });
  34412. }
  34413. // show or hide linked series
  34414. series.linkedSeries.forEach(function (otherSeries) {
  34415. otherSeries.setVisible(vis, false);
  34416. });
  34417. if (ignoreHiddenSeries) {
  34418. chart.isDirtyBox = true;
  34419. }
  34420. fireEvent(series, showOrHide);
  34421. if (redraw !== false) {
  34422. chart.redraw();
  34423. }
  34424. }
  34425. /**
  34426. * Show the series if hidden.
  34427. *
  34428. * @sample highcharts/members/series-hide/
  34429. * Toggle visibility from a button
  34430. *
  34431. * @function Highcharts.Series#show
  34432. * @emits Highcharts.Series#event:show
  34433. */
  34434. show() {
  34435. this.setVisible(true);
  34436. }
  34437. /**
  34438. * Hide the series if visible. If the
  34439. * [chart.ignoreHiddenSeries](https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries)
  34440. * option is true, the chart is redrawn without this series.
  34441. *
  34442. * @sample highcharts/members/series-hide/
  34443. * Toggle visibility from a button
  34444. *
  34445. * @function Highcharts.Series#hide
  34446. * @emits Highcharts.Series#event:hide
  34447. */
  34448. hide() {
  34449. this.setVisible(false);
  34450. }
  34451. /**
  34452. * Select or unselect the series. This means its
  34453. * {@link Highcharts.Series.selected|selected}
  34454. * property is set, the checkbox in the legend is toggled and when selected,
  34455. * the series is returned by the {@link Highcharts.Chart#getSelectedSeries}
  34456. * function.
  34457. *
  34458. * @sample highcharts/members/series-select/
  34459. * Select a series from a button
  34460. *
  34461. * @function Highcharts.Series#select
  34462. *
  34463. * @param {boolean} [selected]
  34464. * True to select the series, false to unselect. If undefined, the selection
  34465. * state is toggled.
  34466. *
  34467. * @emits Highcharts.Series#event:select
  34468. * @emits Highcharts.Series#event:unselect
  34469. */
  34470. select(selected) {
  34471. const series = this;
  34472. series.selected =
  34473. selected =
  34474. this.options.selected = (typeof selected === 'undefined' ?
  34475. !series.selected :
  34476. selected);
  34477. if (series.checkbox) {
  34478. series.checkbox.checked = selected;
  34479. }
  34480. fireEvent(series, selected ? 'select' : 'unselect');
  34481. }
  34482. /**
  34483. * Checks if a tooltip should be shown for a given point.
  34484. *
  34485. * @private
  34486. */
  34487. shouldShowTooltip(plotX, plotY, options = {}) {
  34488. options.series = this;
  34489. options.visiblePlotOnly = true;
  34490. return this.chart.isInsidePlot(plotX, plotY, options);
  34491. }
  34492. /**
  34493. * Draws the legend symbol based on the legendSymbol user option.
  34494. *
  34495. * @private
  34496. */
  34497. drawLegendSymbol(legend, item) {
  34498. var _a;
  34499. (_a = LegendSymbol[this.options.legendSymbol || 'rectangle']) === null || _a === void 0 ? void 0 : _a.call(this, legend, item);
  34500. }
  34501. }
  34502. Series.defaultOptions = SeriesDefaults;
  34503. /**
  34504. * Registry of all available series types.
  34505. *
  34506. * @name Highcharts.Series.types
  34507. * @type {Highcharts.Dictionary<typeof_Highcharts.Series>}
  34508. */
  34509. Series.types = SeriesRegistry.seriesTypes;
  34510. /* *
  34511. *
  34512. * Static Functions
  34513. *
  34514. * */
  34515. /**
  34516. * Registers a series class to be accessible via `Series.types`.
  34517. *
  34518. * @function Highcharts.Series.registerType
  34519. *
  34520. * @param {string} seriesType
  34521. * The series type as an identifier string in lower case.
  34522. *
  34523. * @param {Function} SeriesClass
  34524. * The series class as a class pattern or a constructor function with
  34525. * prototype.
  34526. */
  34527. Series.registerType = SeriesRegistry.registerSeriesType;
  34528. extend(Series.prototype, {
  34529. axisTypes: ['xAxis', 'yAxis'],
  34530. coll: 'series',
  34531. colorCounter: 0,
  34532. cropShoulder: 1,
  34533. directTouch: false,
  34534. isCartesian: true,
  34535. kdAxisArray: ['clientX', 'plotY'],
  34536. // each point's x and y values are stored in this.xData and this.yData:
  34537. parallelArrays: ['x', 'y'],
  34538. pointClass: Point,
  34539. requireSorting: true,
  34540. // requires the data to be sorted:
  34541. sorted: true
  34542. });
  34543. /* *
  34544. *
  34545. * Registry
  34546. *
  34547. * */
  34548. SeriesRegistry.series = Series;
  34549. /* *
  34550. *
  34551. * Default Export
  34552. *
  34553. * */
  34554. /* *
  34555. *
  34556. * API Declarations
  34557. *
  34558. * */
  34559. /**
  34560. * This is a placeholder type of the possible series options for
  34561. * [Highcharts](../highcharts/series), [Highcharts Stock](../highstock/series),
  34562. * [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
  34563. *
  34564. * In TypeScript is this dynamically generated to reference all possible types
  34565. * of series options.
  34566. *
  34567. * @ignore-declaration
  34568. * @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
  34569. */
  34570. /**
  34571. * Options for `dataSorting`.
  34572. *
  34573. * @interface Highcharts.DataSortingOptionsObject
  34574. * @since 8.0.0
  34575. */ /**
  34576. * Enable or disable data sorting for the series.
  34577. * @name Highcharts.DataSortingOptionsObject#enabled
  34578. * @type {boolean|undefined}
  34579. */ /**
  34580. * Whether to allow matching points by name in an update.
  34581. * @name Highcharts.DataSortingOptionsObject#matchByName
  34582. * @type {boolean|undefined}
  34583. */ /**
  34584. * Determines what data value should be used to sort by.
  34585. * @name Highcharts.DataSortingOptionsObject#sortKey
  34586. * @type {string|undefined}
  34587. */
  34588. /**
  34589. * Function callback when a series has been animated.
  34590. *
  34591. * @callback Highcharts.SeriesAfterAnimateCallbackFunction
  34592. *
  34593. * @param {Highcharts.Series} this
  34594. * The series where the event occured.
  34595. *
  34596. * @param {Highcharts.SeriesAfterAnimateEventObject} event
  34597. * Event arguments.
  34598. */
  34599. /**
  34600. * Event information regarding completed animation of a series.
  34601. *
  34602. * @interface Highcharts.SeriesAfterAnimateEventObject
  34603. */ /**
  34604. * Animated series.
  34605. * @name Highcharts.SeriesAfterAnimateEventObject#target
  34606. * @type {Highcharts.Series}
  34607. */ /**
  34608. * Event type.
  34609. * @name Highcharts.SeriesAfterAnimateEventObject#type
  34610. * @type {"afterAnimate"}
  34611. */
  34612. /**
  34613. * Function callback when the checkbox next to the series' name in the legend is
  34614. * clicked.
  34615. *
  34616. * @callback Highcharts.SeriesCheckboxClickCallbackFunction
  34617. *
  34618. * @param {Highcharts.Series} this
  34619. * The series where the event occured.
  34620. *
  34621. * @param {Highcharts.SeriesCheckboxClickEventObject} event
  34622. * Event arguments.
  34623. */
  34624. /**
  34625. * Event information regarding check of a series box.
  34626. *
  34627. * @interface Highcharts.SeriesCheckboxClickEventObject
  34628. */ /**
  34629. * Whether the box has been checked.
  34630. * @name Highcharts.SeriesCheckboxClickEventObject#checked
  34631. * @type {boolean}
  34632. */ /**
  34633. * Related series.
  34634. * @name Highcharts.SeriesCheckboxClickEventObject#item
  34635. * @type {Highcharts.Series}
  34636. */ /**
  34637. * Related series.
  34638. * @name Highcharts.SeriesCheckboxClickEventObject#target
  34639. * @type {Highcharts.Series}
  34640. */ /**
  34641. * Event type.
  34642. * @name Highcharts.SeriesCheckboxClickEventObject#type
  34643. * @type {"checkboxClick"}
  34644. */
  34645. /**
  34646. * Function callback when a series is clicked. Return false to cancel toogle
  34647. * actions.
  34648. *
  34649. * @callback Highcharts.SeriesClickCallbackFunction
  34650. *
  34651. * @param {Highcharts.Series} this
  34652. * The series where the event occured.
  34653. *
  34654. * @param {Highcharts.SeriesClickEventObject} event
  34655. * Event arguments.
  34656. */
  34657. /**
  34658. * Common information for a click event on a series.
  34659. *
  34660. * @interface Highcharts.SeriesClickEventObject
  34661. * @extends global.Event
  34662. */ /**
  34663. * Nearest point on the graph.
  34664. * @name Highcharts.SeriesClickEventObject#point
  34665. * @type {Highcharts.Point}
  34666. */
  34667. /**
  34668. * Gets fired when the series is hidden after chart generation time, either by
  34669. * clicking the legend item or by calling `.hide()`.
  34670. *
  34671. * @callback Highcharts.SeriesHideCallbackFunction
  34672. *
  34673. * @param {Highcharts.Series} this
  34674. * The series where the event occured.
  34675. *
  34676. * @param {global.Event} event
  34677. * The event that occured.
  34678. */
  34679. /**
  34680. * The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
  34681. * graph.
  34682. *
  34683. * @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
  34684. */
  34685. /**
  34686. * Gets fired when the legend item belonging to the series is clicked. The
  34687. * default action is to toggle the visibility of the series. This can be
  34688. * prevented by returning `false` or calling `event.preventDefault()`.
  34689. *
  34690. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  34691. *
  34692. * @param {Highcharts.Series} this
  34693. * The series where the event occured.
  34694. *
  34695. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  34696. * The event that occured.
  34697. */
  34698. /**
  34699. * Information about the event.
  34700. *
  34701. * @interface Highcharts.SeriesLegendItemClickEventObject
  34702. */ /**
  34703. * Related browser event.
  34704. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  34705. * @type {global.PointerEvent}
  34706. */ /**
  34707. * Prevent the default action of toggle the visibility of the series.
  34708. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  34709. * @type {Function}
  34710. */ /**
  34711. * Related series.
  34712. * @name Highcharts.SeriesCheckboxClickEventObject#target
  34713. * @type {Highcharts.Series}
  34714. */ /**
  34715. * Event type.
  34716. * @name Highcharts.SeriesCheckboxClickEventObject#type
  34717. * @type {"checkboxClick"}
  34718. */
  34719. /**
  34720. * Gets fired when the mouse leaves the graph.
  34721. *
  34722. * @callback Highcharts.SeriesMouseOutCallbackFunction
  34723. *
  34724. * @param {Highcharts.Series} this
  34725. * Series where the event occured.
  34726. *
  34727. * @param {global.PointerEvent} event
  34728. * Event that occured.
  34729. */
  34730. /**
  34731. * Gets fired when the mouse enters the graph.
  34732. *
  34733. * @callback Highcharts.SeriesMouseOverCallbackFunction
  34734. *
  34735. * @param {Highcharts.Series} this
  34736. * Series where the event occured.
  34737. *
  34738. * @param {global.PointerEvent} event
  34739. * Event that occured.
  34740. */
  34741. /**
  34742. * Translation and scale for the plot area of a series.
  34743. *
  34744. * @interface Highcharts.SeriesPlotBoxObject
  34745. */ /**
  34746. * @name Highcharts.SeriesPlotBoxObject#scaleX
  34747. * @type {number}
  34748. */ /**
  34749. * @name Highcharts.SeriesPlotBoxObject#scaleY
  34750. * @type {number}
  34751. */ /**
  34752. * @name Highcharts.SeriesPlotBoxObject#translateX
  34753. * @type {number}
  34754. */ /**
  34755. * @name Highcharts.SeriesPlotBoxObject#translateY
  34756. * @type {number}
  34757. */
  34758. /**
  34759. * Gets fired when the series is shown after chart generation time, either by
  34760. * clicking the legend item or by calling `.show()`.
  34761. *
  34762. * @callback Highcharts.SeriesShowCallbackFunction
  34763. *
  34764. * @param {Highcharts.Series} this
  34765. * Series where the event occured.
  34766. *
  34767. * @param {global.Event} event
  34768. * Event that occured.
  34769. */
  34770. /**
  34771. * Possible key values for the series state options.
  34772. *
  34773. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
  34774. */
  34775. ''; // detach doclets above
  34776. /* *
  34777. *
  34778. * API Options
  34779. *
  34780. * */
  34781. /**
  34782. * Series options for specific data and the data itself. In TypeScript you
  34783. * have to cast the series options to specific series types, to get all
  34784. * possible options for a series.
  34785. *
  34786. * @example
  34787. * // TypeScript example
  34788. * Highcharts.chart('container', {
  34789. * series: [{
  34790. * color: '#06C',
  34791. * data: [[0, 1], [2, 3]]
  34792. * } as Highcharts.SeriesLineOptions ]
  34793. * });
  34794. *
  34795. * @type {Array<*>}
  34796. * @apioption series
  34797. */
  34798. /**
  34799. * An id for the series. This can be used after render time to get a pointer
  34800. * to the series object through `chart.get()`.
  34801. *
  34802. * @sample {highcharts} highcharts/plotoptions/series-id/
  34803. * Get series by id
  34804. *
  34805. * @type {string}
  34806. * @since 1.2.0
  34807. * @apioption series.id
  34808. */
  34809. /**
  34810. * The index of the series in the chart, affecting the internal index in the
  34811. * `chart.series` array, the visible Z index as well as the order in the
  34812. * legend.
  34813. *
  34814. * @type {number}
  34815. * @since 2.3.0
  34816. * @apioption series.index
  34817. */
  34818. /**
  34819. * The sequential index of the series in the legend.
  34820. *
  34821. * @see [legend.reversed](#legend.reversed),
  34822. * [yAxis.reversedStacks](#yAxis.reversedStacks)
  34823. *
  34824. * @sample {highcharts|highstock} highcharts/series/legendindex/
  34825. * Legend in opposite order
  34826. *
  34827. * @type {number}
  34828. * @apioption series.legendIndex
  34829. */
  34830. /**
  34831. * The name of the series as shown in the legend, tooltip etc.
  34832. *
  34833. * @sample {highcharts} highcharts/series/name/
  34834. * Series name
  34835. * @sample {highmaps} maps/demo/category-map/
  34836. * Series name
  34837. *
  34838. * @type {string}
  34839. * @apioption series.name
  34840. */
  34841. /**
  34842. * This option allows grouping series in a stacked chart. The stack option
  34843. * can be a string or anything else, as long as the grouped series' stack
  34844. * options match each other after conversion into a string.
  34845. *
  34846. * @sample {highcharts} highcharts/series/stack/
  34847. * Stacked and grouped columns
  34848. *
  34849. * @type {number|string}
  34850. * @since 2.1
  34851. * @product highcharts highstock
  34852. * @apioption series.stack
  34853. */
  34854. /**
  34855. * The type of series, for example `line` or `column`. By default, the
  34856. * series type is inherited from [chart.type](#chart.type), so unless the
  34857. * chart is a combination of series types, there is no need to set it on the
  34858. * series level.
  34859. *
  34860. * @sample {highcharts} highcharts/series/type/
  34861. * Line and column in the same chart
  34862. * @sample highcharts/series/type-dynamic/
  34863. * Dynamic types with button selector
  34864. * @sample {highmaps} maps/demo/mapline-mappoint/
  34865. * Multiple types in the same map
  34866. *
  34867. * @type {string}
  34868. * @apioption series.type
  34869. */
  34870. /**
  34871. * When using dual or multiple x axes, this number defines which xAxis the
  34872. * particular series is connected to. It refers to either the
  34873. * {@link #xAxis.id|axis id}
  34874. * or the index of the axis in the xAxis array, with 0 being the first.
  34875. *
  34876. * @type {number|string}
  34877. * @default 0
  34878. * @product highcharts highstock
  34879. * @apioption series.xAxis
  34880. */
  34881. /**
  34882. * When using dual or multiple y axes, this number defines which yAxis the
  34883. * particular series is connected to. It refers to either the
  34884. * {@link #yAxis.id|axis id}
  34885. * or the index of the axis in the yAxis array, with 0 being the first.
  34886. *
  34887. * @sample {highcharts} highcharts/series/yaxis/
  34888. * Apply the column series to the secondary Y axis
  34889. *
  34890. * @type {number|string}
  34891. * @default 0
  34892. * @product highcharts highstock
  34893. * @apioption series.yAxis
  34894. */
  34895. /**
  34896. * Define the visual z index of the series.
  34897. *
  34898. * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
  34899. * With no z index, the series defined last are on top
  34900. * @sample {highcharts} highcharts/plotoptions/series-zindex/
  34901. * With a z index, the series with the highest z index is on top
  34902. * @sample {highstock} highcharts/plotoptions/series-zindex-default/
  34903. * With no z index, the series defined last are on top
  34904. * @sample {highstock} highcharts/plotoptions/series-zindex/
  34905. * With a z index, the series with the highest z index is on top
  34906. *
  34907. * @type {number}
  34908. * @product highcharts highstock
  34909. * @apioption series.zIndex
  34910. */
  34911. ''; // include precedent doclets in transpilat
  34912. return Series;
  34913. });
  34914. _registerModule(_modules, 'Core/Chart/Chart.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Defaults.js'], _modules['Core/Templating.js'], _modules['Core/Foundation.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Time.js'], _modules['Core/Utilities.js'], _modules['Core/Renderer/HTML/AST.js']], function (A, Axis, D, Templating, Foundation, H, RendererRegistry, Series, SeriesRegistry, SVGRenderer, Time, U, AST) {
  34915. /* *
  34916. *
  34917. * (c) 2010-2021 Torstein Honsi
  34918. *
  34919. * License: www.highcharts.com/license
  34920. *
  34921. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34922. *
  34923. * */
  34924. const { animate, animObject, setAnimation } = A;
  34925. const { defaultOptions, defaultTime } = D;
  34926. const { numberFormat } = Templating;
  34927. const { registerEventOptions } = Foundation;
  34928. const { charts, doc, marginNames, svg, win } = H;
  34929. const { seriesTypes } = SeriesRegistry;
  34930. const { addEvent, attr, createElement, css, defined, diffObjects, discardElement, erase, error, extend, find, fireEvent, getStyle, isArray, isNumber, isObject, isString, merge, objectEach, pick, pInt, relativeLength, removeEvent, splat, syncTimeout, uniqueKey } = U;
  34931. /* *
  34932. *
  34933. * Class
  34934. *
  34935. * */
  34936. /* eslint-disable no-invalid-this, valid-jsdoc */
  34937. /**
  34938. * The Chart class. The recommended constructor is {@link Highcharts#chart}.
  34939. *
  34940. * @example
  34941. * let chart = Highcharts.chart('container', {
  34942. * title: {
  34943. * text: 'My chart'
  34944. * },
  34945. * series: [{
  34946. * data: [1, 3, 2, 4]
  34947. * }]
  34948. * })
  34949. *
  34950. * @class
  34951. * @name Highcharts.Chart
  34952. *
  34953. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  34954. * The DOM element to render to, or its id.
  34955. *
  34956. * @param {Highcharts.Options} options
  34957. * The chart options structure.
  34958. *
  34959. * @param {Highcharts.ChartCallbackFunction} [callback]
  34960. * Function to run when the chart has loaded and and all external images
  34961. * are loaded. Defining a
  34962. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  34963. * handler is equivalent.
  34964. */
  34965. class Chart {
  34966. /**
  34967. * Factory function for basic charts.
  34968. *
  34969. * @example
  34970. * // Render a chart in to div#container
  34971. * let chart = Highcharts.chart('container', {
  34972. * title: {
  34973. * text: 'My chart'
  34974. * },
  34975. * series: [{
  34976. * data: [1, 3, 2, 4]
  34977. * }]
  34978. * });
  34979. *
  34980. * @function Highcharts.chart
  34981. *
  34982. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  34983. * The DOM element to render to, or its id.
  34984. *
  34985. * @param {Highcharts.Options} options
  34986. * The chart options structure.
  34987. *
  34988. * @param {Highcharts.ChartCallbackFunction} [callback]
  34989. * Function to run when the chart has loaded and and all external images are
  34990. * loaded. Defining a
  34991. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  34992. * handler is equivalent.
  34993. *
  34994. * @return {Highcharts.Chart}
  34995. * Returns the Chart object.
  34996. */
  34997. static chart(a, b, c) {
  34998. return new Chart(a, b, c);
  34999. }
  35000. constructor(a, b, c) {
  35001. this.axes = void 0;
  35002. this.axisOffset = void 0;
  35003. this.bounds = void 0;
  35004. this.chartHeight = void 0;
  35005. this.chartWidth = void 0;
  35006. this.clipBox = void 0;
  35007. this.colorCounter = void 0;
  35008. this.container = void 0;
  35009. this.eventOptions = void 0;
  35010. this.index = void 0;
  35011. this.isResizing = void 0;
  35012. this.labelCollectors = void 0;
  35013. this.margin = void 0;
  35014. this.numberFormatter = void 0;
  35015. this.options = void 0;
  35016. this.plotBox = void 0;
  35017. this.plotHeight = void 0;
  35018. this.plotLeft = void 0;
  35019. this.plotTop = void 0;
  35020. this.plotWidth = void 0;
  35021. this.pointCount = void 0;
  35022. this.pointer = void 0;
  35023. this.renderer = void 0;
  35024. this.renderTo = void 0;
  35025. this.series = void 0;
  35026. this.sharedClips = {};
  35027. this.spacing = void 0;
  35028. this.spacingBox = void 0;
  35029. this.symbolCounter = void 0;
  35030. this.time = void 0;
  35031. this.titleOffset = void 0;
  35032. this.userOptions = void 0;
  35033. this.xAxis = void 0;
  35034. this.yAxis = void 0;
  35035. this.zooming = void 0;
  35036. this.getArgs(a, b, c);
  35037. }
  35038. /* *
  35039. *
  35040. * Functions
  35041. *
  35042. * */
  35043. /**
  35044. * Handle the arguments passed to the constructor.
  35045. *
  35046. * @private
  35047. * @function Highcharts.Chart#getArgs
  35048. *
  35049. * @param {...Array<*>} arguments
  35050. * All arguments for the constructor.
  35051. *
  35052. * @emits Highcharts.Chart#event:init
  35053. * @emits Highcharts.Chart#event:afterInit
  35054. */
  35055. getArgs(a, b, c) {
  35056. // Remove the optional first argument, renderTo, and
  35057. // set it on this.
  35058. if (isString(a) || a.nodeName) {
  35059. this.renderTo = a;
  35060. this.init(b, c);
  35061. }
  35062. else {
  35063. this.init(a, b);
  35064. }
  35065. }
  35066. /*
  35067. 是否可选择
  35068. */
  35069. setIsSelect(x){
  35070. if(x){
  35071. const chart = this, options = chart.options.chart, zooming = options.zooming;
  35072. chart.zooming = Object.assign(Object.assign({}, zooming), { type: pick("x", zooming.type), key: pick(options.zoomKey, zooming.key), pinchType: pick(options.pinchType, zooming.pinchType), singleTouch: pick(options.zoomBySingleTouch, zooming.singleTouch, false), resetButton: merge(zooming.resetButton, options.resetZoomButton) });
  35073. }else {
  35074. const chart = this, options = chart.options.chart, zooming = options.zooming;
  35075. chart.zooming = Object.assign(Object.assign({}, zooming), { type: pick("", zooming.type), key: pick(options.zoomKey, zooming.key), pinchType: pick(options.pinchType, zooming.pinchType), singleTouch: pick(options.zoomBySingleTouch, zooming.singleTouch, false), resetButton: merge(zooming.resetButton, options.resetZoomButton) });
  35076. }
  35077. }
  35078. /**
  35079. * Function setting zoom options after chart init and after chart update.
  35080. * Offers support for deprecated options.
  35081. *
  35082. * @private
  35083. * @function Highcharts.Chart#setZoomOptions
  35084. */
  35085. setZoomOptions() {
  35086. console.log("setZoomOptions")
  35087. const chart = this, options = chart.options.chart, zooming = options.zooming;
  35088. chart.zooming = Object.assign(Object.assign({}, zooming), { type: pick(options.zoomType, zooming.type), key: pick(options.zoomKey, zooming.key), pinchType: pick(options.pinchType, zooming.pinchType), singleTouch: pick(options.zoomBySingleTouch, zooming.singleTouch, false), resetButton: merge(zooming.resetButton, options.resetZoomButton) });
  35089. }
  35090. /**
  35091. * Overridable function that initializes the chart. The constructor's
  35092. * arguments are passed on directly.
  35093. *
  35094. * @function Highcharts.Chart#init
  35095. *
  35096. * @param {Highcharts.Options} userOptions
  35097. * Custom options.
  35098. *
  35099. * @param {Function} [callback]
  35100. * Function to run when the chart has loaded and and all external
  35101. * images are loaded.
  35102. *
  35103. *
  35104. * @emits Highcharts.Chart#event:init
  35105. * @emits Highcharts.Chart#event:afterInit
  35106. */
  35107. init(userOptions, callback) {
  35108. // Fire the event with a default function
  35109. fireEvent(this, 'init', { args: arguments }, function () {
  35110. const options = merge(defaultOptions, userOptions), // do the merge
  35111. optionsChart = options.chart;
  35112. /**
  35113. * The original options given to the constructor or a chart factory
  35114. * like {@link Highcharts.chart} and {@link Highcharts.stockChart}.
  35115. * The original options are shallow copied to avoid mutation. The
  35116. * copy, `chart.userOptions`, may later be mutated to reflect
  35117. * updated options throughout the lifetime of the chart.
  35118. *
  35119. * For collections, like `series`, `xAxis` and `yAxis`, the chart
  35120. * user options should always be reflected by the item user option,
  35121. * so for example the following should always be true:
  35122. *
  35123. * `chart.xAxis[0].userOptions === chart.userOptions.xAxis[0]`
  35124. *
  35125. * @name Highcharts.Chart#userOptions
  35126. * @type {Highcharts.Options}
  35127. */
  35128. this.userOptions = extend({}, userOptions);
  35129. this.margin = [];
  35130. this.spacing = [];
  35131. // Pixel data bounds for touch zoom
  35132. this.bounds = { h: {}, v: {} };
  35133. // An array of functions that returns labels that should be
  35134. // considered for anti-collision
  35135. this.labelCollectors = [];
  35136. this.callback = callback;
  35137. this.isResizing = 0;
  35138. /**
  35139. * The options structure for the chart after merging
  35140. * {@link #defaultOptions} and {@link #userOptions}. It contains
  35141. * members for the sub elements like series, legend, tooltip etc.
  35142. *
  35143. * @name Highcharts.Chart#options
  35144. * @type {Highcharts.Options}
  35145. */
  35146. this.options = options;
  35147. /**
  35148. * All the axes in the chart.
  35149. *
  35150. * @see Highcharts.Chart.xAxis
  35151. * @see Highcharts.Chart.yAxis
  35152. *
  35153. * @name Highcharts.Chart#axes
  35154. * @type {Array<Highcharts.Axis>}
  35155. */
  35156. this.axes = [];
  35157. /**
  35158. * All the current series in the chart.
  35159. *
  35160. * @name Highcharts.Chart#series
  35161. * @type {Array<Highcharts.Series>}
  35162. */
  35163. this.series = [];
  35164. /**
  35165. * The `Time` object associated with the chart. Since v6.0.5,
  35166. * time settings can be applied individually for each chart. If
  35167. * no individual settings apply, the `Time` object is shared by
  35168. * all instances.
  35169. *
  35170. * @name Highcharts.Chart#time
  35171. * @type {Highcharts.Time}
  35172. */
  35173. this.time =
  35174. userOptions.time && Object.keys(userOptions.time).length ?
  35175. new Time(userOptions.time) :
  35176. H.time;
  35177. /**
  35178. * Callback function to override the default function that formats
  35179. * all the numbers in the chart. Returns a string with the formatted
  35180. * number.
  35181. *
  35182. * @name Highcharts.Chart#numberFormatter
  35183. * @type {Highcharts.NumberFormatterCallbackFunction}
  35184. */
  35185. this.numberFormatter = optionsChart.numberFormatter || numberFormat;
  35186. /**
  35187. * Whether the chart is in styled mode, meaning all presentational
  35188. * attributes are avoided.
  35189. *
  35190. * @name Highcharts.Chart#styledMode
  35191. * @type {boolean}
  35192. */
  35193. this.styledMode = optionsChart.styledMode;
  35194. this.hasCartesianSeries = optionsChart.showAxes;
  35195. const chart = this;
  35196. /**
  35197. * Index position of the chart in the {@link Highcharts#charts}
  35198. * property.
  35199. *
  35200. * @name Highcharts.Chart#index
  35201. * @type {number}
  35202. * @readonly
  35203. */
  35204. chart.index = charts.length; // Add the chart to the global lookup
  35205. charts.push(chart);
  35206. H.chartCount++;
  35207. // Chart event handlers
  35208. registerEventOptions(this, optionsChart);
  35209. /**
  35210. * A collection of the X axes in the chart.
  35211. *
  35212. * @name Highcharts.Chart#xAxis
  35213. * @type {Array<Highcharts.Axis>}
  35214. */
  35215. chart.xAxis = [];
  35216. /**
  35217. * A collection of the Y axes in the chart.
  35218. *
  35219. * @name Highcharts.Chart#yAxis
  35220. * @type {Array<Highcharts.Axis>}
  35221. *
  35222. * @todo
  35223. * Make events official: Fire the event `afterInit`.
  35224. */
  35225. chart.yAxis = [];
  35226. chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
  35227. this.setZoomOptions();
  35228. // Fire after init but before first render, before axes and series
  35229. // have been initialized.
  35230. fireEvent(chart, 'afterInit');
  35231. chart.firstRender();
  35232. });
  35233. }
  35234. /**
  35235. * Internal function to unitialize an individual series.
  35236. *
  35237. * @private
  35238. * @function Highcharts.Chart#initSeries
  35239. */
  35240. initSeries(options) {
  35241. const chart = this, optionsChart = chart.options.chart, type = (options.type ||
  35242. optionsChart.type), SeriesClass = seriesTypes[type];
  35243. // No such series type
  35244. if (!SeriesClass) {
  35245. error(17, true, chart, { missingModuleFor: type });
  35246. }
  35247. const series = new SeriesClass();
  35248. if (typeof series.init === 'function') {
  35249. series.init(chart, options);
  35250. }
  35251. return series;
  35252. }
  35253. /**
  35254. * Internal function to set data for all series with enabled sorting.
  35255. *
  35256. * @private
  35257. * @function Highcharts.Chart#setSeriesData
  35258. */
  35259. setSeriesData() {
  35260. this.getSeriesOrderByLinks().forEach(function (series) {
  35261. // We need to set data for series with sorting after series init
  35262. if (!series.points && !series.data && series.enabledDataSorting) {
  35263. series.setData(series.options.data, false);
  35264. }
  35265. });
  35266. }
  35267. /**
  35268. * Sort and return chart series in order depending on the number of linked
  35269. * series.
  35270. *
  35271. * @private
  35272. * @function Highcharts.Series#getSeriesOrderByLinks
  35273. */
  35274. getSeriesOrderByLinks() {
  35275. return this.series.concat().sort(function (a, b) {
  35276. if (a.linkedSeries.length || b.linkedSeries.length) {
  35277. return b.linkedSeries.length - a.linkedSeries.length;
  35278. }
  35279. return 0;
  35280. });
  35281. }
  35282. /**
  35283. * Order all series or axes above a given index. When series or axes are
  35284. * added and ordered by configuration, only the last series is handled
  35285. * (#248, #1123, #2456, #6112). This function is called on series and axis
  35286. * initialization and destroy.
  35287. *
  35288. * @private
  35289. * @function Highcharts.Chart#orderItems
  35290. * @param {string} coll The collection name
  35291. * @param {number} [fromIndex=0]
  35292. * If this is given, only the series above this index are handled.
  35293. */
  35294. orderItems(coll, fromIndex = 0) {
  35295. const collection = this[coll],
  35296. // Item options should be reflected in chart.options.series,
  35297. // chart.options.yAxis etc
  35298. optionsArray = this.options[coll] = splat(this.options[coll])
  35299. .slice(), userOptionsArray = this.userOptions[coll] = this.userOptions[coll] ?
  35300. splat(this.userOptions[coll]).slice() :
  35301. [];
  35302. if (this.hasRendered) {
  35303. // Remove all above index
  35304. optionsArray.splice(fromIndex);
  35305. userOptionsArray.splice(fromIndex);
  35306. }
  35307. if (collection) {
  35308. for (let i = fromIndex, iEnd = collection.length; i < iEnd; ++i) {
  35309. const item = collection[i];
  35310. if (item) {
  35311. /**
  35312. * Contains the series' index in the `Chart.series` array.
  35313. *
  35314. * @name Highcharts.Series#index
  35315. * @type {number}
  35316. * @readonly
  35317. */
  35318. item.index = i;
  35319. if (item instanceof Series) {
  35320. item.name = item.getName();
  35321. }
  35322. if (!item.options.isInternal) {
  35323. optionsArray[i] = item.options;
  35324. userOptionsArray[i] = item.userOptions;
  35325. }
  35326. }
  35327. }
  35328. }
  35329. }
  35330. /**
  35331. * Check whether a given point is within the plot area.
  35332. *
  35333. * @function Highcharts.Chart#isInsidePlot
  35334. *
  35335. * @param {number} plotX
  35336. * Pixel x relative to the plot area.
  35337. *
  35338. * @param {number} plotY
  35339. * Pixel y relative to the plot area.
  35340. *
  35341. * @param {Highcharts.ChartIsInsideOptionsObject} [options]
  35342. * Options object.
  35343. *
  35344. * @return {boolean}
  35345. * Returns true if the given point is inside the plot area.
  35346. */
  35347. isInsidePlot(plotX, plotY, options = {}) {
  35348. const { inverted, plotBox, plotLeft, plotTop, scrollablePlotBox } = this;
  35349. let scrollLeft = 0, scrollTop = 0;
  35350. if (options.visiblePlotOnly && this.scrollingContainer) {
  35351. ({ scrollLeft, scrollTop } = this.scrollingContainer);
  35352. }
  35353. const series = options.series, box = (options.visiblePlotOnly && scrollablePlotBox) || plotBox, x = options.inverted ? plotY : plotX, y = options.inverted ? plotX : plotY, e = {
  35354. x,
  35355. y,
  35356. isInsidePlot: true,
  35357. options
  35358. };
  35359. if (!options.ignoreX) {
  35360. const xAxis = (series &&
  35361. (inverted && !this.polar ? series.yAxis : series.xAxis)) || {
  35362. pos: plotLeft,
  35363. len: Infinity
  35364. };
  35365. const chartX = options.paneCoordinates ?
  35366. xAxis.pos + x : plotLeft + x;
  35367. if (!(chartX >= Math.max(scrollLeft + plotLeft, xAxis.pos) &&
  35368. chartX <= Math.min(scrollLeft + plotLeft + box.width, xAxis.pos + xAxis.len))) {
  35369. e.isInsidePlot = false;
  35370. }
  35371. }
  35372. if (!options.ignoreY && e.isInsidePlot) {
  35373. const yAxis = (!inverted && options.axis &&
  35374. !options.axis.isXAxis && options.axis) || (series && (inverted ? series.xAxis : series.yAxis)) || {
  35375. pos: plotTop,
  35376. len: Infinity
  35377. };
  35378. const chartY = options.paneCoordinates ?
  35379. yAxis.pos + y : plotTop + y;
  35380. if (!(chartY >= Math.max(scrollTop + plotTop, yAxis.pos) &&
  35381. chartY <= Math.min(scrollTop + plotTop + box.height, yAxis.pos + yAxis.len))) {
  35382. e.isInsidePlot = false;
  35383. }
  35384. }
  35385. fireEvent(this, 'afterIsInsidePlot', e);
  35386. return e.isInsidePlot;
  35387. }
  35388. /**
  35389. * Redraw the chart after changes have been done to the data, axis extremes
  35390. * chart size or chart elements. All methods for updating axes, series or
  35391. * points have a parameter for redrawing the chart. This is `true` by
  35392. * default. But in many cases you want to do more than one operation on the
  35393. * chart before redrawing, for example add a number of points. In those
  35394. * cases it is a waste of resources to redraw the chart for each new point
  35395. * added. So you add the points and call `chart.redraw()` after.
  35396. *
  35397. * @function Highcharts.Chart#redraw
  35398. *
  35399. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  35400. * If or how to apply animation to the redraw. When `undefined`, it applies
  35401. * the animation that is set in the `chart.animation` option.
  35402. *
  35403. * @emits Highcharts.Chart#event:afterSetExtremes
  35404. * @emits Highcharts.Chart#event:beforeRedraw
  35405. * @emits Highcharts.Chart#event:predraw
  35406. * @emits Highcharts.Chart#event:redraw
  35407. * @emits Highcharts.Chart#event:render
  35408. * @emits Highcharts.Chart#event:updatedData
  35409. */
  35410. redraw(animation) {
  35411. fireEvent(this, 'beforeRedraw');
  35412. const chart = this, axes = chart.hasCartesianSeries ? chart.axes : chart.colorAxis || [], series = chart.series, pointer = chart.pointer, legend = chart.legend, legendUserOptions = chart.userOptions.legend, renderer = chart.renderer, isHiddenChart = renderer.isHidden(), afterRedraw = [];
  35413. let hasDirtyStacks, hasStackedSeries, i, isDirtyBox = chart.isDirtyBox, redrawLegend = chart.isDirtyLegend, serie;
  35414. renderer.rootFontSize = renderer.boxWrapper.getStyle('font-size');
  35415. // Handle responsive rules, not only on resize (#6130)
  35416. if (chart.setResponsive) {
  35417. chart.setResponsive(false);
  35418. }
  35419. // Set the global animation. When chart.hasRendered is not true, the
  35420. // redraw call comes from a responsive rule and animation should not
  35421. // occur.
  35422. setAnimation(chart.hasRendered ? animation : false, chart);
  35423. if (isHiddenChart) {
  35424. chart.temporaryDisplay();
  35425. }
  35426. // Adjust title layout (reflow multiline text)
  35427. chart.layOutTitles(false);
  35428. // link stacked series
  35429. i = series.length;
  35430. while (i--) {
  35431. serie = series[i];
  35432. if (serie.options.stacking || serie.options.centerInCategory) {
  35433. hasStackedSeries = true;
  35434. if (serie.isDirty) {
  35435. hasDirtyStacks = true;
  35436. break;
  35437. }
  35438. }
  35439. }
  35440. if (hasDirtyStacks) { // mark others as dirty
  35441. i = series.length;
  35442. while (i--) {
  35443. serie = series[i];
  35444. if (serie.options.stacking) {
  35445. serie.isDirty = true;
  35446. }
  35447. }
  35448. }
  35449. // Handle updated data in the series
  35450. series.forEach(function (serie) {
  35451. if (serie.isDirty) {
  35452. if (serie.options.legendType === 'point') {
  35453. if (typeof serie.updateTotals === 'function') {
  35454. serie.updateTotals();
  35455. }
  35456. redrawLegend = true;
  35457. }
  35458. else if (legendUserOptions &&
  35459. (legendUserOptions.labelFormatter ||
  35460. legendUserOptions.labelFormat)) {
  35461. redrawLegend = true; // #2165
  35462. }
  35463. }
  35464. if (serie.isDirtyData) {
  35465. fireEvent(serie, 'updatedData');
  35466. }
  35467. });
  35468. // handle added or removed series
  35469. if (redrawLegend && legend && legend.options.enabled) {
  35470. // draw legend graphics
  35471. legend.render();
  35472. chart.isDirtyLegend = false;
  35473. }
  35474. // reset stacks
  35475. if (hasStackedSeries) {
  35476. chart.getStacks();
  35477. }
  35478. // set axes scales
  35479. axes.forEach(function (axis) {
  35480. axis.updateNames();
  35481. axis.setScale();
  35482. });
  35483. chart.getMargins(); // #3098
  35484. // If one axis is dirty, all axes must be redrawn (#792, #2169)
  35485. axes.forEach(function (axis) {
  35486. if (axis.isDirty) {
  35487. isDirtyBox = true;
  35488. }
  35489. });
  35490. // redraw axes
  35491. axes.forEach(function (axis) {
  35492. // Fire 'afterSetExtremes' only if extremes are set
  35493. const key = axis.min + ',' + axis.max;
  35494. if (axis.extKey !== key) { // #821, #4452
  35495. axis.extKey = key;
  35496. // prevent a recursive call to chart.redraw() (#1119)
  35497. afterRedraw.push(function () {
  35498. fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751
  35499. delete axis.eventArgs;
  35500. });
  35501. }
  35502. if (isDirtyBox || hasStackedSeries) {
  35503. axis.redraw();
  35504. }
  35505. });
  35506. // the plot areas size has changed
  35507. if (isDirtyBox) {
  35508. chart.drawChartBox();
  35509. }
  35510. // Fire an event before redrawing series, used by the boost module to
  35511. // clear previous series renderings.
  35512. fireEvent(chart, 'predraw');
  35513. // redraw affected series
  35514. series.forEach(function (serie) {
  35515. if ((isDirtyBox || serie.isDirty) && serie.visible) {
  35516. serie.redraw();
  35517. }
  35518. // Set it here, otherwise we will have unlimited 'updatedData' calls
  35519. // for a hidden series after setData(). Fixes #6012
  35520. serie.isDirtyData = false;
  35521. });
  35522. // move tooltip or reset
  35523. if (pointer) {
  35524. pointer.reset(true);
  35525. }
  35526. // redraw if canvas
  35527. renderer.draw();
  35528. // Fire the events
  35529. fireEvent(chart, 'redraw');
  35530. fireEvent(chart, 'render');
  35531. if (isHiddenChart) {
  35532. chart.temporaryDisplay(true);
  35533. }
  35534. // Fire callbacks that are put on hold until after the redraw
  35535. afterRedraw.forEach(function (callback) {
  35536. callback.call();
  35537. });
  35538. }
  35539. /**
  35540. * Get an axis, series or point object by `id` as given in the configuration
  35541. * options. Returns `undefined` if no item is found.
  35542. *
  35543. * @sample highcharts/plotoptions/series-id/
  35544. * Get series by id
  35545. *
  35546. * @function Highcharts.Chart#get
  35547. *
  35548. * @param {string} id
  35549. * The id as given in the configuration options.
  35550. *
  35551. * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
  35552. * The retrieved item.
  35553. */
  35554. get(id) {
  35555. const series = this.series;
  35556. /**
  35557. * @private
  35558. */
  35559. function itemById(item) {
  35560. return (item.id === id ||
  35561. (item.options && item.options.id === id));
  35562. }
  35563. let ret =
  35564. // Search axes
  35565. find(this.axes, itemById) ||
  35566. // Search series
  35567. find(this.series, itemById);
  35568. // Search points
  35569. for (let i = 0; !ret && i < series.length; i++) {
  35570. ret = find(series[i].points || [], itemById);
  35571. }
  35572. return ret;
  35573. }
  35574. /**
  35575. * Create the Axis instances based on the config options.
  35576. *
  35577. * @private
  35578. * @function Highcharts.Chart#getAxes
  35579. * @emits Highcharts.Chart#event:afterGetAxes
  35580. * @emits Highcharts.Chart#event:getAxes
  35581. */
  35582. getAxes() {
  35583. const options = this.options;
  35584. fireEvent(this, 'getAxes');
  35585. for (const coll of ['xAxis', 'yAxis']) {
  35586. const arr = options[coll] = splat(options[coll] || {});
  35587. for (const axisOptions of arr) {
  35588. // eslint-disable-next-line no-new
  35589. new Axis(this, axisOptions, coll);
  35590. }
  35591. }
  35592. fireEvent(this, 'afterGetAxes');
  35593. }
  35594. /**
  35595. * Returns an array of all currently selected points in the chart. Points
  35596. * can be selected by clicking or programmatically by the
  35597. * {@link Highcharts.Point#select}
  35598. * function.
  35599. *
  35600. * @sample highcharts/plotoptions/series-allowpointselect-line/
  35601. * Get selected points
  35602. *
  35603. * @function Highcharts.Chart#getSelectedPoints
  35604. *
  35605. * @return {Array<Highcharts.Point>}
  35606. * The currently selected points.
  35607. */
  35608. getSelectedPoints() {
  35609. return this.series.reduce((acc, series) => {
  35610. // For one-to-one points inspect series.data in order to retrieve
  35611. // points outside the visible range (#6445). For grouped data,
  35612. // inspect the generated series.points.
  35613. series.getPointsCollection()
  35614. .forEach((point) => {
  35615. if (pick(point.selectedStaging, point.selected)) {
  35616. acc.push(point);
  35617. }
  35618. });
  35619. return acc;
  35620. }, []);
  35621. }
  35622. /**
  35623. * Returns an array of all currently selected series in the chart. Series
  35624. * can be selected either programmatically by the
  35625. * {@link Highcharts.Series#select}
  35626. * function or by checking the checkbox next to the legend item if
  35627. * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
  35628. * is true.
  35629. *
  35630. * @sample highcharts/members/chart-getselectedseries/
  35631. * Get selected series
  35632. *
  35633. * @function Highcharts.Chart#getSelectedSeries
  35634. *
  35635. * @return {Array<Highcharts.Series>}
  35636. * The currently selected series.
  35637. */
  35638. getSelectedSeries() {
  35639. return this.series.filter(function (serie) {
  35640. return serie.selected;
  35641. });
  35642. }
  35643. /**
  35644. * Set a new title or subtitle for the chart.
  35645. *
  35646. * @sample highcharts/members/chart-settitle/
  35647. * Set title text and styles
  35648. *
  35649. * @function Highcharts.Chart#setTitle
  35650. *
  35651. * @param {Highcharts.TitleOptions} [titleOptions]
  35652. * New title options. The title text itself is set by the
  35653. * `titleOptions.text` property.
  35654. *
  35655. * @param {Highcharts.SubtitleOptions} [subtitleOptions]
  35656. * New subtitle options. The subtitle text itself is set by the
  35657. * `subtitleOptions.text` property.
  35658. *
  35659. * @param {boolean} [redraw]
  35660. * Whether to redraw the chart or wait for a later call to
  35661. * `chart.redraw()`.
  35662. */
  35663. setTitle(titleOptions, subtitleOptions, redraw) {
  35664. this.applyDescription('title', titleOptions);
  35665. this.applyDescription('subtitle', subtitleOptions);
  35666. // The initial call also adds the caption. On update, chart.update will
  35667. // relay to Chart.setCaption.
  35668. this.applyDescription('caption', void 0);
  35669. this.layOutTitles(redraw);
  35670. }
  35671. /**
  35672. * Apply a title, subtitle or caption for the chart
  35673. *
  35674. * @private
  35675. * @function Highcharts.Chart#applyDescription
  35676. * @param name {string}
  35677. * Either title, subtitle or caption
  35678. * @param {Highcharts.TitleOptions|Highcharts.SubtitleOptions|Highcharts.CaptionOptions|undefined} explicitOptions
  35679. * The options to set, will be merged with default options.
  35680. */
  35681. applyDescription(name, explicitOptions) {
  35682. const chart = this;
  35683. // Merge default options with explicit options
  35684. const options = this.options[name] = merge(this.options[name], explicitOptions);
  35685. let elem = this[name];
  35686. if (elem && explicitOptions) {
  35687. this[name] = elem = elem.destroy(); // remove old
  35688. }
  35689. if (options && !elem) {
  35690. elem = this.renderer.text(options.text, 0, 0, options.useHTML)
  35691. .attr({
  35692. align: options.align,
  35693. 'class': 'highcharts-' + name,
  35694. zIndex: options.zIndex || 4
  35695. })
  35696. .add();
  35697. // Update methods, relay to `applyDescription`
  35698. elem.update = function (updateOptions, redraw) {
  35699. chart.applyDescription(name, updateOptions);
  35700. chart.layOutTitles(redraw);
  35701. };
  35702. // Presentational
  35703. if (!this.styledMode) {
  35704. elem.css(extend(name === 'title' ? {
  35705. // #2944
  35706. fontSize: this.options.isStock ? '1em' : '1.2em'
  35707. } : {}, options.style));
  35708. }
  35709. /**
  35710. * The chart title. The title has an `update` method that allows
  35711. * modifying the options directly or indirectly via
  35712. * `chart.update`.
  35713. *
  35714. * @sample highcharts/members/title-update/
  35715. * Updating titles
  35716. *
  35717. * @name Highcharts.Chart#title
  35718. * @type {Highcharts.TitleObject}
  35719. */
  35720. /**
  35721. * The chart subtitle. The subtitle has an `update` method that
  35722. * allows modifying the options directly or indirectly via
  35723. * `chart.update`.
  35724. *
  35725. * @name Highcharts.Chart#subtitle
  35726. * @type {Highcharts.SubtitleObject}
  35727. */
  35728. this[name] = elem;
  35729. }
  35730. }
  35731. /**
  35732. * Internal function to lay out the chart title, subtitle and caption, and
  35733. * cache the full offset height for use in `getMargins`. The result is
  35734. * stored in `this.titleOffset`.
  35735. *
  35736. * @private
  35737. * @function Highcharts.Chart#layOutTitles
  35738. *
  35739. * @param {boolean} [redraw=true]
  35740. * @emits Highcharts.Chart#event:afterLayOutTitles
  35741. */
  35742. layOutTitles(redraw = true) {
  35743. const titleOffset = [0, 0, 0], renderer = this.renderer, spacingBox = this.spacingBox;
  35744. // Lay out the title and the subtitle respectively
  35745. ['title', 'subtitle', 'caption'].forEach(function (key) {
  35746. const title = this[key], titleOptions = (this.options[key]), verticalAlign = titleOptions.verticalAlign || 'top', offset = key === 'title' ?
  35747. verticalAlign === 'top' ? -3 : 0 :
  35748. // Floating subtitle (#6574)
  35749. verticalAlign === 'top' ? titleOffset[0] + 2 : 0;
  35750. if (title) {
  35751. title
  35752. .css({
  35753. width: (titleOptions.width ||
  35754. spacingBox.width + (titleOptions.widthAdjust || 0)) + 'px'
  35755. });
  35756. const baseline = renderer.fontMetrics(title).b,
  35757. // Skip the cache for HTML (#3481, #11666)
  35758. height = Math.round(title.getBBox(titleOptions.useHTML).height);
  35759. title.align(extend({
  35760. y: verticalAlign === 'bottom' ?
  35761. baseline :
  35762. offset + baseline,
  35763. height
  35764. }, titleOptions), false, 'spacingBox');
  35765. if (!titleOptions.floating) {
  35766. if (verticalAlign === 'top') {
  35767. titleOffset[0] = Math.ceil(titleOffset[0] +
  35768. height);
  35769. }
  35770. else if (verticalAlign === 'bottom') {
  35771. titleOffset[2] = Math.ceil(titleOffset[2] +
  35772. height);
  35773. }
  35774. }
  35775. }
  35776. }, this);
  35777. // Handle title.margin and caption.margin
  35778. if (titleOffset[0] &&
  35779. (this.options.title.verticalAlign || 'top') === 'top') {
  35780. titleOffset[0] += this.options.title.margin;
  35781. }
  35782. if (titleOffset[2] &&
  35783. this.options.caption.verticalAlign === 'bottom') {
  35784. titleOffset[2] += this.options.caption.margin;
  35785. }
  35786. const requiresDirtyBox = (!this.titleOffset ||
  35787. this.titleOffset.join(',') !== titleOffset.join(','));
  35788. // Used in getMargins
  35789. this.titleOffset = titleOffset;
  35790. fireEvent(this, 'afterLayOutTitles');
  35791. if (!this.isDirtyBox && requiresDirtyBox) {
  35792. this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
  35793. // Redraw if necessary (#2719, #2744)
  35794. if (this.hasRendered && redraw && this.isDirtyBox) {
  35795. this.redraw();
  35796. }
  35797. }
  35798. }
  35799. /**
  35800. * Internal function to get the available size of the container element
  35801. *
  35802. * @private
  35803. * @function Highcharts.Chart#getContainerBox
  35804. */
  35805. getContainerBox() {
  35806. return {
  35807. width: getStyle(this.renderTo, 'width', true) || 0,
  35808. height: getStyle(this.renderTo, 'height', true) || 0
  35809. };
  35810. }
  35811. /**
  35812. * Internal function to get the chart width and height according to options
  35813. * and container size. Sets {@link Chart.chartWidth} and
  35814. * {@link Chart.chartHeight}.
  35815. *
  35816. * @private
  35817. * @function Highcharts.Chart#getChartSize
  35818. */
  35819. getChartSize() {
  35820. const chart = this, optionsChart = chart.options.chart, widthOption = optionsChart.width, heightOption = optionsChart.height, containerBox = chart.getContainerBox();
  35821. /**
  35822. * The current pixel width of the chart.
  35823. *
  35824. * @name Highcharts.Chart#chartWidth
  35825. * @type {number}
  35826. */
  35827. chart.chartWidth = Math.max(// #1393
  35828. 0, widthOption || containerBox.width || 600 // #1460
  35829. );
  35830. /**
  35831. * The current pixel height of the chart.
  35832. *
  35833. * @name Highcharts.Chart#chartHeight
  35834. * @type {number}
  35835. */
  35836. chart.chartHeight = Math.max(0, relativeLength(heightOption, chart.chartWidth) ||
  35837. (containerBox.height > 1 ? containerBox.height : 400));
  35838. chart.containerBox = containerBox;
  35839. }
  35840. /**
  35841. * If the renderTo element has no offsetWidth, most likely one or more of
  35842. * its parents are hidden. Loop up the DOM tree to temporarily display the
  35843. * parents, then save the original display properties, and when the true
  35844. * size is retrieved, reset them. Used on first render and on redraws.
  35845. *
  35846. * @private
  35847. * @function Highcharts.Chart#temporaryDisplay
  35848. *
  35849. * @param {boolean} [revert]
  35850. * Revert to the saved original styles.
  35851. */
  35852. temporaryDisplay(revert) {
  35853. let node = this.renderTo, tempStyle;
  35854. if (!revert) {
  35855. while (node && node.style) {
  35856. // When rendering to a detached node, it needs to be temporarily
  35857. // attached in order to read styling and bounding boxes (#5783,
  35858. // #7024).
  35859. if (!doc.body.contains(node) && !node.parentNode) {
  35860. node.hcOrigDetached = true;
  35861. doc.body.appendChild(node);
  35862. }
  35863. if (getStyle(node, 'display', false) === 'none' ||
  35864. node.hcOricDetached) {
  35865. node.hcOrigStyle = {
  35866. display: node.style.display,
  35867. height: node.style.height,
  35868. overflow: node.style.overflow
  35869. };
  35870. tempStyle = {
  35871. display: 'block',
  35872. overflow: 'hidden'
  35873. };
  35874. if (node !== this.renderTo) {
  35875. tempStyle.height = 0;
  35876. }
  35877. css(node, tempStyle);
  35878. // If it still doesn't have an offset width after setting
  35879. // display to block, it probably has an !important priority
  35880. // #2631, 6803
  35881. if (!node.offsetWidth) {
  35882. node.style.setProperty('display', 'block', 'important');
  35883. }
  35884. }
  35885. node = node.parentNode;
  35886. if (node === doc.body) {
  35887. break;
  35888. }
  35889. }
  35890. }
  35891. else {
  35892. while (node && node.style) {
  35893. if (node.hcOrigStyle) {
  35894. css(node, node.hcOrigStyle);
  35895. delete node.hcOrigStyle;
  35896. }
  35897. if (node.hcOrigDetached) {
  35898. doc.body.removeChild(node);
  35899. node.hcOrigDetached = false;
  35900. }
  35901. node = node.parentNode;
  35902. }
  35903. }
  35904. }
  35905. /**
  35906. * Set the {@link Chart.container|chart container's} class name, in
  35907. * addition to `highcharts-container`.
  35908. *
  35909. * @function Highcharts.Chart#setClassName
  35910. *
  35911. * @param {string} [className]
  35912. * The additional class name.
  35913. */
  35914. setClassName(className) {
  35915. this.container.className = 'highcharts-container ' + (className || '');
  35916. }
  35917. /**
  35918. * Get the containing element, determine the size and create the inner
  35919. * container div to hold the chart.
  35920. *
  35921. * @private
  35922. * @function Highcharts.Chart#afterGetContainer
  35923. * @emits Highcharts.Chart#event:afterGetContainer
  35924. */
  35925. getContainer() {
  35926. const chart = this, options = chart.options, optionsChart = options.chart, indexAttrName = 'data-highcharts-chart', containerId = uniqueKey();
  35927. let containerStyle, renderTo = chart.renderTo;
  35928. if (!renderTo) {
  35929. chart.renderTo = renderTo =
  35930. optionsChart.renderTo;
  35931. }
  35932. if (isString(renderTo)) {
  35933. chart.renderTo = renderTo =
  35934. doc.getElementById(renderTo);
  35935. }
  35936. // Display an error if the renderTo is wrong
  35937. if (!renderTo) {
  35938. error(13, true, chart);
  35939. }
  35940. // If the container already holds a chart, destroy it. The check for
  35941. // hasRendered is there because web pages that are saved to disk from
  35942. // the browser, will preserve the data-highcharts-chart attribute and
  35943. // the SVG contents, but not an interactive chart. So in this case,
  35944. // charts[oldChartIndex] will point to the wrong chart if any (#2609).
  35945. const oldChartIndex = pInt(attr(renderTo, indexAttrName));
  35946. if (isNumber(oldChartIndex) &&
  35947. charts[oldChartIndex] &&
  35948. charts[oldChartIndex].hasRendered) {
  35949. charts[oldChartIndex].destroy();
  35950. }
  35951. // Make a reference to the chart from the div
  35952. attr(renderTo, indexAttrName, chart.index);
  35953. // remove previous chart
  35954. renderTo.innerHTML = AST.emptyHTML;
  35955. // If the container doesn't have an offsetWidth, it has or is a child of
  35956. // a node that has display:none. We need to temporarily move it out to a
  35957. // visible state to determine the size, else the legend and tooltips
  35958. // won't render properly. The skipClone option is used in sparklines as
  35959. // a micro optimization, saving about 1-2 ms each chart.
  35960. if (!optionsChart.skipClone && !renderTo.offsetWidth) {
  35961. chart.temporaryDisplay();
  35962. }
  35963. // get the width and height
  35964. chart.getChartSize();
  35965. const chartWidth = chart.chartWidth;
  35966. const chartHeight = chart.chartHeight;
  35967. // Allow table cells and flex-boxes to shrink without the chart blocking
  35968. // them out (#6427)
  35969. css(renderTo, { overflow: 'hidden' });
  35970. // Create the inner container
  35971. if (!chart.styledMode) {
  35972. containerStyle = extend({
  35973. position: 'relative',
  35974. // needed for context menu (avoidscrollbars) and content
  35975. // overflow in IE
  35976. overflow: 'hidden',
  35977. width: chartWidth + 'px',
  35978. height: chartHeight + 'px',
  35979. textAlign: 'left',
  35980. lineHeight: 'normal',
  35981. zIndex: 0,
  35982. '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
  35983. userSelect: 'none',
  35984. 'touch-action': 'manipulation',
  35985. outline: 'none'
  35986. }, optionsChart.style || {});
  35987. }
  35988. /**
  35989. * The containing HTML element of the chart. The container is
  35990. * dynamically inserted into the element given as the `renderTo`
  35991. * parameter in the {@link Highcharts#chart} constructor.
  35992. *
  35993. * @name Highcharts.Chart#container
  35994. * @type {Highcharts.HTMLDOMElement}
  35995. */
  35996. const container = createElement('div', {
  35997. id: containerId
  35998. }, containerStyle, renderTo);
  35999. chart.container = container;
  36000. // cache the cursor (#1650)
  36001. chart._cursor = container.style.cursor;
  36002. // Initialize the renderer
  36003. const Renderer = optionsChart.renderer || !svg ?
  36004. RendererRegistry.getRendererType(optionsChart.renderer) :
  36005. SVGRenderer;
  36006. /**
  36007. * The renderer instance of the chart. Each chart instance has only one
  36008. * associated renderer.
  36009. *
  36010. * @name Highcharts.Chart#renderer
  36011. * @type {Highcharts.SVGRenderer}
  36012. */
  36013. chart.renderer = new Renderer(container, chartWidth, chartHeight, void 0, optionsChart.forExport, options.exporting && options.exporting.allowHTML, chart.styledMode);
  36014. chart.containerBox = chart.getContainerBox();
  36015. // Set the initial animation from the options
  36016. setAnimation(void 0, chart);
  36017. chart.setClassName(optionsChart.className);
  36018. if (!chart.styledMode) {
  36019. chart.renderer.setStyle(optionsChart.style);
  36020. }
  36021. else {
  36022. // Initialize definitions
  36023. for (const key in options.defs) { // eslint-disable-line guard-for-in
  36024. this.renderer.definition(options.defs[key]);
  36025. }
  36026. }
  36027. // Add a reference to the charts index
  36028. chart.renderer.chartIndex = chart.index;
  36029. fireEvent(this, 'afterGetContainer');
  36030. }
  36031. /**
  36032. * Calculate margins by rendering axis labels in a preliminary position.
  36033. * Title, subtitle and legend have already been rendered at this stage, but
  36034. * will be moved into their final positions.
  36035. *
  36036. * @private
  36037. * @function Highcharts.Chart#getMargins
  36038. * @emits Highcharts.Chart#event:getMargins
  36039. */
  36040. getMargins(skipAxes) {
  36041. const { spacing, margin, titleOffset } = this;
  36042. this.resetMargins();
  36043. // Adjust for title and subtitle
  36044. if (titleOffset[0] && !defined(margin[0])) {
  36045. this.plotTop = Math.max(this.plotTop, titleOffset[0] + spacing[0]);
  36046. }
  36047. if (titleOffset[2] && !defined(margin[2])) {
  36048. this.marginBottom = Math.max(this.marginBottom, titleOffset[2] + spacing[2]);
  36049. }
  36050. // Adjust for legend
  36051. if (this.legend && this.legend.display) {
  36052. this.legend.adjustMargins(margin, spacing);
  36053. }
  36054. fireEvent(this, 'getMargins');
  36055. if (!skipAxes) {
  36056. this.getAxisMargins();
  36057. }
  36058. }
  36059. /**
  36060. * @private
  36061. * @function Highcharts.Chart#getAxisMargins
  36062. */
  36063. getAxisMargins() {
  36064. const chart = this,
  36065. // [top, right, bottom, left]
  36066. axisOffset = chart.axisOffset = [0, 0, 0, 0], colorAxis = chart.colorAxis, margin = chart.margin, getOffset = function (axes) {
  36067. axes.forEach(function (axis) {
  36068. if (axis.visible) {
  36069. axis.getOffset();
  36070. }
  36071. });
  36072. };
  36073. // pre-render axes to get labels offset width
  36074. if (chart.hasCartesianSeries) {
  36075. getOffset(chart.axes);
  36076. }
  36077. else if (colorAxis && colorAxis.length) {
  36078. getOffset(colorAxis);
  36079. }
  36080. // Add the axis offsets
  36081. marginNames.forEach(function (m, side) {
  36082. if (!defined(margin[side])) {
  36083. chart[m] += axisOffset[side];
  36084. }
  36085. });
  36086. chart.setChartSize();
  36087. }
  36088. /**
  36089. * Return the current options of the chart, but only those that differ from
  36090. * default options. Items that can be either an object or an array of
  36091. * objects, like `series`, `xAxis` and `yAxis`, are always returned as
  36092. * array.
  36093. *
  36094. * @sample highcharts/members/chart-getoptions
  36095. *
  36096. * @function Highcharts.Chart#getOptions
  36097. *
  36098. * @since 11.1.0
  36099. */
  36100. getOptions() {
  36101. return diffObjects(this.userOptions, defaultOptions);
  36102. }
  36103. /**
  36104. * Reflows the chart to its container. By default, the Resize Observer is
  36105. * attached to the chart's div which allows to reflows the chart
  36106. * automatically to its container, as per the
  36107. * [chart.reflow](https://api.highcharts.com/highcharts/chart.reflow)
  36108. * option.
  36109. *
  36110. * @sample highcharts/chart/events-container/
  36111. * Pop up and reflow
  36112. *
  36113. * @function Highcharts.Chart#reflow
  36114. *
  36115. * @param {global.Event} [e]
  36116. * Event arguments. Used primarily when the function is called
  36117. * internally as a response to window resize.
  36118. */
  36119. reflow(e) {
  36120. const chart = this, optionsChart = chart.options.chart, hasUserSize = (defined(optionsChart.width) &&
  36121. defined(optionsChart.height)), oldBox = chart.containerBox, containerBox = chart.getContainerBox();
  36122. delete chart.pointer.chartPosition;
  36123. // Width and height checks for display:none. Target is doc in Opera
  36124. // and win in Firefox, Chrome and IE9.
  36125. if (!hasUserSize &&
  36126. !chart.isPrinting &&
  36127. oldBox &&
  36128. // When fired by resize observer inside hidden container
  36129. containerBox.width) {
  36130. if (containerBox.width !== oldBox.width ||
  36131. containerBox.height !== oldBox.height) {
  36132. U.clearTimeout(chart.reflowTimeout);
  36133. // When called from window.resize, e is set, else it's called
  36134. // directly (#2224)
  36135. chart.reflowTimeout = syncTimeout(function () {
  36136. // Set size, it may have been destroyed in the meantime
  36137. // (#1257)
  36138. if (chart.container) {
  36139. chart.setSize(void 0, void 0, false);
  36140. }
  36141. }, e ? 100 : 0);
  36142. }
  36143. chart.containerBox = containerBox;
  36144. }
  36145. }
  36146. /**
  36147. * Toggle the event handlers necessary for auto resizing, depending on the
  36148. * `chart.reflow` option.
  36149. *
  36150. * @private
  36151. * @function Highcharts.Chart#setReflow
  36152. */
  36153. setReflow() {
  36154. const chart = this;
  36155. const runReflow = (e) => {
  36156. var _a;
  36157. if (((_a = chart.options) === null || _a === void 0 ? void 0 : _a.chart.reflow) && chart.hasLoaded) {
  36158. chart.reflow(e);
  36159. }
  36160. };
  36161. if (typeof ResizeObserver === 'function') {
  36162. (new ResizeObserver(runReflow)).observe(chart.renderTo);
  36163. // Fallback for more legacy browser versions.
  36164. }
  36165. else {
  36166. const unbind = addEvent(win, 'resize', runReflow);
  36167. addEvent(this, 'destroy', unbind);
  36168. }
  36169. }
  36170. /**
  36171. * Resize the chart to a given width and height. In order to set the width
  36172. * only, the height argument may be skipped. To set the height only, pass
  36173. * `undefined` for the width.
  36174. *
  36175. * @sample highcharts/members/chart-setsize-button/
  36176. * Test resizing from buttons
  36177. * @sample highcharts/members/chart-setsize-jquery-resizable/
  36178. * Add a jQuery UI resizable
  36179. * @sample stock/members/chart-setsize/
  36180. * Highcharts Stock with UI resizable
  36181. *
  36182. * @function Highcharts.Chart#setSize
  36183. *
  36184. * @param {number|null} [width]
  36185. * The new pixel width of the chart. Since v4.2.6, the argument can
  36186. * be `undefined` in order to preserve the current value (when
  36187. * setting height only), or `null` to adapt to the width of the
  36188. * containing element.
  36189. *
  36190. * @param {number|null} [height]
  36191. * The new pixel height of the chart. Since v4.2.6, the argument can
  36192. * be `undefined` in order to preserve the current value, or `null`
  36193. * in order to adapt to the height of the containing element.
  36194. *
  36195. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36196. * Whether and how to apply animation. When `undefined`, it applies
  36197. * the animation that is set in the `chart.animation` option.
  36198. *
  36199. *
  36200. * @emits Highcharts.Chart#event:endResize
  36201. * @emits Highcharts.Chart#event:resize
  36202. */
  36203. setSize(width, height, animation) {
  36204. const chart = this, renderer = chart.renderer;
  36205. // Handle the isResizing counter
  36206. chart.isResizing += 1;
  36207. // set the animation for the current process
  36208. setAnimation(animation, chart);
  36209. const globalAnimation = renderer.globalAnimation;
  36210. chart.oldChartHeight = chart.chartHeight;
  36211. chart.oldChartWidth = chart.chartWidth;
  36212. if (typeof width !== 'undefined') {
  36213. chart.options.chart.width = width;
  36214. }
  36215. if (typeof height !== 'undefined') {
  36216. chart.options.chart.height = height;
  36217. }
  36218. chart.getChartSize();
  36219. // Resize the container with the global animation applied if enabled
  36220. // (#2503)
  36221. if (!chart.styledMode) {
  36222. (globalAnimation ? animate : css)(chart.container, {
  36223. width: chart.chartWidth + 'px',
  36224. height: chart.chartHeight + 'px'
  36225. }, globalAnimation);
  36226. }
  36227. chart.setChartSize(true);
  36228. renderer.setSize(chart.chartWidth, chart.chartHeight, globalAnimation);
  36229. // handle axes
  36230. chart.axes.forEach(function (axis) {
  36231. axis.isDirty = true;
  36232. axis.setScale();
  36233. });
  36234. chart.isDirtyLegend = true; // force legend redraw
  36235. chart.isDirtyBox = true; // force redraw of plot and chart border
  36236. chart.layOutTitles(); // #2857
  36237. chart.getMargins();
  36238. chart.redraw(globalAnimation);
  36239. chart.oldChartHeight = null;
  36240. fireEvent(chart, 'resize');
  36241. // Fire endResize and set isResizing back. If animation is disabled,
  36242. // fire without delay
  36243. syncTimeout(function () {
  36244. if (chart) {
  36245. fireEvent(chart, 'endResize', null, function () {
  36246. chart.isResizing -= 1;
  36247. });
  36248. }
  36249. }, animObject(globalAnimation).duration);
  36250. }
  36251. /**
  36252. * Set the public chart properties. This is done before and after the
  36253. * pre-render to determine margin sizes.
  36254. *
  36255. * @private
  36256. * @function Highcharts.Chart#setChartSize
  36257. * @emits Highcharts.Chart#event:afterSetChartSize
  36258. */
  36259. setChartSize(skipAxes) {
  36260. const chart = this, inverted = chart.inverted, renderer = chart.renderer, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, optionsChart = chart.options.chart, spacing = chart.spacing, clipOffset = chart.clipOffset;
  36261. let plotLeft, plotTop, plotWidth, plotHeight;
  36262. /**
  36263. * The current left position of the plot area in pixels.
  36264. *
  36265. * @name Highcharts.Chart#plotLeft
  36266. * @type {number}
  36267. */
  36268. chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
  36269. /**
  36270. * The current top position of the plot area in pixels.
  36271. *
  36272. * @name Highcharts.Chart#plotTop
  36273. * @type {number}
  36274. */
  36275. chart.plotTop = plotTop = Math.round(chart.plotTop);
  36276. /**
  36277. * The current width of the plot area in pixels.
  36278. *
  36279. * @name Highcharts.Chart#plotWidth
  36280. * @type {number}
  36281. */
  36282. chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
  36283. /**
  36284. * The current height of the plot area in pixels.
  36285. *
  36286. * @name Highcharts.Chart#plotHeight
  36287. * @type {number}
  36288. */
  36289. chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
  36290. chart.plotSizeX = inverted ? plotHeight : plotWidth;
  36291. chart.plotSizeY = inverted ? plotWidth : plotHeight;
  36292. chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
  36293. // Set boxes used for alignment
  36294. chart.spacingBox = renderer.spacingBox = {
  36295. x: spacing[3],
  36296. y: spacing[0],
  36297. width: chartWidth - spacing[3] - spacing[1],
  36298. height: chartHeight - spacing[0] - spacing[2]
  36299. };
  36300. chart.plotBox = renderer.plotBox = {
  36301. x: plotLeft,
  36302. y: plotTop,
  36303. width: plotWidth,
  36304. height: plotHeight
  36305. };
  36306. const plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2), clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2), clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
  36307. chart.clipBox = {
  36308. x: clipX,
  36309. y: clipY,
  36310. width: Math.floor(chart.plotSizeX -
  36311. Math.max(plotBorderWidth, clipOffset[1]) / 2 -
  36312. clipX),
  36313. height: Math.max(0, Math.floor(chart.plotSizeY -
  36314. Math.max(plotBorderWidth, clipOffset[2]) / 2 -
  36315. clipY))
  36316. };
  36317. if (!skipAxes) {
  36318. chart.axes.forEach(function (axis) {
  36319. axis.setAxisSize();
  36320. axis.setAxisTranslation();
  36321. });
  36322. renderer.alignElements();
  36323. }
  36324. fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
  36325. }
  36326. /**
  36327. * Initial margins before auto size margins are applied.
  36328. *
  36329. * @private
  36330. * @function Highcharts.Chart#resetMargins
  36331. */
  36332. resetMargins() {
  36333. fireEvent(this, 'resetMargins');
  36334. const chart = this, chartOptions = chart.options.chart;
  36335. // Create margin and spacing array
  36336. ['margin', 'spacing'].forEach(function splashArrays(target) {
  36337. const value = chartOptions[target], values = isObject(value) ? value : [value, value, value, value];
  36338. [
  36339. 'Top',
  36340. 'Right',
  36341. 'Bottom',
  36342. 'Left'
  36343. ].forEach(function (sideName, side) {
  36344. chart[target][side] = pick(chartOptions[target + sideName], values[side]);
  36345. });
  36346. });
  36347. // Set margin names like chart.plotTop, chart.plotLeft,
  36348. // chart.marginRight, chart.marginBottom.
  36349. marginNames.forEach(function (m, side) {
  36350. chart[m] = pick(chart.margin[side], chart.spacing[side]);
  36351. });
  36352. chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
  36353. chart.clipOffset = [0, 0, 0, 0];
  36354. }
  36355. /**
  36356. * Internal function to draw or redraw the borders and backgrounds for chart
  36357. * and plot area.
  36358. *
  36359. * @private
  36360. * @function Highcharts.Chart#drawChartBox
  36361. * @emits Highcharts.Chart#event:afterDrawChartBox
  36362. */
  36363. drawChartBox() {
  36364. const chart = this, optionsChart = chart.options.chart, renderer = chart.renderer, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, styledMode = chart.styledMode, plotBGImage = chart.plotBGImage, chartBackgroundColor = optionsChart.backgroundColor, plotBackgroundColor = optionsChart.plotBackgroundColor, plotBackgroundImage = optionsChart.plotBackgroundImage, plotLeft = chart.plotLeft, plotTop = chart.plotTop, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotBox = chart.plotBox, clipRect = chart.clipRect, clipBox = chart.clipBox;
  36365. let chartBackground = chart.chartBackground, plotBackground = chart.plotBackground, plotBorder = chart.plotBorder, chartBorderWidth, mgn, bgAttr, verb = 'animate';
  36366. // Chart area
  36367. if (!chartBackground) {
  36368. chart.chartBackground = chartBackground = renderer.rect()
  36369. .addClass('highcharts-background')
  36370. .add();
  36371. verb = 'attr';
  36372. }
  36373. if (!styledMode) {
  36374. // Presentational
  36375. chartBorderWidth = optionsChart.borderWidth || 0;
  36376. mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
  36377. bgAttr = {
  36378. fill: chartBackgroundColor || 'none'
  36379. };
  36380. if (chartBorderWidth || chartBackground['stroke-width']) { // #980
  36381. bgAttr.stroke = optionsChart.borderColor;
  36382. bgAttr['stroke-width'] = chartBorderWidth;
  36383. }
  36384. chartBackground
  36385. .attr(bgAttr)
  36386. .shadow(optionsChart.shadow);
  36387. }
  36388. else {
  36389. chartBorderWidth = mgn = chartBackground.strokeWidth();
  36390. }
  36391. chartBackground[verb]({
  36392. x: mgn / 2,
  36393. y: mgn / 2,
  36394. width: chartWidth - mgn - chartBorderWidth % 2,
  36395. height: chartHeight - mgn - chartBorderWidth % 2,
  36396. r: optionsChart.borderRadius
  36397. });
  36398. // Plot background
  36399. verb = 'animate';
  36400. if (!plotBackground) {
  36401. verb = 'attr';
  36402. chart.plotBackground = plotBackground = renderer.rect()
  36403. .addClass('highcharts-plot-background')
  36404. .add();
  36405. }
  36406. plotBackground[verb](plotBox);
  36407. if (!styledMode) {
  36408. // Presentational attributes for the background
  36409. plotBackground
  36410. .attr({
  36411. fill: plotBackgroundColor || 'none'
  36412. })
  36413. .shadow(optionsChart.plotShadow);
  36414. // Create the background image
  36415. if (plotBackgroundImage) {
  36416. if (!plotBGImage) {
  36417. chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight).add();
  36418. }
  36419. else {
  36420. if (plotBackgroundImage !== plotBGImage.attr('href')) {
  36421. plotBGImage.attr('href', plotBackgroundImage);
  36422. }
  36423. plotBGImage.animate(plotBox);
  36424. }
  36425. }
  36426. }
  36427. // Plot clip
  36428. if (!clipRect) {
  36429. chart.clipRect = renderer.clipRect(clipBox);
  36430. }
  36431. else {
  36432. clipRect.animate({
  36433. width: clipBox.width,
  36434. height: clipBox.height
  36435. });
  36436. }
  36437. // Plot area border
  36438. verb = 'animate';
  36439. if (!plotBorder) {
  36440. verb = 'attr';
  36441. chart.plotBorder = plotBorder = renderer.rect()
  36442. .addClass('highcharts-plot-border')
  36443. .attr({
  36444. zIndex: 1 // Above the grid
  36445. })
  36446. .add();
  36447. }
  36448. if (!styledMode) {
  36449. // Presentational
  36450. plotBorder.attr({
  36451. stroke: optionsChart.plotBorderColor,
  36452. 'stroke-width': optionsChart.plotBorderWidth || 0,
  36453. fill: 'none'
  36454. });
  36455. }
  36456. plotBorder[verb](plotBorder.crisp({
  36457. x: plotLeft,
  36458. y: plotTop,
  36459. width: plotWidth,
  36460. height: plotHeight
  36461. }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
  36462. // reset
  36463. chart.isDirtyBox = false;
  36464. fireEvent(this, 'afterDrawChartBox');
  36465. }
  36466. /**
  36467. * Detect whether a certain chart property is needed based on inspecting its
  36468. * options and series. This mainly applies to the chart.inverted property,
  36469. * and in extensions to the chart.angular and chart.polar properties.
  36470. *
  36471. * @private
  36472. * @function Highcharts.Chart#propFromSeries
  36473. */
  36474. propFromSeries() {
  36475. const chart = this, optionsChart = chart.options.chart, seriesOptions = chart.options.series;
  36476. let i, klass, value;
  36477. /**
  36478. * The flag is set to `true` if a series of the chart is inverted.
  36479. *
  36480. * @name Highcharts.Chart#inverted
  36481. * @type {boolean|undefined}
  36482. */
  36483. ['inverted', 'angular', 'polar'].forEach(function (key) {
  36484. // The default series type's class
  36485. klass = seriesTypes[optionsChart.type];
  36486. // Get the value from available chart-wide properties
  36487. value =
  36488. // It is set in the options:
  36489. optionsChart[key] ||
  36490. // The default series class:
  36491. (klass && klass.prototype[key]);
  36492. // requires it
  36493. // 4. Check if any the chart's series require it
  36494. i = seriesOptions && seriesOptions.length;
  36495. while (!value && i--) {
  36496. klass = seriesTypes[seriesOptions[i].type];
  36497. if (klass && klass.prototype[key]) {
  36498. value = true;
  36499. }
  36500. }
  36501. // Set the chart property
  36502. chart[key] = value;
  36503. });
  36504. }
  36505. /**
  36506. * Internal function to link two or more series together, based on the
  36507. * `linkedTo` option. This is done from `Chart.render`, and after
  36508. * `Chart.addSeries` and `Series.remove`.
  36509. *
  36510. * @private
  36511. * @function Highcharts.Chart#linkSeries
  36512. * @emits Highcharts.Chart#event:afterLinkSeries
  36513. */
  36514. linkSeries(isUpdating) {
  36515. const chart = this, chartSeries = chart.series;
  36516. // Reset links
  36517. chartSeries.forEach(function (series) {
  36518. series.linkedSeries.length = 0;
  36519. });
  36520. // Apply new links
  36521. chartSeries.forEach(function (series) {
  36522. let linkedTo = series.options.linkedTo;
  36523. if (isString(linkedTo)) {
  36524. if (linkedTo === ':previous') {
  36525. linkedTo = chart.series[series.index - 1];
  36526. }
  36527. else {
  36528. linkedTo = chart.get(linkedTo);
  36529. }
  36530. // #3341 avoid mutual linking
  36531. if (linkedTo && linkedTo.linkedParent !== series) {
  36532. linkedTo.linkedSeries.push(series);
  36533. series.linkedParent = linkedTo;
  36534. if (linkedTo.enabledDataSorting) {
  36535. series.setDataSortingOptions();
  36536. }
  36537. series.visible = pick(series.options.visible, linkedTo.options.visible, series.visible); // #3879
  36538. }
  36539. }
  36540. });
  36541. fireEvent(this, 'afterLinkSeries', { isUpdating });
  36542. }
  36543. /**
  36544. * Render series for the chart.
  36545. *
  36546. * @private
  36547. * @function Highcharts.Chart#renderSeries
  36548. */
  36549. renderSeries() {
  36550. this.series.forEach(function (serie) {
  36551. serie.translate();
  36552. serie.render();
  36553. });
  36554. }
  36555. /**
  36556. * Render all graphics for the chart. Runs internally on initialization.
  36557. *
  36558. * @private
  36559. * @function Highcharts.Chart#render
  36560. */
  36561. render() {
  36562. const chart = this, axes = chart.axes, colorAxis = chart.colorAxis, renderer = chart.renderer, renderAxes = function (axes) {
  36563. axes.forEach(function (axis) {
  36564. if (axis.visible) {
  36565. axis.render();
  36566. }
  36567. });
  36568. };
  36569. let correction = 0; // correction for X axis labels
  36570. // Title
  36571. chart.setTitle();
  36572. // Fire an event before the margins are computed. This is where the
  36573. // legend is assigned.
  36574. fireEvent(chart, 'beforeMargins');
  36575. // Get stacks
  36576. if (chart.getStacks) {
  36577. chart.getStacks();
  36578. }
  36579. // Get chart margins
  36580. chart.getMargins(true);
  36581. chart.setChartSize();
  36582. // Record preliminary dimensions for later comparison
  36583. const tempWidth = chart.plotWidth;
  36584. axes.some(function (axis) {
  36585. if (axis.horiz &&
  36586. axis.visible &&
  36587. axis.options.labels.enabled &&
  36588. axis.series.length) {
  36589. // 21 is the most common correction for X axis labels
  36590. correction = 21;
  36591. return true;
  36592. }
  36593. });
  36594. // use Math.max to prevent negative plotHeight
  36595. chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
  36596. const tempHeight = chart.plotHeight;
  36597. // Get margins by pre-rendering axes
  36598. axes.forEach(function (axis) {
  36599. axis.setScale();
  36600. });
  36601. chart.getAxisMargins();
  36602. // If the plot area size has changed significantly, calculate tick
  36603. // positions again
  36604. const redoHorizontal = tempWidth / chart.plotWidth > 1.1;
  36605. // Height is more sensitive, use lower threshold
  36606. const redoVertical = tempHeight / chart.plotHeight > 1.05;
  36607. if (redoHorizontal || redoVertical) {
  36608. axes.forEach(function (axis) {
  36609. if ((axis.horiz && redoHorizontal) ||
  36610. (!axis.horiz && redoVertical)) {
  36611. // update to reflect the new margins
  36612. axis.setTickInterval(true);
  36613. }
  36614. });
  36615. chart.getMargins(); // second pass to check for new labels
  36616. }
  36617. // Draw the borders and backgrounds
  36618. chart.drawChartBox();
  36619. // Axes
  36620. if (chart.hasCartesianSeries) {
  36621. renderAxes(axes);
  36622. }
  36623. else if (colorAxis && colorAxis.length) {
  36624. renderAxes(colorAxis);
  36625. }
  36626. // The series
  36627. if (!chart.seriesGroup) {
  36628. chart.seriesGroup = renderer.g('series-group')
  36629. .attr({ zIndex: 3 })
  36630. .shadow(chart.options.chart.seriesGroupShadow)
  36631. .add();
  36632. }
  36633. chart.renderSeries();
  36634. // Credits
  36635. chart.addCredits();
  36636. // Handle responsiveness
  36637. if (chart.setResponsive) {
  36638. chart.setResponsive();
  36639. }
  36640. // Set flag
  36641. chart.hasRendered = true;
  36642. }
  36643. /**
  36644. * Set a new credits label for the chart.
  36645. *
  36646. * @sample highcharts/credits/credits-update/
  36647. * Add and update credits
  36648. *
  36649. * @function Highcharts.Chart#addCredits
  36650. *
  36651. * @param {Highcharts.CreditsOptions} [credits]
  36652. * A configuration object for the new credits.
  36653. */
  36654. addCredits(credits) {
  36655. const chart = this, creds = merge(true, this.options.credits, credits);
  36656. if (creds.enabled && !this.credits) {
  36657. /**
  36658. * The chart's credits label. The label has an `update` method that
  36659. * allows setting new options as per the
  36660. * [credits options set](https://api.highcharts.com/highcharts/credits).
  36661. *
  36662. * @name Highcharts.Chart#credits
  36663. * @type {Highcharts.SVGElement}
  36664. */
  36665. this.credits = this.renderer.text(creds.text + (this.mapCredits || ''), 0, 0)
  36666. .addClass('highcharts-credits')
  36667. .on('click', function () {
  36668. if (creds.href) {
  36669. win.location.href = creds.href;
  36670. }
  36671. })
  36672. .attr({
  36673. align: creds.position.align,
  36674. zIndex: 8
  36675. });
  36676. if (!chart.styledMode) {
  36677. this.credits.css(creds.style);
  36678. }
  36679. this.credits
  36680. .add()
  36681. .align(creds.position);
  36682. // Dynamically update
  36683. this.credits.update = function (options) {
  36684. chart.credits = chart.credits.destroy();
  36685. chart.addCredits(options);
  36686. };
  36687. }
  36688. }
  36689. /**
  36690. * Remove the chart and purge memory. This method is called internally
  36691. * before adding a second chart into the same container, as well as on
  36692. * window unload to prevent leaks.
  36693. *
  36694. * @sample highcharts/members/chart-destroy/
  36695. * Destroy the chart from a button
  36696. * @sample stock/members/chart-destroy/
  36697. * Destroy with Highcharts Stock
  36698. *
  36699. * @function Highcharts.Chart#destroy
  36700. *
  36701. * @emits Highcharts.Chart#event:destroy
  36702. */
  36703. destroy() {
  36704. const chart = this, axes = chart.axes, series = chart.series, container = chart.container, parentNode = container && container.parentNode;
  36705. let i;
  36706. // fire the chart.destoy event
  36707. fireEvent(chart, 'destroy');
  36708. // Delete the chart from charts lookup array
  36709. if (chart.renderer.forExport) {
  36710. erase(charts, chart); // #6569
  36711. }
  36712. else {
  36713. charts[chart.index] = void 0;
  36714. }
  36715. H.chartCount--;
  36716. chart.renderTo.removeAttribute('data-highcharts-chart');
  36717. // remove events
  36718. removeEvent(chart);
  36719. // ==== Destroy collections:
  36720. // Destroy axes
  36721. i = axes.length;
  36722. while (i--) {
  36723. axes[i] = axes[i].destroy();
  36724. }
  36725. // Destroy scroller & scroller series before destroying base series
  36726. if (this.scroller && this.scroller.destroy) {
  36727. this.scroller.destroy();
  36728. }
  36729. // Destroy each series
  36730. i = series.length;
  36731. while (i--) {
  36732. series[i] = series[i].destroy();
  36733. }
  36734. // ==== Destroy chart properties:
  36735. [
  36736. 'title', 'subtitle', 'chartBackground', 'plotBackground',
  36737. 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
  36738. 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
  36739. 'renderer'
  36740. ].forEach(function (name) {
  36741. const prop = chart[name];
  36742. if (prop && prop.destroy) {
  36743. chart[name] = prop.destroy();
  36744. }
  36745. });
  36746. // Remove container and all SVG, check container as it can break in IE
  36747. // when destroyed before finished loading
  36748. if (container) {
  36749. container.innerHTML = AST.emptyHTML;
  36750. removeEvent(container);
  36751. if (parentNode) {
  36752. discardElement(container);
  36753. }
  36754. }
  36755. // clean it all up
  36756. objectEach(chart, function (val, key) {
  36757. delete chart[key];
  36758. });
  36759. }
  36760. /**
  36761. * Prepare for first rendering after all data are loaded.
  36762. *
  36763. * @private
  36764. * @function Highcharts.Chart#firstRender
  36765. * @emits Highcharts.Chart#event:beforeRender
  36766. */
  36767. firstRender() {
  36768. const chart = this, options = chart.options;
  36769. // Create the container
  36770. chart.getContainer();
  36771. chart.resetMargins();
  36772. chart.setChartSize();
  36773. // Set the common chart properties (mainly invert) from the given series
  36774. chart.propFromSeries();
  36775. // get axes
  36776. chart.getAxes();
  36777. // Initialize the series
  36778. const series = isArray(options.series) ? options.series : [];
  36779. options.series = []; // Avoid mutation
  36780. series.forEach(
  36781. // #9680
  36782. function (serieOptions) {
  36783. chart.initSeries(serieOptions);
  36784. });
  36785. chart.linkSeries();
  36786. chart.setSeriesData();
  36787. // Run an event after axes and series are initialized, but before
  36788. // render. At this stage, the series data is indexed and cached in the
  36789. // xData and yData arrays, so we can access those before rendering. Used
  36790. // in Highcharts Stock.
  36791. fireEvent(chart, 'beforeRender');
  36792. chart.render();
  36793. chart.pointer.getChartPosition(); // #14973
  36794. // Fire the load event if there are no external images
  36795. if (!chart.renderer.imgCount && !chart.hasLoaded) {
  36796. chart.onload();
  36797. }
  36798. // If the chart was rendered outside the top container, put it back in
  36799. // (#3679)
  36800. chart.temporaryDisplay(true);
  36801. }
  36802. /**
  36803. * Internal function that runs on chart load, async if any images are loaded
  36804. * in the chart. Runs the callbacks and triggers the `load` and `render`
  36805. * events.
  36806. *
  36807. * @private
  36808. * @function Highcharts.Chart#onload
  36809. * @emits Highcharts.Chart#event:load
  36810. * @emits Highcharts.Chart#event:render
  36811. */
  36812. onload() {
  36813. // Run callbacks, first the ones registered by modules, then user's one
  36814. this.callbacks.concat([this.callback]).forEach(function (fn) {
  36815. // Chart destroyed in its own callback (#3600)
  36816. if (fn && typeof this.index !== 'undefined') {
  36817. fn.apply(this, [this]);
  36818. }
  36819. }, this);
  36820. fireEvent(this, 'load');
  36821. fireEvent(this, 'render');
  36822. // Set up auto resize, check for not destroyed (#6068)
  36823. if (defined(this.index)) {
  36824. this.setReflow();
  36825. }
  36826. this.warnIfA11yModuleNotLoaded();
  36827. // Don't run again
  36828. this.hasLoaded = true;
  36829. }
  36830. /**
  36831. * Emit console warning if the a11y module is not loaded.
  36832. */
  36833. warnIfA11yModuleNotLoaded() {
  36834. const { options, title } = this;
  36835. if (options && !this.accessibility) {
  36836. // Make chart behave as an image with the title as alt text
  36837. this.renderer.boxWrapper.attr({
  36838. role: 'img',
  36839. 'aria-label': ((title && title.element.textContent) || ''
  36840. // #17753, < is not allowed in SVG attributes
  36841. ).replace(/</g, '&lt;')
  36842. });
  36843. if (!(options.accessibility && options.accessibility.enabled === false)) {
  36844. error('Highcharts warning: Consider including the ' +
  36845. '"accessibility.js" module to make your chart more ' +
  36846. 'usable for people with disabilities. Set the ' +
  36847. '"accessibility.enabled" option to false to remove this ' +
  36848. 'warning. See https://www.highcharts.com/docs/accessibility/accessibility-module.', false, this);
  36849. }
  36850. }
  36851. }
  36852. /**
  36853. * Add a series to the chart after render time. Note that this method should
  36854. * never be used when adding data synchronously at chart render time, as it
  36855. * adds expense to the calculations and rendering. When adding data at the
  36856. * same time as the chart is initialized, add the series as a configuration
  36857. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  36858. *
  36859. * @sample highcharts/members/chart-addseries/
  36860. * Add a series from a button
  36861. * @sample stock/members/chart-addseries/
  36862. * Add a series in Highcharts Stock
  36863. *
  36864. * @function Highcharts.Chart#addSeries
  36865. *
  36866. * @param {Highcharts.SeriesOptionsType} options
  36867. * The config options for the series.
  36868. *
  36869. * @param {boolean} [redraw=true]
  36870. * Whether to redraw the chart after adding.
  36871. *
  36872. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36873. * Whether to apply animation, and optionally animation
  36874. * configuration. When `undefined`, it applies the animation that is
  36875. * set in the `chart.animation` option.
  36876. *
  36877. * @return {Highcharts.Series}
  36878. * The newly created series object.
  36879. *
  36880. * @emits Highcharts.Chart#event:addSeries
  36881. * @emits Highcharts.Chart#event:afterAddSeries
  36882. */
  36883. addSeries(options, redraw, animation) {
  36884. const chart = this;
  36885. let series;
  36886. if (options) { // <- not necessary
  36887. redraw = pick(redraw, true); // defaults to true
  36888. fireEvent(chart, 'addSeries', { options: options }, function () {
  36889. series = chart.initSeries(options);
  36890. chart.isDirtyLegend = true;
  36891. chart.linkSeries();
  36892. if (series.enabledDataSorting) {
  36893. // We need to call `setData` after `linkSeries`
  36894. series.setData(options.data, false);
  36895. }
  36896. fireEvent(chart, 'afterAddSeries', { series: series });
  36897. if (redraw) {
  36898. chart.redraw(animation);
  36899. }
  36900. });
  36901. }
  36902. return series;
  36903. }
  36904. /**
  36905. * Add an axis to the chart after render time. Note that this method should
  36906. * never be used when adding data synchronously at chart render time, as it
  36907. * adds expense to the calculations and rendering. When adding data at the
  36908. * same time as the chart is initialized, add the axis as a configuration
  36909. * option instead.
  36910. *
  36911. * @sample highcharts/members/chart-addaxis/
  36912. * Add and remove axes
  36913. *
  36914. * @function Highcharts.Chart#addAxis
  36915. *
  36916. * @param {Highcharts.AxisOptions} options
  36917. * The axis options.
  36918. *
  36919. * @param {boolean} [isX=false]
  36920. * Whether it is an X axis or a value axis.
  36921. *
  36922. * @param {boolean} [redraw=true]
  36923. * Whether to redraw the chart after adding.
  36924. *
  36925. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36926. * Whether and how to apply animation in the redraw. When
  36927. * `undefined`, it applies the animation that is set in the
  36928. * `chart.animation` option.
  36929. *
  36930. * @return {Highcharts.Axis}
  36931. * The newly generated Axis object.
  36932. */
  36933. addAxis(options, isX, redraw, animation) {
  36934. return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
  36935. }
  36936. /**
  36937. * Add a color axis to the chart after render time. Note that this method
  36938. * should never be used when adding data synchronously at chart render time,
  36939. * as it adds expense to the calculations and rendering. When adding data at
  36940. * the same time as the chart is initialized, add the axis as a
  36941. * configuration option instead.
  36942. *
  36943. * @sample highcharts/members/chart-addaxis/
  36944. * Add and remove axes
  36945. *
  36946. * @function Highcharts.Chart#addColorAxis
  36947. *
  36948. * @param {Highcharts.ColorAxisOptions} options
  36949. * The axis options.
  36950. *
  36951. * @param {boolean} [redraw=true]
  36952. * Whether to redraw the chart after adding.
  36953. *
  36954. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36955. * Whether and how to apply animation in the redraw. When
  36956. * `undefined`, it applies the animation that is set in the
  36957. * `chart.animation` option.
  36958. *
  36959. * @return {Highcharts.Axis}
  36960. * The newly generated Axis object.
  36961. */
  36962. addColorAxis(options, redraw, animation) {
  36963. return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
  36964. }
  36965. /**
  36966. * Factory for creating different axis types.
  36967. *
  36968. * @private
  36969. * @function Highcharts.Chart#createAxis
  36970. *
  36971. * @param {string} coll
  36972. * An axis type.
  36973. *
  36974. * @param {...Array<*>} arguments
  36975. * All arguments for the constructor.
  36976. *
  36977. * @return {Highcharts.Axis}
  36978. * The newly generated Axis object.
  36979. */
  36980. createAxis(coll, options) {
  36981. const axis = new Axis(this, options.axis, coll);
  36982. if (pick(options.redraw, true)) {
  36983. this.redraw(options.animation);
  36984. }
  36985. return axis;
  36986. }
  36987. /**
  36988. * Dim the chart and show a loading text or symbol. Options for the loading
  36989. * screen are defined in {@link
  36990. * https://api.highcharts.com/highcharts/loading|the loading options}.
  36991. *
  36992. * @sample highcharts/members/chart-hideloading/
  36993. * Show and hide loading from a button
  36994. * @sample highcharts/members/chart-showloading/
  36995. * Apply different text labels
  36996. * @sample stock/members/chart-show-hide-loading/
  36997. * Toggle loading in Highcharts Stock
  36998. *
  36999. * @function Highcharts.Chart#showLoading
  37000. *
  37001. * @param {string} [str]
  37002. * An optional text to show in the loading label instead of the
  37003. * default one. The default text is set in
  37004. * [lang.loading](https://api.highcharts.com/highcharts/lang.loading).
  37005. */
  37006. showLoading(str) {
  37007. const chart = this, options = chart.options, loadingOptions = options.loading, setLoadingSize = function () {
  37008. if (loadingDiv) {
  37009. css(loadingDiv, {
  37010. left: chart.plotLeft + 'px',
  37011. top: chart.plotTop + 'px',
  37012. width: chart.plotWidth + 'px',
  37013. height: chart.plotHeight + 'px'
  37014. });
  37015. }
  37016. };
  37017. let loadingDiv = chart.loadingDiv, loadingSpan = chart.loadingSpan;
  37018. // create the layer at the first call
  37019. if (!loadingDiv) {
  37020. chart.loadingDiv = loadingDiv = createElement('div', {
  37021. className: 'highcharts-loading highcharts-loading-hidden'
  37022. }, null, chart.container);
  37023. }
  37024. if (!loadingSpan) {
  37025. chart.loadingSpan = loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
  37026. addEvent(chart, 'redraw', setLoadingSize); // #1080
  37027. }
  37028. loadingDiv.className = 'highcharts-loading';
  37029. // Update text
  37030. AST.setElementHTML(loadingSpan, pick(str, options.lang.loading, ''));
  37031. if (!chart.styledMode) {
  37032. // Update visuals
  37033. css(loadingDiv, extend(loadingOptions.style, {
  37034. zIndex: 10
  37035. }));
  37036. css(loadingSpan, loadingOptions.labelStyle);
  37037. // Show it
  37038. if (!chart.loadingShown) {
  37039. css(loadingDiv, {
  37040. opacity: 0,
  37041. display: ''
  37042. });
  37043. animate(loadingDiv, {
  37044. opacity: loadingOptions.style.opacity || 0.5
  37045. }, {
  37046. duration: loadingOptions.showDuration || 0
  37047. });
  37048. }
  37049. }
  37050. chart.loadingShown = true;
  37051. setLoadingSize();
  37052. }
  37053. /**
  37054. * Hide the loading layer.
  37055. *
  37056. * @see Highcharts.Chart#showLoading
  37057. *
  37058. * @sample highcharts/members/chart-hideloading/
  37059. * Show and hide loading from a button
  37060. * @sample stock/members/chart-show-hide-loading/
  37061. * Toggle loading in Highcharts Stock
  37062. *
  37063. * @function Highcharts.Chart#hideLoading
  37064. */
  37065. hideLoading() {
  37066. const options = this.options, loadingDiv = this.loadingDiv;
  37067. if (loadingDiv) {
  37068. loadingDiv.className =
  37069. 'highcharts-loading highcharts-loading-hidden';
  37070. if (!this.styledMode) {
  37071. animate(loadingDiv, {
  37072. opacity: 0
  37073. }, {
  37074. duration: options.loading.hideDuration || 100,
  37075. complete: function () {
  37076. css(loadingDiv, { display: 'none' });
  37077. }
  37078. });
  37079. }
  37080. }
  37081. this.loadingShown = false;
  37082. }
  37083. /**
  37084. * A generic function to update any element of the chart. Elements can be
  37085. * enabled and disabled, moved, re-styled, re-formatted etc.
  37086. *
  37087. * A special case is configuration objects that take arrays, for example
  37088. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  37089. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  37090. * [series](https://api.highcharts.com/highcharts/series). For these
  37091. * collections, an `id` option is used to map the new option set to an
  37092. * existing object. If an existing object of the same id is not found, the
  37093. * corresponding item is updated. So for example, running `chart.update`
  37094. * with a series item without an id, will cause the existing chart's series
  37095. * with the same index in the series array to be updated. When the
  37096. * `oneToOne` parameter is true, `chart.update` will also take care of
  37097. * adding and removing items from the collection. Read more under the
  37098. * parameter description below.
  37099. *
  37100. * Note that when changing series data, `chart.update` may mutate the passed
  37101. * data options.
  37102. *
  37103. * See also the
  37104. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  37105. * Switching between `responsive.rules` basically runs `chart.update` under
  37106. * the hood.
  37107. *
  37108. * @sample highcharts/members/chart-update/
  37109. * Update chart geometry
  37110. *
  37111. * @function Highcharts.Chart#update
  37112. *
  37113. * @param {Highcharts.Options} options
  37114. * A configuration object for the new chart options.
  37115. *
  37116. * @param {boolean} [redraw=true]
  37117. * Whether to redraw the chart.
  37118. *
  37119. * @param {boolean} [oneToOne=false]
  37120. * When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
  37121. * collections will be updated one to one, and items will be either
  37122. * added or removed to match the new updated options. For example,
  37123. * if the chart has two series and we call `chart.update` with a
  37124. * configuration containing three series, one will be added. If we
  37125. * call `chart.update` with one series, one will be removed. Setting
  37126. * an empty `series` array will remove all series, but leaving out
  37127. * the`series` property will leave all series untouched. If the
  37128. * series have id's, the new series options will be matched by id,
  37129. * and the remaining ones removed.
  37130. *
  37131. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  37132. * Whether to apply animation, and optionally animation
  37133. * configuration. When `undefined`, it applies the animation that is
  37134. * set in the `chart.animation` option.
  37135. *
  37136. * @emits Highcharts.Chart#event:update
  37137. * @emits Highcharts.Chart#event:afterUpdate
  37138. */
  37139. update(options, redraw, oneToOne, animation) {
  37140. const chart = this, adders = {
  37141. credits: 'addCredits',
  37142. title: 'setTitle',
  37143. subtitle: 'setSubtitle',
  37144. caption: 'setCaption'
  37145. }, isResponsiveOptions = options.isResponsiveOptions, itemsForRemoval = [];
  37146. let updateAllAxes, updateAllSeries, runSetSize;
  37147. fireEvent(chart, 'update', { options: options });
  37148. // If there are responsive rules in action, undo the responsive rules
  37149. // before we apply the updated options and replay the responsive rules
  37150. // on top from the chart.redraw function (#9617).
  37151. if (!isResponsiveOptions) {
  37152. chart.setResponsive(false, true);
  37153. }
  37154. options = diffObjects(options, chart.options);
  37155. chart.userOptions = merge(chart.userOptions, options);
  37156. // If the top-level chart option is present, some special updates are
  37157. // required
  37158. const optionsChart = options.chart;
  37159. if (optionsChart) {
  37160. merge(true, chart.options.chart, optionsChart);
  37161. // Add support for deprecated zooming options like zoomType, #17861
  37162. this.setZoomOptions();
  37163. // Setter function
  37164. if ('className' in optionsChart) {
  37165. chart.setClassName(optionsChart.className);
  37166. }
  37167. if ('inverted' in optionsChart ||
  37168. 'polar' in optionsChart ||
  37169. 'type' in optionsChart) {
  37170. // Parse options.chart.inverted and options.chart.polar together
  37171. // with the available series.
  37172. chart.propFromSeries();
  37173. updateAllAxes = true;
  37174. }
  37175. if ('alignTicks' in optionsChart) { // #6452
  37176. updateAllAxes = true;
  37177. }
  37178. if ('events' in optionsChart) {
  37179. // Chart event handlers
  37180. registerEventOptions(this, optionsChart);
  37181. }
  37182. objectEach(optionsChart, function (val, key) {
  37183. if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  37184. -1) {
  37185. updateAllSeries = true;
  37186. }
  37187. // Only dirty box
  37188. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  37189. chart.isDirtyBox = true;
  37190. }
  37191. // Chart setSize
  37192. if (chart.propsRequireReflow.indexOf(key) !== -1) {
  37193. if (isResponsiveOptions) {
  37194. chart.isDirtyBox = true;
  37195. }
  37196. else {
  37197. runSetSize = true;
  37198. }
  37199. }
  37200. });
  37201. if (!chart.styledMode && optionsChart.style) {
  37202. chart.renderer.setStyle(chart.options.chart.style || {});
  37203. }
  37204. }
  37205. // Moved up, because tooltip needs updated plotOptions (#6218)
  37206. if (!chart.styledMode && options.colors) {
  37207. this.options.colors = options.colors;
  37208. }
  37209. if (options.time) {
  37210. // Maintaining legacy global time. If the chart is instanciated
  37211. // first with global time, then updated with time options, we need
  37212. // to create a new Time instance to avoid mutating the global time
  37213. // (#10536).
  37214. if (this.time === defaultTime) {
  37215. this.time = new Time(options.time);
  37216. }
  37217. // If we're updating, the time class is different from other chart
  37218. // classes (chart.legend, chart.tooltip etc) in that it doesn't know
  37219. // about the chart. The other chart[something].update functions also
  37220. // set the chart.options[something]. For the time class however we
  37221. // need to update the chart options separately. #14230.
  37222. merge(true, chart.options.time, options.time);
  37223. }
  37224. // Some option stuctures correspond one-to-one to chart objects that
  37225. // have update methods, for example
  37226. // options.credits => chart.credits
  37227. // options.legend => chart.legend
  37228. // options.title => chart.title
  37229. // options.tooltip => chart.tooltip
  37230. // options.subtitle => chart.subtitle
  37231. // options.mapNavigation => chart.mapNavigation
  37232. // options.navigator => chart.navigator
  37233. // options.scrollbar => chart.scrollbar
  37234. objectEach(options, function (val, key) {
  37235. if (chart[key] &&
  37236. typeof chart[key].update === 'function') {
  37237. chart[key].update(val, false);
  37238. // If a one-to-one object does not exist, look for an adder function
  37239. }
  37240. else if (typeof chart[adders[key]] === 'function') {
  37241. chart[adders[key]](val);
  37242. // Else, just merge the options. For nodes like loading, noData,
  37243. // plotOptions
  37244. }
  37245. else if (key !== 'colors' &&
  37246. chart.collectionsWithUpdate.indexOf(key) === -1) {
  37247. merge(true, chart.options[key], options[key]);
  37248. }
  37249. if (key !== 'chart' &&
  37250. chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
  37251. updateAllSeries = true;
  37252. }
  37253. });
  37254. // Setters for collections. For axes and series, each item is referred
  37255. // by an id. If the id is not found, it defaults to the corresponding
  37256. // item in the collection, so setting one series without an id, will
  37257. // update the first series in the chart. Setting two series without
  37258. // an id will update the first and the second respectively (#6019)
  37259. // chart.update and responsive.
  37260. this.collectionsWithUpdate.forEach(function (coll) {
  37261. if (options[coll]) {
  37262. splat(options[coll]).forEach(function (newOptions, i) {
  37263. const hasId = defined(newOptions.id);
  37264. let item;
  37265. // Match by id
  37266. if (hasId) {
  37267. item = chart.get(newOptions.id);
  37268. }
  37269. // No match by id found, match by index instead
  37270. if (!item && chart[coll]) {
  37271. item = chart[coll][pick(newOptions.index, i)];
  37272. // Check if we grabbed an item with an exising but
  37273. // different id (#13541). Check that the item in this
  37274. // position is not internal (navigator).
  37275. if (item && ((hasId && defined(item.options.id)) ||
  37276. item.options.isInternal)) {
  37277. item = void 0;
  37278. }
  37279. }
  37280. if (item && item.coll === coll) {
  37281. item.update(newOptions, false);
  37282. if (oneToOne) {
  37283. item.touched = true;
  37284. }
  37285. }
  37286. // If oneToOne and no matching item is found, add one
  37287. if (!item && oneToOne && chart.collectionsWithInit[coll]) {
  37288. chart.collectionsWithInit[coll][0].apply(chart,
  37289. // [newOptions, ...extraArguments, redraw=false]
  37290. [
  37291. newOptions
  37292. ].concat(
  37293. // Not all initializers require extra args
  37294. chart.collectionsWithInit[coll][1] || []).concat([
  37295. false
  37296. ])).touched = true;
  37297. }
  37298. });
  37299. // Add items for removal
  37300. if (oneToOne) {
  37301. chart[coll].forEach(function (item) {
  37302. if (!item.touched && !item.options.isInternal) {
  37303. itemsForRemoval.push(item);
  37304. }
  37305. else {
  37306. delete item.touched;
  37307. }
  37308. });
  37309. }
  37310. }
  37311. });
  37312. itemsForRemoval.forEach(function (item) {
  37313. if (item.chart && item.remove) { // #9097, avoid removing twice
  37314. item.remove(false);
  37315. }
  37316. });
  37317. if (updateAllAxes) {
  37318. chart.axes.forEach(function (axis) {
  37319. axis.update({}, false);
  37320. });
  37321. }
  37322. // Certain options require the whole series structure to be thrown away
  37323. // and rebuilt
  37324. if (updateAllSeries) {
  37325. chart.getSeriesOrderByLinks().forEach(function (series) {
  37326. // Avoid removed navigator series
  37327. if (series.chart) {
  37328. series.update({}, false);
  37329. }
  37330. }, this);
  37331. }
  37332. // Update size. Redraw is forced.
  37333. const newWidth = optionsChart && optionsChart.width;
  37334. const newHeight = optionsChart && (isString(optionsChart.height) ?
  37335. relativeLength(optionsChart.height, newWidth || chart.chartWidth) :
  37336. optionsChart.height);
  37337. if (
  37338. // In this case, run chart.setSize with newWidth and newHeight which
  37339. // are undefined, only for reflowing chart elements because margin
  37340. // or spacing has been set (#8190)
  37341. runSetSize ||
  37342. // In this case, the size is actually set
  37343. (isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  37344. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  37345. chart.setSize(newWidth, newHeight, animation);
  37346. }
  37347. else if (pick(redraw, true)) {
  37348. chart.redraw(animation);
  37349. }
  37350. fireEvent(chart, 'afterUpdate', {
  37351. options: options,
  37352. redraw: redraw,
  37353. animation: animation
  37354. });
  37355. }
  37356. /**
  37357. * Shortcut to set the subtitle options. This can also be done from {@link
  37358. * Chart#update} or {@link Chart#setTitle}.
  37359. *
  37360. * @function Highcharts.Chart#setSubtitle
  37361. *
  37362. * @param {Highcharts.SubtitleOptions} options
  37363. * New subtitle options. The subtitle text itself is set by the
  37364. * `options.text` property.
  37365. */
  37366. setSubtitle(options, redraw) {
  37367. this.applyDescription('subtitle', options);
  37368. this.layOutTitles(redraw);
  37369. }
  37370. /**
  37371. * Set the caption options. This can also be done from {@link
  37372. * Chart#update}.
  37373. *
  37374. * @function Highcharts.Chart#setCaption
  37375. *
  37376. * @param {Highcharts.CaptionOptions} options
  37377. * New caption options. The caption text itself is set by the
  37378. * `options.text` property.
  37379. */
  37380. setCaption(options, redraw) {
  37381. this.applyDescription('caption', options);
  37382. this.layOutTitles(redraw);
  37383. }
  37384. /**
  37385. * Display the zoom button, so users can reset zoom to the default view
  37386. * settings.
  37387. *
  37388. * @function Highcharts.Chart#showResetZoom
  37389. *
  37390. * @emits Highcharts.Chart#event:afterShowResetZoom
  37391. * @emits Highcharts.Chart#event:beforeShowResetZoom
  37392. */
  37393. showResetZoom() {
  37394. const chart = this, lang = defaultOptions.lang, btnOptions = chart.zooming.resetButton, theme = btnOptions.theme, alignTo = (btnOptions.relativeTo === 'chart' ||
  37395. btnOptions.relativeTo === 'spacingBox' ?
  37396. null :
  37397. 'scrollablePlotBox');
  37398. /**
  37399. * @private
  37400. */
  37401. function zoomOut() {
  37402. chart.zoomOut();
  37403. }
  37404. fireEvent(this, 'beforeShowResetZoom', null, function () {
  37405. chart.resetZoomButton = chart.renderer
  37406. .button(lang.resetZoom, null, null, zoomOut, theme)
  37407. .attr({
  37408. align: btnOptions.position.align,
  37409. title: lang.resetZoomTitle
  37410. })
  37411. .addClass('highcharts-reset-zoom')
  37412. .add()
  37413. .align(btnOptions.position, false, alignTo);
  37414. });
  37415. fireEvent(this, 'afterShowResetZoom');
  37416. }
  37417. /**
  37418. * Zoom the chart out after a user has zoomed in. See also
  37419. * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
  37420. *
  37421. * @function Highcharts.Chart#zoomOut
  37422. *
  37423. * @emits Highcharts.Chart#event:selection
  37424. */
  37425. zoomOut() {
  37426. fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
  37427. }
  37428. /**
  37429. * Zoom into a given portion of the chart given by axis coordinates.
  37430. *
  37431. * @private
  37432. * @function Highcharts.Chart#zoom
  37433. * @param {Highcharts.SelectEventObject} event
  37434. */
  37435. zoom(event) {
  37436. const chart = this, pointer = chart.pointer;
  37437. let displayButton = false, hasZoomed;
  37438. // If zoom is called with no arguments, reset the axes
  37439. if (!event || event.resetSelection) {
  37440. chart.axes.forEach(function (axis) {
  37441. hasZoomed = axis.zoom();
  37442. });
  37443. pointer.initiated = false; // #6804
  37444. }
  37445. else { // else, zoom in on all axes
  37446. event.xAxis.concat(event.yAxis).forEach(function (axisData) {
  37447. const axis = axisData.axis, isXAxis = axis.isXAxis;
  37448. // don't zoom more than minRange
  37449. if (pointer[isXAxis ? 'zoomX' : 'zoomY'] &&
  37450. (defined(pointer.mouseDownX) &&
  37451. defined(pointer.mouseDownY) &&
  37452. chart.isInsidePlot(pointer.mouseDownX - chart.plotLeft, pointer.mouseDownY - chart.plotTop, { axis })) || !defined(chart.inverted ? pointer.mouseDownX : pointer.mouseDownY)) {
  37453. hasZoomed = axis.zoom(axisData.min, axisData.max);
  37454. if (axis.displayBtn) {
  37455. displayButton = true;
  37456. }
  37457. }
  37458. });
  37459. }
  37460. // Show or hide the Reset zoom button
  37461. const resetZoomButton = chart.resetZoomButton;
  37462. if (displayButton && !resetZoomButton) {
  37463. chart.showResetZoom();
  37464. }
  37465. else if (!displayButton && isObject(resetZoomButton)) {
  37466. chart.resetZoomButton = resetZoomButton.destroy();
  37467. }
  37468. // Redraw
  37469. if (hasZoomed) {
  37470. chart.redraw(pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100));
  37471. }
  37472. }
  37473. /**
  37474. * Pan the chart by dragging the mouse across the pane. This function is
  37475. * called on mouse move, and the distance to pan is computed from chartX
  37476. * compared to the first chartX position in the dragging operation.
  37477. *
  37478. * @private
  37479. * @function Highcharts.Chart#pan
  37480. * @param {Highcharts.PointerEventObject} e
  37481. * @param {string} panning
  37482. */
  37483. pan(e, panning) {
  37484. const chart = this, hoverPoints = chart.hoverPoints, panningOptions = (typeof panning === 'object' ?
  37485. panning :
  37486. {
  37487. enabled: panning,
  37488. type: 'x'
  37489. }), chartOptions = chart.options.chart;
  37490. if (chartOptions && chartOptions.panning) {
  37491. chartOptions.panning = panningOptions;
  37492. }
  37493. const type = panningOptions.type;
  37494. let doRedraw;
  37495. fireEvent(this, 'pan', { originalEvent: e }, function () {
  37496. // remove active points for shared tooltip
  37497. if (hoverPoints) {
  37498. hoverPoints.forEach(function (point) {
  37499. point.setState();
  37500. });
  37501. }
  37502. let axes = chart.xAxis;
  37503. if (type === 'xy') {
  37504. axes = axes.concat(chart.yAxis);
  37505. }
  37506. else if (type === 'y') {
  37507. axes = chart.yAxis;
  37508. }
  37509. const nextMousePos = {};
  37510. axes.forEach(function (axis) {
  37511. if (!axis.options.panningEnabled || axis.options.isInternal) {
  37512. return;
  37513. }
  37514. const horiz = axis.horiz, mousePos = e[horiz ? 'chartX' : 'chartY'], mouseDown = horiz ? 'mouseDownX' : 'mouseDownY', startPos = chart[mouseDown], halfPointRange = axis.minPointOffset || 0, pointRangeDirection = (axis.reversed && !chart.inverted) ||
  37515. (!axis.reversed && chart.inverted) ?
  37516. -1 :
  37517. 1, extremes = axis.getExtremes(), panMin = axis.toValue(startPos - mousePos, true) +
  37518. halfPointRange * pointRangeDirection, panMax = axis.toValue(startPos + axis.len - mousePos, true) -
  37519. ((halfPointRange * pointRangeDirection) ||
  37520. (axis.isXAxis && axis.pointRangePadding) ||
  37521. 0), flipped = panMax < panMin, hasVerticalPanning = axis.hasVerticalPanning();
  37522. let newMin = flipped ? panMax : panMin, newMax = flipped ? panMin : panMax, panningState = axis.panningState, spill;
  37523. // General calculations of panning state.
  37524. // This is related to using vertical panning. (#11315).
  37525. if (hasVerticalPanning &&
  37526. !axis.isXAxis && (!panningState || panningState.isDirty)) {
  37527. axis.series.forEach(function (series) {
  37528. const processedData = series.getProcessedData(true), dataExtremes = series.getExtremes(processedData.yData, true);
  37529. if (!panningState) {
  37530. panningState = {
  37531. startMin: Number.MAX_VALUE,
  37532. startMax: -Number.MAX_VALUE
  37533. };
  37534. }
  37535. if (isNumber(dataExtremes.dataMin) &&
  37536. isNumber(dataExtremes.dataMax)) {
  37537. panningState.startMin = Math.min(pick(series.options.threshold, Infinity), dataExtremes.dataMin, panningState.startMin);
  37538. panningState.startMax = Math.max(pick(series.options.threshold, -Infinity), dataExtremes.dataMax, panningState.startMax);
  37539. }
  37540. });
  37541. }
  37542. const paddedMin = Math.min(pick(panningState && panningState.startMin, extremes.dataMin), halfPointRange ?
  37543. extremes.min :
  37544. axis.toValue(axis.toPixels(extremes.min) -
  37545. axis.minPixelPadding));
  37546. const paddedMax = Math.max(pick(panningState && panningState.startMax, extremes.dataMax), halfPointRange ?
  37547. extremes.max :
  37548. axis.toValue(axis.toPixels(extremes.max) +
  37549. axis.minPixelPadding));
  37550. axis.panningState = panningState;
  37551. // It is not necessary to calculate extremes on ordinal axis,
  37552. // because they are already calculated, so we don't want to
  37553. // override them.
  37554. if (!axis.isOrdinal) {
  37555. // If the new range spills over, either to the min or max,
  37556. // adjust the new range.
  37557. spill = paddedMin - newMin;
  37558. if (spill > 0) {
  37559. newMax += spill;
  37560. newMin = paddedMin;
  37561. }
  37562. spill = newMax - paddedMax;
  37563. if (spill > 0) {
  37564. newMax = paddedMax;
  37565. newMin -= spill;
  37566. }
  37567. // Set new extremes if they are actually new
  37568. if (axis.series.length &&
  37569. newMin !== extremes.min &&
  37570. newMax !== extremes.max &&
  37571. newMin >= paddedMin &&
  37572. newMax <= paddedMax) {
  37573. axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' });
  37574. if (!chart.resetZoomButton &&
  37575. // Show reset zoom button only when both newMin and
  37576. // newMax values are between padded axis range.
  37577. newMin !== paddedMin &&
  37578. newMax !== paddedMax &&
  37579. type.match('y')) {
  37580. chart.showResetZoom();
  37581. axis.displayBtn = false;
  37582. }
  37583. doRedraw = true;
  37584. }
  37585. // set new reference for next run:
  37586. nextMousePos[mouseDown] = mousePos;
  37587. }
  37588. });
  37589. objectEach(nextMousePos, (pos, down) => {
  37590. chart[down] = pos;
  37591. });
  37592. if (doRedraw) {
  37593. chart.redraw(false);
  37594. }
  37595. css(chart.container, { cursor: 'move' });
  37596. });
  37597. }
  37598. }
  37599. extend(Chart.prototype, {
  37600. // Hook for adding callbacks in modules
  37601. callbacks: [],
  37602. /**
  37603. * These collections (arrays) implement `Chart.addSomethig` method used in
  37604. * chart.update() to create new object in the collection. Equivalent for
  37605. * deleting is resolved by simple `Somethig.remove()`.
  37606. *
  37607. * Note: We need to define these references after initializers are bound to
  37608. * chart's prototype.
  37609. *
  37610. * @private
  37611. */
  37612. collectionsWithInit: {
  37613. // collectionName: [ initializingMethod, [extraArguments] ]
  37614. xAxis: [Chart.prototype.addAxis, [true]],
  37615. yAxis: [Chart.prototype.addAxis, [false]],
  37616. series: [Chart.prototype.addSeries]
  37617. },
  37618. /**
  37619. * These collections (arrays) implement update() methods with support for
  37620. * one-to-one option.
  37621. * @private
  37622. */
  37623. collectionsWithUpdate: [
  37624. 'xAxis',
  37625. 'yAxis',
  37626. 'series'
  37627. ],
  37628. /**
  37629. * These properties cause isDirtyBox to be set to true when updating. Can be
  37630. * extended from plugins.
  37631. * @private
  37632. */
  37633. propsRequireDirtyBox: [
  37634. 'backgroundColor',
  37635. 'borderColor',
  37636. 'borderWidth',
  37637. 'borderRadius',
  37638. 'plotBackgroundColor',
  37639. 'plotBackgroundImage',
  37640. 'plotBorderColor',
  37641. 'plotBorderWidth',
  37642. 'plotShadow',
  37643. 'shadow'
  37644. ],
  37645. /**
  37646. * These properties require a full reflow of chart elements, best
  37647. * implemented through running `Chart.setSize` internally (#8190).
  37648. * @private
  37649. */
  37650. propsRequireReflow: [
  37651. 'margin',
  37652. 'marginTop',
  37653. 'marginRight',
  37654. 'marginBottom',
  37655. 'marginLeft',
  37656. 'spacing',
  37657. 'spacingTop',
  37658. 'spacingRight',
  37659. 'spacingBottom',
  37660. 'spacingLeft'
  37661. ],
  37662. /**
  37663. * These properties cause all series to be updated when updating. Can be
  37664. * extended from plugins.
  37665. * @private
  37666. */
  37667. propsRequireUpdateSeries: [
  37668. 'chart.inverted',
  37669. 'chart.polar',
  37670. 'chart.ignoreHiddenSeries',
  37671. 'chart.type',
  37672. 'colors',
  37673. 'plotOptions',
  37674. 'time',
  37675. 'tooltip'
  37676. ]
  37677. });
  37678. /* *
  37679. *
  37680. * Default Export
  37681. *
  37682. * */
  37683. /* *
  37684. *
  37685. * API Declarations
  37686. *
  37687. * */
  37688. /**
  37689. * Callback for chart constructors.
  37690. *
  37691. * @callback Highcharts.ChartCallbackFunction
  37692. *
  37693. * @param {Highcharts.Chart} chart
  37694. * Created chart.
  37695. */
  37696. /**
  37697. * Format a number and return a string based on input settings.
  37698. *
  37699. * @callback Highcharts.NumberFormatterCallbackFunction
  37700. *
  37701. * @param {number} number
  37702. * The input number to format.
  37703. *
  37704. * @param {number} decimals
  37705. * The amount of decimals. A value of -1 preserves the amount in the
  37706. * input number.
  37707. *
  37708. * @param {string} [decimalPoint]
  37709. * The decimal point, defaults to the one given in the lang options, or
  37710. * a dot.
  37711. *
  37712. * @param {string} [thousandsSep]
  37713. * The thousands separator, defaults to the one given in the lang
  37714. * options, or a space character.
  37715. *
  37716. * @return {string} The formatted number.
  37717. */
  37718. /**
  37719. * The chart title. The title has an `update` method that allows modifying the
  37720. * options directly or indirectly via `chart.update`.
  37721. *
  37722. * @interface Highcharts.TitleObject
  37723. * @extends Highcharts.SVGElement
  37724. */ /**
  37725. * Modify options for the title.
  37726. *
  37727. * @function Highcharts.TitleObject#update
  37728. *
  37729. * @param {Highcharts.TitleOptions} titleOptions
  37730. * Options to modify.
  37731. *
  37732. * @param {boolean} [redraw=true]
  37733. * Whether to redraw the chart after the title is altered. If doing more
  37734. * operations on the chart, it is a good idea to set redraw to false and
  37735. * call {@link Chart#redraw} after.
  37736. */
  37737. /**
  37738. * The chart subtitle. The subtitle has an `update` method that
  37739. * allows modifying the options directly or indirectly via
  37740. * `chart.update`.
  37741. *
  37742. * @interface Highcharts.SubtitleObject
  37743. * @extends Highcharts.SVGElement
  37744. */ /**
  37745. * Modify options for the subtitle.
  37746. *
  37747. * @function Highcharts.SubtitleObject#update
  37748. *
  37749. * @param {Highcharts.SubtitleOptions} subtitleOptions
  37750. * Options to modify.
  37751. *
  37752. * @param {boolean} [redraw=true]
  37753. * Whether to redraw the chart after the subtitle is altered. If doing
  37754. * more operations on the chart, it is a good idea to set redraw to false
  37755. * and call {@link Chart#redraw} after.
  37756. */
  37757. /**
  37758. * The chart caption. The caption has an `update` method that
  37759. * allows modifying the options directly or indirectly via
  37760. * `chart.update`.
  37761. *
  37762. * @interface Highcharts.CaptionObject
  37763. * @extends Highcharts.SVGElement
  37764. */ /**
  37765. * Modify options for the caption.
  37766. *
  37767. * @function Highcharts.CaptionObject#update
  37768. *
  37769. * @param {Highcharts.CaptionOptions} captionOptions
  37770. * Options to modify.
  37771. *
  37772. * @param {boolean} [redraw=true]
  37773. * Whether to redraw the chart after the caption is altered. If doing
  37774. * more operations on the chart, it is a good idea to set redraw to false
  37775. * and call {@link Chart#redraw} after.
  37776. */
  37777. /**
  37778. * @interface Highcharts.ChartIsInsideOptionsObject
  37779. */ /**
  37780. * @name Highcharts.ChartIsInsideOptionsObject#axis
  37781. * @type {Highcharts.Axis|undefined}
  37782. */ /**
  37783. * @name Highcharts.ChartIsInsideOptionsObject#ignoreX
  37784. * @type {boolean|undefined}
  37785. */ /**
  37786. * @name Highcharts.ChartIsInsideOptionsObject#ignoreY
  37787. * @type {boolean|undefined}
  37788. */ /**
  37789. * @name Highcharts.ChartIsInsideOptionsObject#inverted
  37790. * @type {boolean|undefined}
  37791. */ /**
  37792. * @name Highcharts.ChartIsInsideOptionsObject#paneCoordinates
  37793. * @type {boolean|undefined}
  37794. */ /**
  37795. * @name Highcharts.ChartIsInsideOptionsObject#series
  37796. * @type {Highcharts.Series|undefined}
  37797. */ /**
  37798. * @name Highcharts.ChartIsInsideOptionsObject#visiblePlotOnly
  37799. * @type {boolean|undefined}
  37800. */
  37801. ''; // keeps doclets above in JS file
  37802. return Chart;
  37803. });
  37804. _registerModule(_modules, 'Extensions/ScrollablePlotArea.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Series/Series.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Utilities.js']], function (A, Axis, Chart, Series, RendererRegistry, U) {
  37805. /* *
  37806. *
  37807. * (c) 2010-2021 Torstein Honsi
  37808. *
  37809. * License: www.highcharts.com/license
  37810. *
  37811. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37812. *
  37813. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  37814. * horizontally on mobile devices. Supports left and right side axes.
  37815. */
  37816. /*
  37817. WIP on vertical scrollable plot area (#9378). To do:
  37818. - Bottom axis positioning
  37819. - Test with Gantt
  37820. - Look for size optimizing the code
  37821. - API and demos
  37822. */
  37823. const { stop } = A;
  37824. const { addEvent, createElement, defined, merge, pick } = U;
  37825. /* eslint-disable no-invalid-this, valid-jsdoc */
  37826. addEvent(Chart, 'afterSetChartSize', function (e) {
  37827. let scrollablePlotArea = this.options.chart.scrollablePlotArea, scrollableMinWidth = scrollablePlotArea && scrollablePlotArea.minWidth, scrollableMinHeight = scrollablePlotArea && scrollablePlotArea.minHeight, scrollablePixelsX, scrollablePixelsY, corrections;
  37828. if (!this.renderer.forExport) {
  37829. // The amount of pixels to scroll, the difference between chart
  37830. // width and scrollable width
  37831. if (scrollableMinWidth) {
  37832. this.scrollablePixelsX = scrollablePixelsX = Math.max(0, scrollableMinWidth - this.chartWidth);
  37833. if (scrollablePixelsX) {
  37834. this.scrollablePlotBox = (this.renderer.scrollablePlotBox = merge(this.plotBox));
  37835. this.plotBox.width = this.plotWidth += scrollablePixelsX;
  37836. if (this.inverted) {
  37837. this.clipBox.height += scrollablePixelsX;
  37838. }
  37839. else {
  37840. this.clipBox.width += scrollablePixelsX;
  37841. }
  37842. corrections = {
  37843. // Corrections for right side
  37844. 1: { name: 'right', value: scrollablePixelsX }
  37845. };
  37846. }
  37847. // Currently we can only do either X or Y
  37848. }
  37849. else if (scrollableMinHeight) {
  37850. this.scrollablePixelsY = scrollablePixelsY = Math.max(0, scrollableMinHeight - this.chartHeight);
  37851. if (defined(scrollablePixelsY)) {
  37852. this.scrollablePlotBox = (this.renderer.scrollablePlotBox = merge(this.plotBox));
  37853. this.plotBox.height = this.plotHeight += scrollablePixelsY;
  37854. if (this.inverted) {
  37855. this.clipBox.width += scrollablePixelsY;
  37856. }
  37857. else {
  37858. this.clipBox.height += scrollablePixelsY;
  37859. }
  37860. corrections = {
  37861. 2: { name: 'bottom', value: scrollablePixelsY }
  37862. };
  37863. }
  37864. }
  37865. if (corrections && !e.skipAxes) {
  37866. this.axes.forEach(function (axis) {
  37867. // For right and bottom axes, only fix the plot line length
  37868. if (corrections[axis.side]) {
  37869. // Get the plot lines right in getPlotLinePath,
  37870. // temporarily set it to the adjusted plot width.
  37871. axis.getPlotLinePath = function () {
  37872. let marginName = corrections[axis.side].name, correctionValue = corrections[axis.side].value,
  37873. // axis.right or axis.bottom
  37874. margin = this[marginName], path;
  37875. // Temporarily adjust
  37876. this[marginName] = margin - correctionValue;
  37877. path = Axis.prototype.getPlotLinePath.apply(this, arguments);
  37878. // Reset
  37879. this[marginName] = margin;
  37880. return path;
  37881. };
  37882. }
  37883. else {
  37884. // Apply the corrected plotWidth
  37885. axis.setAxisSize();
  37886. axis.setAxisTranslation();
  37887. }
  37888. });
  37889. }
  37890. }
  37891. });
  37892. addEvent(Chart, 'render', function () {
  37893. if (this.scrollablePixelsX || this.scrollablePixelsY) {
  37894. if (this.setUpScrolling) {
  37895. this.setUpScrolling();
  37896. }
  37897. this.applyFixed();
  37898. }
  37899. else if (this.fixedDiv) { // Has been in scrollable mode
  37900. this.applyFixed();
  37901. }
  37902. });
  37903. /**
  37904. * @private
  37905. * @function Highcharts.Chart#setUpScrolling
  37906. * @return {void}
  37907. */
  37908. Chart.prototype.setUpScrolling = function () {
  37909. const css = {
  37910. WebkitOverflowScrolling: 'touch',
  37911. overflowX: 'hidden',
  37912. overflowY: 'hidden'
  37913. };
  37914. if (this.scrollablePixelsX) {
  37915. css.overflowX = 'auto';
  37916. }
  37917. if (this.scrollablePixelsY) {
  37918. css.overflowY = 'auto';
  37919. }
  37920. // Insert a container with position relative
  37921. // that scrolling and fixed container renders to (#10555)
  37922. this.scrollingParent = createElement('div', {
  37923. className: 'highcharts-scrolling-parent'
  37924. }, {
  37925. position: 'relative'
  37926. }, this.renderTo);
  37927. // Add the necessary divs to provide scrolling
  37928. this.scrollingContainer = createElement('div', {
  37929. 'className': 'highcharts-scrolling'
  37930. }, css, this.scrollingParent);
  37931. // On scroll, reset the chart position because it applies to the scrolled
  37932. // container
  37933. let lastHoverPoint;
  37934. addEvent(this.scrollingContainer, 'scroll', () => {
  37935. if (this.pointer) {
  37936. delete this.pointer.chartPosition;
  37937. if (this.hoverPoint) {
  37938. lastHoverPoint = this.hoverPoint;
  37939. }
  37940. this.pointer.runPointActions(void 0, lastHoverPoint, true);
  37941. }
  37942. });
  37943. this.innerContainer = createElement('div', {
  37944. 'className': 'highcharts-inner-container'
  37945. }, null, this.scrollingContainer);
  37946. // Now move the container inside
  37947. this.innerContainer.appendChild(this.container);
  37948. // Don't run again
  37949. this.setUpScrolling = null;
  37950. };
  37951. /**
  37952. * These elements are moved over to the fixed renderer and stay fixed when the
  37953. * user scrolls the chart
  37954. * @private
  37955. */
  37956. Chart.prototype.moveFixedElements = function () {
  37957. let container = this.container, fixedRenderer = this.fixedRenderer, fixedSelectors = [
  37958. '.highcharts-breadcrumbs-group',
  37959. '.highcharts-contextbutton',
  37960. '.highcharts-credits',
  37961. '.highcharts-legend',
  37962. '.highcharts-legend-checkbox',
  37963. '.highcharts-navigator-series',
  37964. '.highcharts-navigator-xaxis',
  37965. '.highcharts-navigator-yaxis',
  37966. '.highcharts-navigator',
  37967. '.highcharts-reset-zoom',
  37968. '.highcharts-drillup-button',
  37969. '.highcharts-scrollbar',
  37970. '.highcharts-subtitle',
  37971. '.highcharts-title'
  37972. ], axisClass;
  37973. if (this.scrollablePixelsX && !this.inverted) {
  37974. axisClass = '.highcharts-yaxis';
  37975. }
  37976. else if (this.scrollablePixelsX && this.inverted) {
  37977. axisClass = '.highcharts-xaxis';
  37978. }
  37979. else if (this.scrollablePixelsY && !this.inverted) {
  37980. axisClass = '.highcharts-xaxis';
  37981. }
  37982. else if (this.scrollablePixelsY && this.inverted) {
  37983. axisClass = '.highcharts-yaxis';
  37984. }
  37985. if (axisClass) {
  37986. fixedSelectors.push(`${axisClass}:not(.highcharts-radial-axis)`, `${axisClass}-labels:not(.highcharts-radial-axis-labels)`);
  37987. }
  37988. fixedSelectors.forEach(function (className) {
  37989. [].forEach.call(container.querySelectorAll(className), function (elem) {
  37990. (elem.namespaceURI === fixedRenderer.SVG_NS ?
  37991. fixedRenderer.box :
  37992. fixedRenderer.box.parentNode).appendChild(elem);
  37993. elem.style.pointerEvents = 'auto';
  37994. });
  37995. });
  37996. };
  37997. /**
  37998. * @private
  37999. * @function Highcharts.Chart#applyFixed
  38000. * @return {void}
  38001. */
  38002. Chart.prototype.applyFixed = function () {
  38003. const firstTime = !this.fixedDiv, chartOptions = this.options.chart, scrollableOptions = chartOptions.scrollablePlotArea, Renderer = RendererRegistry.getRendererType();
  38004. let fixedRenderer, scrollableWidth, scrollableHeight;
  38005. // First render
  38006. if (firstTime) {
  38007. this.fixedDiv = createElement('div', {
  38008. className: 'highcharts-fixed'
  38009. }, {
  38010. position: 'absolute',
  38011. overflow: 'hidden',
  38012. pointerEvents: 'none',
  38013. zIndex: (chartOptions.style && chartOptions.style.zIndex || 0) + 2,
  38014. top: 0
  38015. }, null, true);
  38016. if (this.scrollingContainer) {
  38017. this.scrollingContainer.parentNode.insertBefore(this.fixedDiv, this.scrollingContainer);
  38018. }
  38019. this.renderTo.style.overflow = 'visible';
  38020. this.fixedRenderer = fixedRenderer = new Renderer(this.fixedDiv, this.chartWidth, this.chartHeight, this.options.chart.style);
  38021. // Mask
  38022. this.scrollableMask = fixedRenderer
  38023. .path()
  38024. .attr({
  38025. fill: this.options.chart.backgroundColor || '#fff',
  38026. 'fill-opacity': pick(scrollableOptions.opacity, 0.85),
  38027. zIndex: -1
  38028. })
  38029. .addClass('highcharts-scrollable-mask')
  38030. .add();
  38031. addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
  38032. addEvent(this, 'afterApplyDrilldown', this.moveFixedElements);
  38033. addEvent(this, 'afterLayOutTitles', this.moveFixedElements);
  38034. }
  38035. else {
  38036. // Set the size of the fixed renderer to the visible width
  38037. this.fixedRenderer.setSize(this.chartWidth, this.chartHeight);
  38038. }
  38039. if (this.scrollableDirty || firstTime) {
  38040. this.scrollableDirty = false;
  38041. this.moveFixedElements();
  38042. }
  38043. // Increase the size of the scrollable renderer and background
  38044. scrollableWidth = this.chartWidth + (this.scrollablePixelsX || 0);
  38045. scrollableHeight = this.chartHeight + (this.scrollablePixelsY || 0);
  38046. stop(this.container);
  38047. this.container.style.width = scrollableWidth + 'px';
  38048. this.container.style.height = scrollableHeight + 'px';
  38049. this.renderer.boxWrapper.attr({
  38050. width: scrollableWidth,
  38051. height: scrollableHeight,
  38052. viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
  38053. });
  38054. this.chartBackground.attr({
  38055. width: scrollableWidth,
  38056. height: scrollableHeight
  38057. });
  38058. this.scrollingContainer.style.height = this.chartHeight + 'px';
  38059. // Set scroll position
  38060. if (firstTime) {
  38061. if (scrollableOptions.scrollPositionX) {
  38062. this.scrollingContainer.scrollLeft =
  38063. this.scrollablePixelsX *
  38064. scrollableOptions.scrollPositionX;
  38065. }
  38066. if (scrollableOptions.scrollPositionY) {
  38067. this.scrollingContainer.scrollTop =
  38068. this.scrollablePixelsY *
  38069. scrollableOptions.scrollPositionY;
  38070. }
  38071. }
  38072. // Mask behind the left and right side
  38073. let axisOffset = this.axisOffset, maskTop = this.plotTop - axisOffset[0] - 1, maskLeft = this.plotLeft - axisOffset[3] - 1, maskBottom = this.plotTop + this.plotHeight + axisOffset[2] + 1, maskRight = this.plotLeft + this.plotWidth + axisOffset[1] + 1, maskPlotRight = this.plotLeft + this.plotWidth -
  38074. (this.scrollablePixelsX || 0), maskPlotBottom = this.plotTop + this.plotHeight -
  38075. (this.scrollablePixelsY || 0), d;
  38076. if (this.scrollablePixelsX) {
  38077. d = [
  38078. // Left side
  38079. ['M', 0, maskTop],
  38080. ['L', this.plotLeft - 1, maskTop],
  38081. ['L', this.plotLeft - 1, maskBottom],
  38082. ['L', 0, maskBottom],
  38083. ['Z'],
  38084. // Right side
  38085. ['M', maskPlotRight, maskTop],
  38086. ['L', this.chartWidth, maskTop],
  38087. ['L', this.chartWidth, maskBottom],
  38088. ['L', maskPlotRight, maskBottom],
  38089. ['Z']
  38090. ];
  38091. }
  38092. else if (this.scrollablePixelsY) {
  38093. d = [
  38094. // Top side
  38095. ['M', maskLeft, 0],
  38096. ['L', maskLeft, this.plotTop - 1],
  38097. ['L', maskRight, this.plotTop - 1],
  38098. ['L', maskRight, 0],
  38099. ['Z'],
  38100. // Bottom side
  38101. ['M', maskLeft, maskPlotBottom],
  38102. ['L', maskLeft, this.chartHeight],
  38103. ['L', maskRight, this.chartHeight],
  38104. ['L', maskRight, maskPlotBottom],
  38105. ['Z']
  38106. ];
  38107. }
  38108. else {
  38109. d = [['M', 0, 0]];
  38110. }
  38111. if (this.redrawTrigger !== 'adjustHeight') {
  38112. this.scrollableMask.attr({ d });
  38113. }
  38114. };
  38115. addEvent(Axis, 'afterInit', function () {
  38116. this.chart.scrollableDirty = true;
  38117. });
  38118. addEvent(Series, 'show', function () {
  38119. this.chart.scrollableDirty = true;
  38120. });
  38121. /* *
  38122. *
  38123. * API Declarations
  38124. *
  38125. * */
  38126. /**
  38127. * Options for a scrollable plot area. This feature provides a minimum size for
  38128. * the plot area of the chart. If the size gets smaller than this, typically
  38129. * on mobile devices, a native browser scrollbar is presented. This scrollbar
  38130. * provides smooth scrolling for the contents of the plot area, whereas the
  38131. * title, legend and unaffected axes are fixed.
  38132. *
  38133. * Since v7.1.2, a scrollable plot area can be defined for either horizontal or
  38134. * vertical scrolling, depending on whether the `minWidth` or `minHeight`
  38135. * option is set.
  38136. *
  38137. * @sample highcharts/chart/scrollable-plotarea
  38138. * Scrollable plot area
  38139. * @sample highcharts/chart/scrollable-plotarea-vertical
  38140. * Vertically scrollable plot area
  38141. * @sample {gantt} gantt/chart/scrollable-plotarea-vertical
  38142. * Gantt chart with vertically scrollable plot area
  38143. *
  38144. * @since 6.1.0
  38145. * @product highcharts gantt
  38146. * @apioption chart.scrollablePlotArea
  38147. */
  38148. /**
  38149. * The minimum height for the plot area. If it gets smaller than this, the plot
  38150. * area will become scrollable.
  38151. *
  38152. * @type {number}
  38153. * @since 7.1.2
  38154. * @apioption chart.scrollablePlotArea.minHeight
  38155. */
  38156. /**
  38157. * The minimum width for the plot area. If it gets smaller than this, the plot
  38158. * area will become scrollable.
  38159. *
  38160. * @type {number}
  38161. * @since 6.1.0
  38162. * @apioption chart.scrollablePlotArea.minWidth
  38163. */
  38164. /**
  38165. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  38166. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  38167. * Typically we would use 1 if the chart has right aligned Y axes.
  38168. *
  38169. * @type {number}
  38170. * @since 6.1.0
  38171. * @apioption chart.scrollablePlotArea.scrollPositionX
  38172. */
  38173. /**
  38174. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  38175. * 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
  38176. *
  38177. * @type {number}
  38178. * @since 7.1.2
  38179. * @apioption chart.scrollablePlotArea.scrollPositionY
  38180. */
  38181. /**
  38182. * The opacity of mask applied on one of the sides of the plot
  38183. * area.
  38184. *
  38185. * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
  38186. * Disabled opacity for the mask
  38187. *
  38188. * @type {number}
  38189. * @default 0.85
  38190. * @since 7.1.1
  38191. * @apioption chart.scrollablePlotArea.opacity
  38192. */
  38193. (''); // keep doclets above in transpiled file
  38194. });
  38195. _registerModule(_modules, 'Core/Axis/Stacking/StackItem.js', [_modules['Core/Templating.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (T, SeriesRegistry, U) {
  38196. /* *
  38197. *
  38198. * (c) 2010-2021 Torstein Honsi
  38199. *
  38200. * License: www.highcharts.com/license
  38201. *
  38202. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38203. *
  38204. * */
  38205. const { format } = T;
  38206. const { series: Series } = SeriesRegistry;
  38207. const { destroyObjectProperties, fireEvent, isNumber, merge, pick } = U;
  38208. /* *
  38209. *
  38210. * Class
  38211. *
  38212. * */
  38213. /**
  38214. * The class for stacks. Each stack, on a specific X value and either negative
  38215. * or positive, has its own stack item.
  38216. * @private
  38217. */
  38218. class StackItem {
  38219. /* *
  38220. *
  38221. * Constructor
  38222. *
  38223. * */
  38224. constructor(axis, options, negativeValue, x, stackOption) {
  38225. const inverted = axis.chart.inverted, reversed = axis.reversed;
  38226. this.axis = axis;
  38227. // The stack goes to the left either if the stack has negative value
  38228. // or when axis is reversed. XOR operator.
  38229. const isNegative = (this.isNegative = !!negativeValue !== !!reversed);
  38230. // Save the options to be able to style the label
  38231. this.options = options = options || {};
  38232. // Save the x value to be able to position the label later
  38233. this.x = x;
  38234. // Initialize total value
  38235. this.total = null;
  38236. this.cumulative = null;
  38237. // This will keep each points' extremes stored by series.index and point
  38238. // index
  38239. this.points = {};
  38240. this.hasValidPoints = false;
  38241. // Save the stack option on the series configuration object,
  38242. // and whether to treat it as percent
  38243. this.stack = stackOption;
  38244. this.leftCliff = 0;
  38245. this.rightCliff = 0;
  38246. // The align options and text align varies on whether the stack is
  38247. // negative and if the chart is inverted or not.
  38248. // First test the user supplied value, then use the dynamic.
  38249. this.alignOptions = {
  38250. align: options.align ||
  38251. (inverted ? (isNegative ? 'left' : 'right') : 'center'),
  38252. verticalAlign: options.verticalAlign ||
  38253. (inverted ? 'middle' : isNegative ? 'bottom' : 'top'),
  38254. y: options.y,
  38255. x: options.x
  38256. };
  38257. this.textAlign =
  38258. options.textAlign ||
  38259. (inverted ? (!isNegative ? 'left' : 'right') : 'center');
  38260. }
  38261. /**
  38262. * @private
  38263. */
  38264. destroy() {
  38265. destroyObjectProperties(this, this.axis);
  38266. }
  38267. /**
  38268. * Renders the stack total label and adds it to the stack label group.
  38269. * @private
  38270. */
  38271. render(group) {
  38272. const chart = this.axis.chart, options = this.options, formatOption = options.format,
  38273. // Format the text in the label.
  38274. str = formatOption ?
  38275. format(formatOption, this, chart) :
  38276. options.formatter.call(this);
  38277. // Change the text to reflect the new total and set visibility to hidden
  38278. // in case the serie is hidden
  38279. if (this.label) {
  38280. this.label.attr({ text: str, visibility: 'hidden' });
  38281. }
  38282. else {
  38283. // Create new label
  38284. this.label = chart.renderer.label(str, null, void 0, options.shape, void 0, void 0, options.useHTML, false, 'stack-labels');
  38285. const attr = {
  38286. r: options.borderRadius || 0,
  38287. text: str,
  38288. // set default padding to 5 as it is in datalabels #12308
  38289. padding: pick(options.padding, 5),
  38290. visibility: 'hidden' // hidden until setOffset is called
  38291. };
  38292. if (!chart.styledMode) {
  38293. attr.fill = options.backgroundColor;
  38294. attr.stroke = options.borderColor;
  38295. attr['stroke-width'] = options.borderWidth;
  38296. this.label.css(options.style || {});
  38297. }
  38298. this.label.attr(attr);
  38299. if (!this.label.added) {
  38300. this.label.add(group); // add to the labels-group
  38301. }
  38302. }
  38303. // Rank it higher than data labels (#8742)
  38304. this.label.labelrank = chart.plotSizeY;
  38305. fireEvent(this, 'afterRender');
  38306. }
  38307. /**
  38308. * Sets the offset that the stack has from the x value and repositions the
  38309. * label.
  38310. * @private
  38311. */
  38312. setOffset(xOffset, width, boxBottom, boxTop, defaultX, xAxis) {
  38313. const { alignOptions, axis, label, options, textAlign } = this, chart = axis.chart, stackBox = this.getStackBox({
  38314. xOffset,
  38315. width,
  38316. boxBottom,
  38317. boxTop,
  38318. defaultX,
  38319. xAxis
  38320. }), { verticalAlign } = alignOptions;
  38321. if (label && stackBox) {
  38322. const labelBox = label.getBBox(), padding = label.padding;
  38323. let isJustify = pick(options.overflow, 'justify') === 'justify', visible;
  38324. // Reset alignOptions property after justify #12337
  38325. alignOptions.x = options.x || 0;
  38326. alignOptions.y = options.y || 0;
  38327. // Calculate the adjusted Stack position, to take into consideration
  38328. // The size if the labelBox and vertical alignment as
  38329. // well as the text alignment. It's need to be done to work with
  38330. // default SVGLabel.align/justify methods.
  38331. const { x, y } = this.adjustStackPosition({
  38332. labelBox,
  38333. verticalAlign,
  38334. textAlign
  38335. });
  38336. stackBox.x -= x;
  38337. stackBox.y -= y;
  38338. // Align the label to the adjusted box.
  38339. label.align(alignOptions, false, stackBox);
  38340. // Check if label is inside the plotArea #12294
  38341. visible = chart.isInsidePlot(label.alignAttr.x + alignOptions.x + x, label.alignAttr.y + alignOptions.y + y);
  38342. if (!visible) {
  38343. isJustify = false;
  38344. }
  38345. if (isJustify) {
  38346. // Justify stackLabel into the alignBox
  38347. Series.prototype.justifyDataLabel.call(axis, label, alignOptions, label.alignAttr, labelBox, stackBox);
  38348. }
  38349. // Add attr to aviod the default animation of justifyDataLabel.
  38350. // Also add correct rotation with its rotation origin. #15129
  38351. label.attr({
  38352. x: label.alignAttr.x,
  38353. y: label.alignAttr.y,
  38354. rotation: options.rotation,
  38355. rotationOriginX: labelBox.width / 2,
  38356. rotationOriginY: labelBox.height / 2
  38357. });
  38358. // Check if the dataLabel should be visible.
  38359. if (pick(!isJustify && options.crop, true)) {
  38360. visible =
  38361. isNumber(label.x) &&
  38362. isNumber(label.y) &&
  38363. chart.isInsidePlot(label.x - padding + label.width, label.y) &&
  38364. chart.isInsidePlot(label.x + padding, label.y);
  38365. }
  38366. label[visible ? 'show' : 'hide']();
  38367. }
  38368. fireEvent(this, 'afterSetOffset', { xOffset, width });
  38369. }
  38370. /**
  38371. * Adjust the stack BBox position, to take into consideration the alignment
  38372. * of the dataLabel. This is necessary to make the stackDataLabel work with
  38373. * core methods like `SVGLabel.adjust` and `Series.justifyDataLabel`.
  38374. * @param AdjustStackPositionProps
  38375. * @return {{x: number, y: number}} Adjusted BBox position of the stack.
  38376. */
  38377. adjustStackPosition({ labelBox, verticalAlign, textAlign }) {
  38378. const factorMap = {
  38379. bottom: 0,
  38380. middle: 1,
  38381. top: 2,
  38382. right: 1,
  38383. center: 0,
  38384. left: -1
  38385. }, verticalAlignFactor = factorMap[verticalAlign], textAlignFactor = factorMap[textAlign];
  38386. return {
  38387. x: labelBox.width / 2 + (labelBox.width / 2) * textAlignFactor,
  38388. y: (labelBox.height / 2) * verticalAlignFactor
  38389. };
  38390. }
  38391. /**
  38392. * Get the bbox of the stack.
  38393. * @private
  38394. * @function Highcharts.StackItem#getStackBox
  38395. * @return {BBoxObject} The x, y, height, width of the stack.
  38396. */
  38397. getStackBox(stackBoxProps) {
  38398. const stackItem = this, axis = this.axis, chart = axis.chart, { boxTop, defaultX, xOffset, width, boxBottom } = stackBoxProps, totalStackValue = axis.stacking.usePercentage ?
  38399. 100 :
  38400. pick(boxTop, this.total, 0), y = axis.toPixels(totalStackValue), xAxis = stackBoxProps.xAxis || chart.xAxis[0], x = pick(defaultX, xAxis.translate(this.x)) + xOffset, yZero = axis.toPixels(boxBottom ||
  38401. (isNumber(axis.min) &&
  38402. axis.logarithmic &&
  38403. axis.logarithmic.lin2log(axis.min)) ||
  38404. 0), height = Math.abs(y - yZero), inverted = chart.inverted, neg = stackItem.isNegative;
  38405. return inverted ?
  38406. {
  38407. x: (neg ? y : y - height) - chart.plotLeft,
  38408. y: xAxis.height - x - width,
  38409. width: height,
  38410. height: width
  38411. } : {
  38412. x: x + xAxis.transB - chart.plotLeft,
  38413. y: (neg ? y - height : y) - chart.plotTop,
  38414. width: width,
  38415. height: height
  38416. };
  38417. }
  38418. }
  38419. /* *
  38420. *
  38421. * Default Export
  38422. *
  38423. * */
  38424. /* *
  38425. *
  38426. * API Declarations
  38427. *
  38428. * */
  38429. /**
  38430. * Stack of data points
  38431. *
  38432. * @product highcharts
  38433. *
  38434. * @interface Highcharts.StackItemObject
  38435. */ /**
  38436. * Alignment settings
  38437. * @name Highcharts.StackItemObject#alignOptions
  38438. * @type {Highcharts.AlignObject}
  38439. */ /**
  38440. * Related axis
  38441. * @name Highcharts.StackItemObject#axis
  38442. * @type {Highcharts.Axis}
  38443. */ /**
  38444. * Cumulative value of the stacked data points
  38445. * @name Highcharts.StackItemObject#cumulative
  38446. * @type {number}
  38447. */ /**
  38448. * True if on the negative side
  38449. * @name Highcharts.StackItemObject#isNegative
  38450. * @type {boolean}
  38451. */ /**
  38452. * Related SVG element
  38453. * @name Highcharts.StackItemObject#label
  38454. * @type {Highcharts.SVGElement}
  38455. */ /**
  38456. * Related stack options
  38457. * @name Highcharts.StackItemObject#options
  38458. * @type {Highcharts.YAxisStackLabelsOptions}
  38459. */ /**
  38460. * Total value of the stacked data points
  38461. * @name Highcharts.StackItemObject#total
  38462. * @type {number}
  38463. */ /**
  38464. * Shared x value of the stack
  38465. * @name Highcharts.StackItemObject#x
  38466. * @type {number}
  38467. */
  38468. ''; // keeps doclets above in JS file
  38469. return StackItem;
  38470. });
  38471. _registerModule(_modules, 'Core/Axis/Stacking/StackingAxis.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Axis/Stacking/StackItem.js'], _modules['Core/Utilities.js']], function (A, Axis, SeriesRegistry, StackItem, U) {
  38472. /* *
  38473. *
  38474. * (c) 2010-2021 Torstein Honsi
  38475. *
  38476. * License: www.highcharts.com/license
  38477. *
  38478. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38479. *
  38480. * */
  38481. const { getDeferredAnimation } = A;
  38482. const { series: { prototype: seriesProto } } = SeriesRegistry;
  38483. const { addEvent, correctFloat, defined, destroyObjectProperties, fireEvent, isArray, isNumber, objectEach, pick } = U;
  38484. /* *
  38485. *
  38486. * Functions
  38487. *
  38488. * */
  38489. /**
  38490. * Generate stacks for each series and calculate stacks total values
  38491. *
  38492. * @private
  38493. * @function Highcharts.Chart#getStacks
  38494. */
  38495. function chartGetStacks() {
  38496. const chart = this, inverted = chart.inverted;
  38497. // reset stacks for each yAxis
  38498. chart.yAxis.forEach((axis) => {
  38499. if (axis.stacking && axis.stacking.stacks && axis.hasVisibleSeries) {
  38500. axis.stacking.oldStacks = axis.stacking.stacks;
  38501. }
  38502. });
  38503. chart.series.forEach((series) => {
  38504. const xAxisOptions = series.xAxis && series.xAxis.options || {};
  38505. if (series.options.stacking &&
  38506. (series.visible === true ||
  38507. chart.options.chart.ignoreHiddenSeries === false)) {
  38508. series.stackKey = [
  38509. series.type,
  38510. pick(series.options.stack, ''),
  38511. inverted ? xAxisOptions.top : xAxisOptions.left,
  38512. inverted ? xAxisOptions.height : xAxisOptions.width
  38513. ].join(',');
  38514. }
  38515. });
  38516. }
  38517. /**
  38518. * @private
  38519. */
  38520. function onAxisDestroy() {
  38521. const stacking = this.stacking;
  38522. if (!stacking) {
  38523. return;
  38524. }
  38525. const stacks = stacking.stacks;
  38526. // Destroy each stack total
  38527. objectEach(stacks, function (stack, stackKey) {
  38528. destroyObjectProperties(stack);
  38529. stacks[stackKey] = null;
  38530. });
  38531. if (stacking &&
  38532. stacking.stackTotalGroup) {
  38533. stacking.stackTotalGroup.destroy();
  38534. }
  38535. }
  38536. /**
  38537. * @private
  38538. */
  38539. function onAxisInit() {
  38540. if (this.coll === 'yAxis' && !this.stacking) {
  38541. this.stacking = new AxisAdditions(this);
  38542. }
  38543. }
  38544. /**
  38545. * Get stack indicator, according to it's x-value, to determine points with the
  38546. * same x-value
  38547. *
  38548. * @private
  38549. * @function Highcharts.Series#getStackIndicator
  38550. */
  38551. function seriesGetStackIndicator(stackIndicator, x, index, key) {
  38552. // Update stack indicator, when:
  38553. // first point in a stack || x changed || stack type (negative vs positive)
  38554. // changed:
  38555. if (!defined(stackIndicator) ||
  38556. stackIndicator.x !== x ||
  38557. (key && stackIndicator.stackKey !== key)) {
  38558. stackIndicator = {
  38559. x: x,
  38560. index: 0,
  38561. key: key,
  38562. stackKey: key
  38563. };
  38564. }
  38565. else {
  38566. (stackIndicator).index++;
  38567. }
  38568. stackIndicator.key =
  38569. [index, x, stackIndicator.index].join(',');
  38570. return stackIndicator;
  38571. }
  38572. /**
  38573. * Iterate over all stacks and compute the absolute values to percent
  38574. *
  38575. * @private
  38576. * @function Highcharts.Series#modifyStacks
  38577. */
  38578. function seriesModifyStacks() {
  38579. const series = this, yAxis = series.yAxis, stackKey = series.stackKey, stacks = yAxis.stacking.stacks, processedXData = series.processedXData, stacking = series.options.stacking, stacker = series[stacking + 'Stacker'];
  38580. let stackIndicator;
  38581. if (stacker) { // Modifier function exists (Series.percentStacker etc.)
  38582. [stackKey, '-' + stackKey].forEach((key) => {
  38583. let i = processedXData.length, x, stack, pointExtremes;
  38584. while (i--) {
  38585. x = processedXData[i];
  38586. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index, key);
  38587. stack = stacks[key] && stacks[key][x];
  38588. pointExtremes =
  38589. stack && stack.points[stackIndicator.key];
  38590. if (pointExtremes) {
  38591. stacker.call(series, pointExtremes, stack, i);
  38592. }
  38593. }
  38594. });
  38595. }
  38596. }
  38597. /**
  38598. * Modifier function for percent stacks. Blows up the stack to 100%.
  38599. *
  38600. * @private
  38601. * @function Highcharts.Series#percentStacker
  38602. */
  38603. function seriesPercentStacker(pointExtremes, stack, i) {
  38604. const totalFactor = stack.total ? 100 / stack.total : 0;
  38605. // Y bottom value
  38606. pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
  38607. // Y value
  38608. pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
  38609. this.stackedYData[i] = pointExtremes[1];
  38610. }
  38611. /**
  38612. * Set grouped points in a stack-like object. When `centerInCategory` is true,
  38613. * and `stacking` is not enabled, we need a pseudo (horizontal) stack in order
  38614. * to handle grouping of points within the same category.
  38615. *
  38616. * @private
  38617. * @function Highcharts.Series#setStackedPoints
  38618. * @return {void}
  38619. */
  38620. function seriesSetGroupedPoints() {
  38621. const stacking = this.yAxis.stacking;
  38622. if (this.options.centerInCategory &&
  38623. (this.is('column') || this.is('columnrange')) &&
  38624. // With stacking enabled, we already have stacks that we can compute
  38625. // from
  38626. !this.options.stacking &&
  38627. // With only one series, we don't need to consider centerInCategory
  38628. this.chart.series.length > 1) {
  38629. seriesProto.setStackedPoints.call(this, 'group');
  38630. // After updating, if we now have proper stacks, we must delete the group
  38631. // pseudo stacks (#14986)
  38632. }
  38633. else if (stacking) {
  38634. objectEach(stacking.stacks, (type, key) => {
  38635. if (key.slice(-5) === 'group') {
  38636. objectEach(type, (stack) => stack.destroy());
  38637. delete stacking.stacks[key];
  38638. }
  38639. });
  38640. }
  38641. }
  38642. /**
  38643. * Adds series' points value to corresponding stack
  38644. *
  38645. * @private
  38646. * @function Highcharts.Series#setStackedPoints
  38647. */
  38648. function seriesSetStackedPoints(stackingParam) {
  38649. const chart = this.chart, stacking = stackingParam || this.options.stacking;
  38650. if (!stacking || (this.visible !== true &&
  38651. chart.options.chart.ignoreHiddenSeries !== false)) {
  38652. return;
  38653. }
  38654. const series = this, xData = series.processedXData, yData = series.processedYData, stackedYData = [], yDataLength = yData.length, seriesOptions = series.options, threshold = seriesOptions.threshold, stackThreshold = pick(seriesOptions.startFromThreshold && threshold, 0), stackOption = seriesOptions.stack, stackKey = stackingParam ? `${series.type},${stacking}` : series.stackKey, negKey = '-' + stackKey, negStacks = series.negStacks, yAxis = stacking === 'group' ?
  38655. chart.yAxis[0] :
  38656. series.yAxis, stacks = yAxis.stacking.stacks, oldStacks = yAxis.stacking.oldStacks;
  38657. let stackIndicator, isNegative, stack, other, key, pointKey, i, x, y;
  38658. yAxis.stacking.stacksTouched += 1;
  38659. // loop over the non-null y values and read them into a local array
  38660. for (i = 0; i < yDataLength; i++) {
  38661. x = xData[i];
  38662. y = yData[i];
  38663. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
  38664. pointKey = stackIndicator.key;
  38665. // Read stacked values into a stack based on the x value,
  38666. // the sign of y and the stack key. Stacking is also handled for null
  38667. // values (#739)
  38668. isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
  38669. key = isNegative ? negKey : stackKey;
  38670. // Create empty object for this stack if it doesn't exist yet
  38671. if (!stacks[key]) {
  38672. stacks[key] = {};
  38673. }
  38674. // Initialize StackItem for this x
  38675. if (!stacks[key][x]) {
  38676. if (oldStacks[key] &&
  38677. oldStacks[key][x]) {
  38678. stacks[key][x] = oldStacks[key][x];
  38679. stacks[key][x].total = null;
  38680. }
  38681. else {
  38682. stacks[key][x] = new StackItem(yAxis, yAxis.options.stackLabels, !!isNegative, x, stackOption);
  38683. }
  38684. }
  38685. // If the StackItem doesn't exist, create it first
  38686. stack = stacks[key][x];
  38687. if (y !== null) {
  38688. stack.points[pointKey] = stack.points[series.index] =
  38689. [pick(stack.cumulative, stackThreshold)];
  38690. // Record the base of the stack
  38691. if (!defined(stack.cumulative)) {
  38692. stack.base = pointKey;
  38693. }
  38694. stack.touched = yAxis.stacking.stacksTouched;
  38695. // In area charts, if there are multiple points on the same X value,
  38696. // let the area fill the full span of those points
  38697. if (stackIndicator.index > 0 && series.singleStacks === false) {
  38698. stack.points[pointKey][0] =
  38699. stack.points[series.index + ',' + x + ',0'][0];
  38700. }
  38701. // When updating to null, reset the point stack (#7493)
  38702. }
  38703. else {
  38704. stack.points[pointKey] = stack.points[series.index] =
  38705. null;
  38706. }
  38707. // Add value to the stack total
  38708. if (stacking === 'percent') {
  38709. // Percent stacked column, totals are the same for the positive and
  38710. // negative stacks
  38711. other = isNegative ? stackKey : negKey;
  38712. if (negStacks && stacks[other] && stacks[other][x]) {
  38713. other = stacks[other][x];
  38714. stack.total = other.total =
  38715. Math.max(other.total, stack.total) +
  38716. Math.abs(y) ||
  38717. 0;
  38718. // Percent stacked areas
  38719. }
  38720. else {
  38721. stack.total =
  38722. correctFloat(stack.total + (Math.abs(y) || 0));
  38723. }
  38724. }
  38725. else if (stacking === 'group') {
  38726. if (isArray(y)) {
  38727. y = y[0];
  38728. }
  38729. // In this stack, the total is the number of valid points
  38730. if (y !== null) {
  38731. stack.total = (stack.total || 0) + 1;
  38732. }
  38733. }
  38734. else {
  38735. stack.total = correctFloat(stack.total + (y || 0));
  38736. }
  38737. if (stacking === 'group') {
  38738. // This point's index within the stack, pushed to stack.points[1]
  38739. stack.cumulative = (stack.total || 1) - 1;
  38740. }
  38741. else {
  38742. stack.cumulative = correctFloat(pick(stack.cumulative, stackThreshold) +
  38743. (y || 0));
  38744. }
  38745. if (y !== null) {
  38746. stack.points[pointKey].push(stack.cumulative);
  38747. stackedYData[i] = stack.cumulative;
  38748. stack.hasValidPoints = true;
  38749. }
  38750. }
  38751. if (stacking === 'percent') {
  38752. yAxis.stacking.usePercentage = true;
  38753. }
  38754. if (stacking !== 'group') {
  38755. this.stackedYData = stackedYData; // To be used in getExtremes
  38756. }
  38757. // Reset old stacks
  38758. yAxis.stacking.oldStacks = {};
  38759. }
  38760. /* *
  38761. *
  38762. * Classes
  38763. *
  38764. * */
  38765. /**
  38766. * Adds stacking support to axes.
  38767. * @private
  38768. * @class
  38769. */
  38770. class AxisAdditions {
  38771. /* *
  38772. *
  38773. * Constructors
  38774. *
  38775. * */
  38776. constructor(axis) {
  38777. this.oldStacks = {};
  38778. this.stacks = {};
  38779. this.stacksTouched = 0;
  38780. this.axis = axis;
  38781. }
  38782. /* *
  38783. *
  38784. * Functions
  38785. *
  38786. * */
  38787. /**
  38788. * Build the stacks from top down
  38789. * @private
  38790. */
  38791. buildStacks() {
  38792. const stacking = this;
  38793. const axis = stacking.axis;
  38794. const axisSeries = axis.series;
  38795. const reversedStacks = axis.options.reversedStacks;
  38796. const len = axisSeries.length;
  38797. let actualSeries, i;
  38798. stacking.usePercentage = false;
  38799. i = len;
  38800. while (i--) {
  38801. actualSeries = axisSeries[reversedStacks ? i : len - i - 1];
  38802. actualSeries.setStackedPoints();
  38803. actualSeries.setGroupedPoints();
  38804. }
  38805. // Loop up again to compute percent and stream stack
  38806. for (i = 0; i < len; i++) {
  38807. axisSeries[i].modifyStacks();
  38808. }
  38809. fireEvent(axis, 'afterBuildStacks');
  38810. }
  38811. /**
  38812. * @private
  38813. */
  38814. cleanStacks() {
  38815. const stacking = this;
  38816. let stacks;
  38817. if (stacking.oldStacks) {
  38818. stacks = stacking.stacks = stacking.oldStacks;
  38819. }
  38820. // reset stacks
  38821. objectEach(stacks, function (type) {
  38822. objectEach(type, function (stack) {
  38823. stack.cumulative = stack.total;
  38824. });
  38825. });
  38826. }
  38827. /**
  38828. * Set all the stacks to initial states and destroy unused ones.
  38829. * @private
  38830. */
  38831. resetStacks() {
  38832. objectEach(this.stacks, (type) => {
  38833. objectEach(type, (stack, x) => {
  38834. // Clean up memory after point deletion (#1044, #4320)
  38835. if (isNumber(stack.touched) &&
  38836. stack.touched < this.stacksTouched) {
  38837. stack.destroy();
  38838. delete type[x];
  38839. // Reset stacks
  38840. }
  38841. else {
  38842. stack.total = null;
  38843. stack.cumulative = null;
  38844. }
  38845. });
  38846. });
  38847. }
  38848. /**
  38849. * @private
  38850. */
  38851. renderStackTotals() {
  38852. const stacking = this, axis = stacking.axis, chart = axis.chart, renderer = chart.renderer, stacks = stacking.stacks, stackLabelsAnim = axis.options.stackLabels &&
  38853. axis.options.stackLabels.animation, animationConfig = getDeferredAnimation(chart, stackLabelsAnim || false), stackTotalGroup = stacking.stackTotalGroup = (stacking.stackTotalGroup ||
  38854. renderer
  38855. .g('stack-labels')
  38856. .attr({
  38857. zIndex: 6,
  38858. opacity: 0
  38859. })
  38860. .add());
  38861. // plotLeft/Top will change when y axis gets wider so we need to
  38862. // translate the stackTotalGroup at every render call. See bug #506
  38863. // and #516
  38864. stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
  38865. // Render each stack total
  38866. objectEach(stacks, function (type) {
  38867. objectEach(type, function (stack) {
  38868. stack.render(stackTotalGroup);
  38869. });
  38870. });
  38871. stackTotalGroup.animate({
  38872. opacity: 1
  38873. }, animationConfig);
  38874. }
  38875. }
  38876. /* *
  38877. *
  38878. * Composition
  38879. *
  38880. * */
  38881. var StackingAxis;
  38882. (function (StackingAxis) {
  38883. /* *
  38884. *
  38885. * Constants
  38886. *
  38887. * */
  38888. const composedMembers = [];
  38889. /* *
  38890. *
  38891. * Functions
  38892. *
  38893. * */
  38894. /**
  38895. * Extends axis with stacking support.
  38896. * @private
  38897. */
  38898. function compose(AxisClass, ChartClass, SeriesClass) {
  38899. if (U.pushUnique(composedMembers, AxisClass)) {
  38900. addEvent(AxisClass, 'init', onAxisInit);
  38901. addEvent(AxisClass, 'destroy', onAxisDestroy);
  38902. }
  38903. if (U.pushUnique(composedMembers, ChartClass)) {
  38904. const chartProto = ChartClass.prototype;
  38905. chartProto.getStacks = chartGetStacks;
  38906. }
  38907. if (U.pushUnique(composedMembers, SeriesClass)) {
  38908. const seriesProto = SeriesClass.prototype;
  38909. seriesProto.getStackIndicator = seriesGetStackIndicator;
  38910. seriesProto.modifyStacks = seriesModifyStacks;
  38911. seriesProto.percentStacker = seriesPercentStacker;
  38912. seriesProto.setGroupedPoints = seriesSetGroupedPoints;
  38913. seriesProto.setStackedPoints = seriesSetStackedPoints;
  38914. }
  38915. }
  38916. StackingAxis.compose = compose;
  38917. })(StackingAxis || (StackingAxis = {}));
  38918. /* *
  38919. *
  38920. * Default Export
  38921. *
  38922. * */
  38923. return StackingAxis;
  38924. });
  38925. _registerModule(_modules, 'Series/Line/LineSeries.js', [_modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Series, SeriesRegistry, U) {
  38926. /* *
  38927. *
  38928. * (c) 2010-2021 Torstein Honsi
  38929. *
  38930. * License: www.highcharts.com/license
  38931. *
  38932. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38933. *
  38934. * */
  38935. const { defined, merge } = U;
  38936. /* *
  38937. *
  38938. * Class
  38939. *
  38940. * */
  38941. /**
  38942. * The line series is the base type and is therefor the series base prototype.
  38943. *
  38944. * @private
  38945. */
  38946. class LineSeries extends Series {
  38947. constructor() {
  38948. /* *
  38949. *
  38950. * Static Functions
  38951. *
  38952. * */
  38953. super(...arguments);
  38954. /* *
  38955. *
  38956. * Properties
  38957. *
  38958. * */
  38959. this.data = void 0;
  38960. this.options = void 0;
  38961. this.points = void 0;
  38962. }
  38963. /* *
  38964. *
  38965. * Functions
  38966. *
  38967. * */
  38968. /**
  38969. * Draw the graph. Called internally when rendering line-like series
  38970. * types. The first time it generates the `series.graph` item and
  38971. * optionally other series-wide items like `series.area` for area
  38972. * charts. On subsequent calls these items are updated with new
  38973. * positions and attributes.
  38974. *
  38975. * @function Highcharts.Series#drawGraph
  38976. */
  38977. drawGraph() {
  38978. const series = this, options = this.options, graphPath = (this.gappedPath || this.getGraphPath).call(this), styledMode = this.chart.styledMode;
  38979. let props = [[
  38980. 'graph',
  38981. 'highcharts-graph'
  38982. ]];
  38983. // Presentational properties
  38984. if (!styledMode) {
  38985. props[0].push((options.lineColor ||
  38986. this.color ||
  38987. "#cccccc" /* Palette.neutralColor20 */ // when colorByPoint = true
  38988. ), options.dashStyle);
  38989. }
  38990. props = series.getZonesGraphs(props);
  38991. // Draw the graph
  38992. props.forEach(function (prop, i) {
  38993. const graphKey = prop[0];
  38994. let attribs, graph = series[graphKey];
  38995. const verb = graph ? 'animate' : 'attr';
  38996. if (graph) {
  38997. graph.endX = series.preventGraphAnimation ?
  38998. null :
  38999. graphPath.xMap;
  39000. graph.animate({ d: graphPath });
  39001. }
  39002. else if (graphPath.length) { // #1487
  39003. /**
  39004. * SVG element of area-based charts. Can be used for styling
  39005. * purposes. If zones are configured, this element will be
  39006. * hidden and replaced by multiple zone areas, accessible
  39007. * via `series['zone-area-x']` (where x is a number,
  39008. * starting with 0).
  39009. *
  39010. * @name Highcharts.Series#area
  39011. * @type {Highcharts.SVGElement|undefined}
  39012. */
  39013. /**
  39014. * SVG element of line-based charts. Can be used for styling
  39015. * purposes. If zones are configured, this element will be
  39016. * hidden and replaced by multiple zone lines, accessible
  39017. * via `series['zone-graph-x']` (where x is a number,
  39018. * starting with 0).
  39019. *
  39020. * @name Highcharts.Series#graph
  39021. * @type {Highcharts.SVGElement|undefined}
  39022. */
  39023. series[graphKey] = graph = series.chart.renderer
  39024. .path(graphPath)
  39025. .addClass(prop[1])
  39026. .attr({ zIndex: 1 }) // #1069
  39027. .add(series.group);
  39028. }
  39029. if (graph && !styledMode) {
  39030. attribs = {
  39031. 'stroke': prop[2],
  39032. 'stroke-width': options.lineWidth || 0,
  39033. // Polygon series use filled graph
  39034. 'fill': (series.fillGraph && series.color) || 'none'
  39035. };
  39036. // Apply dash style
  39037. if (prop[3]) {
  39038. attribs.dashstyle = prop[3];
  39039. // The reason for the `else if` is that linecaps don't mix well
  39040. // with dashstyle. The gaps get partially filled by the
  39041. // linecap.
  39042. }
  39043. else if (options.linecap !== 'square') {
  39044. attribs['stroke-linecap'] =
  39045. attribs['stroke-linejoin'] = 'round';
  39046. }
  39047. graph[verb](attribs)
  39048. // Add shadow to normal series (0) or to first
  39049. // zone (1) #3932
  39050. .shadow((i < 2) && options.shadow);
  39051. }
  39052. // Helpers for animation
  39053. if (graph) {
  39054. graph.startX = graphPath.xMap;
  39055. graph.isArea = graphPath.isArea; // For arearange animation
  39056. }
  39057. });
  39058. }
  39059. // eslint-disable-next-line valid-jsdoc
  39060. /**
  39061. * Get the graph path.
  39062. *
  39063. * @private
  39064. */
  39065. getGraphPath(points, nullsAsZeroes, connectCliffs) {
  39066. const series = this, options = series.options, graphPath = [], xMap = [];
  39067. let gap, step = options.step;
  39068. points = points || series.points;
  39069. // Bottom of a stack is reversed
  39070. const reversed = points.reversed;
  39071. if (reversed) {
  39072. points.reverse();
  39073. }
  39074. // Reverse the steps (#5004)
  39075. step = {
  39076. right: 1,
  39077. center: 2
  39078. }[step] || (step && 3);
  39079. if (step && reversed) {
  39080. step = 4 - step;
  39081. }
  39082. // Remove invalid points, especially in spline (#5015)
  39083. points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
  39084. // Build the line
  39085. points.forEach(function (point, i) {
  39086. const plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1], isNull = point.isNull || typeof plotY !== 'number';
  39087. // the path to this point from the previous
  39088. let pathToPoint;
  39089. if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
  39090. !connectCliffs) {
  39091. gap = true; // ... and continue
  39092. }
  39093. // Line series, nullsAsZeroes is not handled
  39094. if (isNull && !defined(nullsAsZeroes) && i > 0) {
  39095. gap = !options.connectNulls;
  39096. // Area series, nullsAsZeroes is set
  39097. }
  39098. else if (isNull && !nullsAsZeroes) {
  39099. gap = true;
  39100. }
  39101. else {
  39102. if (i === 0 || gap) {
  39103. pathToPoint = [[
  39104. 'M',
  39105. point.plotX,
  39106. point.plotY
  39107. ]];
  39108. // Generate the spline as defined in the SplineSeries object
  39109. }
  39110. else if (series.getPointSpline) {
  39111. pathToPoint = [series.getPointSpline(points, point, i)];
  39112. }
  39113. else if (step) {
  39114. if (step === 1) { // right
  39115. pathToPoint = [[
  39116. 'L',
  39117. lastPoint.plotX,
  39118. plotY
  39119. ]];
  39120. }
  39121. else if (step === 2) { // center
  39122. pathToPoint = [[
  39123. 'L',
  39124. (lastPoint.plotX + plotX) / 2,
  39125. lastPoint.plotY
  39126. ], [
  39127. 'L',
  39128. (lastPoint.plotX + plotX) / 2,
  39129. plotY
  39130. ]];
  39131. }
  39132. else {
  39133. pathToPoint = [[
  39134. 'L',
  39135. plotX,
  39136. lastPoint.plotY
  39137. ]];
  39138. }
  39139. pathToPoint.push([
  39140. 'L',
  39141. plotX,
  39142. plotY
  39143. ]);
  39144. }
  39145. else {
  39146. // normal line to next point
  39147. pathToPoint = [[
  39148. 'L',
  39149. plotX,
  39150. plotY
  39151. ]];
  39152. }
  39153. // Prepare for animation. When step is enabled, there are
  39154. // two path nodes for each x value.
  39155. xMap.push(point.x);
  39156. if (step) {
  39157. xMap.push(point.x);
  39158. if (step === 2) { // step = center (#8073)
  39159. xMap.push(point.x);
  39160. }
  39161. }
  39162. graphPath.push.apply(graphPath, pathToPoint);
  39163. gap = false;
  39164. }
  39165. });
  39166. graphPath.xMap = xMap;
  39167. series.graphPath = graphPath;
  39168. return graphPath;
  39169. }
  39170. // eslint-disable-next-line valid-jsdoc
  39171. /**
  39172. * Get zones properties for building graphs. Extendable by series with
  39173. * multiple lines within one series.
  39174. *
  39175. * @private
  39176. */
  39177. getZonesGraphs(props) {
  39178. // Add the zone properties if any
  39179. this.zones.forEach(function (zone, i) {
  39180. const propset = [
  39181. 'zone-graph-' + i,
  39182. 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
  39183. (zone.className || '')
  39184. ];
  39185. if (!this.chart.styledMode) {
  39186. propset.push((zone.color || this.color), (zone.dashStyle || this.options.dashStyle));
  39187. }
  39188. props.push(propset);
  39189. }, this);
  39190. return props;
  39191. }
  39192. }
  39193. LineSeries.defaultOptions = merge(Series.defaultOptions,
  39194. /**
  39195. * General options for all series types.
  39196. *
  39197. * @optionparent plotOptions.series
  39198. */
  39199. {
  39200. legendSymbol: 'lineMarker'
  39201. });
  39202. SeriesRegistry.registerSeriesType('line', LineSeries);
  39203. /* *
  39204. *
  39205. * Default Export
  39206. *
  39207. * */
  39208. /* *
  39209. *
  39210. * API Options
  39211. *
  39212. * */
  39213. /**
  39214. * A line series displays information as a series of data points connected by
  39215. * straight line segments.
  39216. *
  39217. * @sample {highcharts} highcharts/demo/line-basic/
  39218. * Line chart
  39219. * @sample {highstock} stock/demo/basic-line/
  39220. * Line chart
  39221. *
  39222. * @extends plotOptions.series
  39223. * @product highcharts highstock
  39224. * @apioption plotOptions.line
  39225. */
  39226. /**
  39227. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  39228. * of a line graph. Round means that lines are rounded in the ends and
  39229. * bends.
  39230. *
  39231. * @type {Highcharts.SeriesLinecapValue}
  39232. * @default round
  39233. * @since 3.0.7
  39234. * @apioption plotOptions.line.linecap
  39235. */
  39236. /**
  39237. * A `line` series. If the [type](#series.line.type) option is not
  39238. * specified, it is inherited from [chart.type](#chart.type).
  39239. *
  39240. * @extends series,plotOptions.line
  39241. * @excluding dataParser,dataURL
  39242. * @product highcharts highstock
  39243. * @apioption series.line
  39244. */
  39245. /**
  39246. * An array of data points for the series. For the `line` series type,
  39247. * points can be given in the following ways:
  39248. *
  39249. * 1. An array of numerical values. In this case, the numerical values will be
  39250. * interpreted as `y` options. The `x` values will be automatically
  39251. * calculated, either starting at 0 and incremented by 1, or from
  39252. * `pointStart` and `pointInterval` given in the series options. If the axis
  39253. * has categories, these will be used. Example:
  39254. * ```js
  39255. * data: [0, 5, 3, 5]
  39256. * ```
  39257. *
  39258. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39259. * `x,y`. If the first value is a string, it is applied as the name of the
  39260. * point, and the `x` value is inferred.
  39261. * ```js
  39262. * data: [
  39263. * [0, 1],
  39264. * [1, 2],
  39265. * [2, 8]
  39266. * ]
  39267. * ```
  39268. *
  39269. * 3. An array of objects with named values. The following snippet shows only a
  39270. * few settings, see the complete options set below. If the total number of
  39271. * data points exceeds the series'
  39272. * [turboThreshold](#series.line.turboThreshold),
  39273. * this option is not available.
  39274. * ```js
  39275. * data: [{
  39276. * x: 1,
  39277. * y: 9,
  39278. * name: "Point2",
  39279. * color: "#00FF00"
  39280. * }, {
  39281. * x: 1,
  39282. * y: 6,
  39283. * name: "Point1",
  39284. * color: "#FF00FF"
  39285. * }]
  39286. * ```
  39287. *
  39288. * **Note:** In TypeScript you have to extend `PointOptionsObject` with an
  39289. * additional declaration to allow custom data types:
  39290. * ```ts
  39291. * declare module `highcharts` {
  39292. * interface PointOptionsObject {
  39293. * custom: Record<string, (boolean|number|string)>;
  39294. * }
  39295. * }
  39296. * ```
  39297. *
  39298. * @sample {highcharts} highcharts/chart/reflow-true/
  39299. * Numerical values
  39300. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39301. * Arrays of numeric x and y
  39302. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39303. * Arrays of datetime x and y
  39304. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39305. * Arrays of point.name and y
  39306. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39307. * Config objects
  39308. *
  39309. * @declare Highcharts.PointOptionsObject
  39310. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39311. * @apioption series.line.data
  39312. */
  39313. /**
  39314. * An additional, individual class name for the data point's graphic
  39315. * representation. Changes to a point's color will also be reflected in a
  39316. * chart's legend and tooltip.
  39317. *
  39318. * @sample {highcharts} highcharts/css/point-series-classname
  39319. *
  39320. * @type {string}
  39321. * @since 5.0.0
  39322. * @product highcharts gantt
  39323. * @apioption series.line.data.className
  39324. */
  39325. /**
  39326. * Individual color for the point. By default the color is pulled from
  39327. * the global `colors` array.
  39328. *
  39329. * In styled mode, the `color` option doesn't take effect. Instead, use
  39330. * `colorIndex`.
  39331. *
  39332. * @sample {highcharts} highcharts/point/color/
  39333. * Mark the highest point
  39334. *
  39335. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39336. * @product highcharts highstock gantt
  39337. * @apioption series.line.data.color
  39338. */
  39339. /**
  39340. * A specific color index to use for the point, so its graphic representations
  39341. * are given the class name `highcharts-color-{n}`. In styled mode this will
  39342. * change the color of the graphic. In non-styled mode, the color is set by the
  39343. * `fill` attribute, so the change in class name won't have a visual effect by
  39344. * default.
  39345. *
  39346. * Since v11, CSS variables on the form `--highcharts-color-{n}` make changing
  39347. * the color scheme very convenient.
  39348. *
  39349. * @sample {highcharts} highcharts/css/colorindex/
  39350. * Series and point color index
  39351. *
  39352. * @type {number}
  39353. * @since 5.0.0
  39354. * @product highcharts gantt
  39355. * @apioption series.line.data.colorIndex
  39356. */
  39357. /**
  39358. * A reserved subspace to store options and values for customized functionality.
  39359. * Here you can add additional data for your own event callbacks and formatter
  39360. * callbacks.
  39361. *
  39362. * @sample {highcharts} highcharts/point/custom/
  39363. * Point and series with custom data
  39364. *
  39365. * @type {Highcharts.Dictionary<*>}
  39366. * @apioption series.line.data.custom
  39367. */
  39368. /**
  39369. * Individual data label for each point. The options are the same as
  39370. * the ones for [plotOptions.series.dataLabels](
  39371. * #plotOptions.series.dataLabels).
  39372. *
  39373. * @sample highcharts/point/datalabels/
  39374. * Show a label for the last value
  39375. *
  39376. * @declare Highcharts.DataLabelsOptions
  39377. * @extends plotOptions.line.dataLabels
  39378. * @product highcharts highstock gantt
  39379. * @apioption series.line.data.dataLabels
  39380. */
  39381. /**
  39382. * A description of the point to add to the screen reader information
  39383. * about the point.
  39384. *
  39385. * @type {string}
  39386. * @since 5.0.0
  39387. * @requires modules/accessibility
  39388. * @apioption series.line.data.description
  39389. */
  39390. /**
  39391. * An id for the point. This can be used after render time to get a
  39392. * pointer to the point object through `chart.get()`.
  39393. *
  39394. * @sample {highcharts} highcharts/point/id/
  39395. * Remove an id'd point
  39396. *
  39397. * @type {string}
  39398. * @since 1.2.0
  39399. * @product highcharts highstock gantt
  39400. * @apioption series.line.data.id
  39401. */
  39402. /**
  39403. * The rank for this point's data label in case of collision. If two
  39404. * data labels are about to overlap, only the one with the highest `labelrank`
  39405. * will be drawn.
  39406. *
  39407. * @type {number}
  39408. * @apioption series.line.data.labelrank
  39409. */
  39410. /**
  39411. * The name of the point as shown in the legend, tooltip, dataLabels, etc.
  39412. *
  39413. * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
  39414. *
  39415. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39416. * Point names
  39417. *
  39418. * @type {string}
  39419. * @apioption series.line.data.name
  39420. */
  39421. /**
  39422. * Whether the data point is selected initially.
  39423. *
  39424. * @type {boolean}
  39425. * @default false
  39426. * @product highcharts highstock gantt
  39427. * @apioption series.line.data.selected
  39428. */
  39429. /**
  39430. * The x value of the point. For datetime axes, the X value is the timestamp
  39431. * in milliseconds since 1970.
  39432. *
  39433. * @type {number}
  39434. * @product highcharts highstock
  39435. * @apioption series.line.data.x
  39436. */
  39437. /**
  39438. * The y value of the point.
  39439. *
  39440. * @type {number|null}
  39441. * @product highcharts highstock
  39442. * @apioption series.line.data.y
  39443. */
  39444. /**
  39445. * The individual point events.
  39446. *
  39447. * @extends plotOptions.series.point.events
  39448. * @product highcharts highstock gantt
  39449. * @apioption series.line.data.events
  39450. */
  39451. /**
  39452. * Options for the point markers of line-like series.
  39453. *
  39454. * @declare Highcharts.PointMarkerOptionsObject
  39455. * @extends plotOptions.series.marker
  39456. * @product highcharts highstock
  39457. * @apioption series.line.data.marker
  39458. */
  39459. ''; // include precedent doclets in transpilat
  39460. return LineSeries;
  39461. });
  39462. _registerModule(_modules, 'Series/Area/AreaSeries.js', [_modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Color, SeriesRegistry, U) {
  39463. /* *
  39464. *
  39465. * (c) 2010-2021 Torstein Honsi
  39466. *
  39467. * License: www.highcharts.com/license
  39468. *
  39469. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39470. *
  39471. * */
  39472. const { parse: color } = Color;
  39473. const { seriesTypes: { line: LineSeries } } = SeriesRegistry;
  39474. const { extend, merge, objectEach, pick } = U;
  39475. /* *
  39476. *
  39477. * Class
  39478. *
  39479. * */
  39480. /**
  39481. * Area series type.
  39482. *
  39483. * @private
  39484. * @class
  39485. * @name AreaSeries
  39486. *
  39487. * @augments LineSeries
  39488. */
  39489. class AreaSeries extends LineSeries {
  39490. constructor() {
  39491. /* *
  39492. *
  39493. * Static Properties
  39494. *
  39495. * */
  39496. super(...arguments);
  39497. this.data = void 0;
  39498. this.options = void 0;
  39499. this.points = void 0;
  39500. /* eslint-enable valid-jsdoc */
  39501. }
  39502. /* *
  39503. *
  39504. * Functions
  39505. *
  39506. * */
  39507. /* eslint-disable valid-jsdoc */
  39508. /**
  39509. * Draw the graph and the underlying area. This method calls the Series
  39510. * base function and adds the area. The areaPath is calculated in the
  39511. * getSegmentPath method called from Series.prototype.drawGraph.
  39512. * @private
  39513. */
  39514. drawGraph() {
  39515. // Define or reset areaPath
  39516. this.areaPath = [];
  39517. // Call the base method
  39518. super.drawGraph.apply(this);
  39519. // Define local variables
  39520. const series = this, areaPath = this.areaPath, options = this.options, zones = this.zones, props = [[
  39521. 'area',
  39522. 'highcharts-area',
  39523. this.color,
  39524. options.fillColor
  39525. ]]; // area name, main color, fill color
  39526. zones.forEach(function (zone, i) {
  39527. props.push([
  39528. 'zone-area-' + i,
  39529. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  39530. zone.className,
  39531. zone.color || series.color,
  39532. zone.fillColor || options.fillColor
  39533. ]);
  39534. });
  39535. props.forEach(function (prop) {
  39536. const areaKey = prop[0], attribs = {};
  39537. let area = series[areaKey];
  39538. const verb = area ? 'animate' : 'attr';
  39539. // Create or update the area
  39540. if (area) { // update
  39541. area.endX = series.preventGraphAnimation ?
  39542. null :
  39543. areaPath.xMap;
  39544. area.animate({ d: areaPath });
  39545. }
  39546. else { // create
  39547. attribs.zIndex = 0; // #1069
  39548. area = series[areaKey] = series.chart.renderer
  39549. .path(areaPath)
  39550. .addClass(prop[1])
  39551. .add(series.group);
  39552. area.isArea = true;
  39553. }
  39554. if (!series.chart.styledMode) {
  39555. // If there is fillColor defined for the area, set it
  39556. if (prop[3]) {
  39557. attribs.fill = prop[3];
  39558. }
  39559. else {
  39560. // Otherwise, we set it to the series color and add
  39561. // fill-opacity (#18939)
  39562. attribs.fill = prop[2];
  39563. attribs['fill-opacity'] = pick(options.fillOpacity, 0.75);
  39564. }
  39565. }
  39566. area[verb](attribs);
  39567. area.startX = areaPath.xMap;
  39568. area.shiftUnit = options.step ? 2 : 1;
  39569. });
  39570. }
  39571. /**
  39572. * @private
  39573. */
  39574. getGraphPath(points) {
  39575. const getGraphPath = LineSeries.prototype.getGraphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, bottomPoints = [], graphPoints = [], seriesIndex = this.index, stacks = yAxis.stacking.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
  39576. yAxis.getThreshold(options.threshold)), connectNulls = pick(// #10574
  39577. options.connectNulls, stacking === 'percent'),
  39578. // To display null points in underlying stacked series, this
  39579. // series graph must be broken, and the area also fall down to
  39580. // fill the gap left by the null point. #2069
  39581. addDummyPoints = function (i, otherI, side) {
  39582. const point = points[i], stackedValues = stacking &&
  39583. stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0;
  39584. let top, bottom, isNull = true;
  39585. if (cliffVal || nullVal) {
  39586. top = (nullVal ?
  39587. stackedValues[0] :
  39588. stackedValues[1]) + cliffVal;
  39589. bottom = stackedValues[0] + cliffVal;
  39590. isNull = !!nullVal;
  39591. }
  39592. else if (!stacking &&
  39593. points[otherI] &&
  39594. points[otherI].isNull) {
  39595. top = bottom = threshold;
  39596. }
  39597. // Add to the top and bottom line of the area
  39598. if (typeof top !== 'undefined') {
  39599. graphPoints.push({
  39600. plotX: plotX,
  39601. plotY: top === null ?
  39602. translatedThreshold :
  39603. yAxis.getThreshold(top),
  39604. isNull: isNull,
  39605. isCliff: true
  39606. });
  39607. bottomPoints.push({
  39608. plotX: plotX,
  39609. plotY: bottom === null ?
  39610. translatedThreshold :
  39611. yAxis.getThreshold(bottom),
  39612. doCurve: false // #1041, gaps in areaspline areas
  39613. });
  39614. }
  39615. };
  39616. let plotX, isNull, yBottom;
  39617. // Find what points to use
  39618. points = points || this.points;
  39619. // Fill in missing points
  39620. if (stacking) {
  39621. points = this.getStackPoints(points);
  39622. }
  39623. for (let i = 0, iEnd = points.length; i < iEnd; ++i) {
  39624. // Reset after series.update of stacking property (#12033)
  39625. if (!stacking) {
  39626. points[i].leftCliff = points[i].rightCliff =
  39627. points[i].leftNull = points[i].rightNull = void 0;
  39628. }
  39629. isNull = points[i].isNull;
  39630. plotX = pick(points[i].rectPlotX, points[i].plotX);
  39631. yBottom = stacking ?
  39632. pick(points[i].yBottom, translatedThreshold) :
  39633. translatedThreshold;
  39634. if (!isNull || connectNulls) {
  39635. if (!connectNulls) {
  39636. addDummyPoints(i, i - 1, 'left');
  39637. }
  39638. // Skip null point when stacking is false and connectNulls
  39639. // true
  39640. if (!(isNull && !stacking && connectNulls)) {
  39641. graphPoints.push(points[i]);
  39642. bottomPoints.push({
  39643. x: i,
  39644. plotX: plotX,
  39645. plotY: yBottom
  39646. });
  39647. }
  39648. if (!connectNulls) {
  39649. addDummyPoints(i, i + 1, 'right');
  39650. }
  39651. }
  39652. }
  39653. const topPath = getGraphPath.call(this, graphPoints, true, true);
  39654. bottomPoints.reversed = true;
  39655. const bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  39656. const firstBottomPoint = bottomPath[0];
  39657. if (firstBottomPoint && firstBottomPoint[0] === 'M') {
  39658. bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]];
  39659. }
  39660. const areaPath = topPath.concat(bottomPath);
  39661. if (areaPath.length) {
  39662. areaPath.push(['Z']);
  39663. }
  39664. // TODO: don't set leftCliff and rightCliff when connectNulls?
  39665. const graphPath = getGraphPath
  39666. .call(this, graphPoints, false, connectNulls);
  39667. areaPath.xMap = topPath.xMap;
  39668. this.areaPath = areaPath;
  39669. return graphPath;
  39670. }
  39671. /**
  39672. * Return an array of stacked points, where null and missing points are
  39673. * replaced by dummy points in order for gaps to be drawn correctly in
  39674. * stacks.
  39675. * @private
  39676. */
  39677. getStackPoints(points) {
  39678. const series = this, segment = [], keys = [], xAxis = this.xAxis, yAxis = this.yAxis, stack = yAxis.stacking.stacks[this.stackKey], pointMap = {}, yAxisSeries = yAxis.series, seriesLength = yAxisSeries.length, upOrDown = yAxis.options.reversedStacks ? 1 : -1, seriesIndex = yAxisSeries.indexOf(series);
  39679. points = points || this.points;
  39680. if (this.options.stacking) {
  39681. for (let i = 0; i < points.length; i++) {
  39682. // Reset after point update (#7326)
  39683. points[i].leftNull = points[i].rightNull = void 0;
  39684. // Create a map where we can quickly look up the points by
  39685. // their X values.
  39686. pointMap[points[i].x] = points[i];
  39687. }
  39688. // Sort the keys (#1651)
  39689. objectEach(stack, function (stackX, x) {
  39690. // nulled after switching between
  39691. // grouping and not (#1651, #2336)
  39692. if (stackX.total !== null) {
  39693. keys.push(x);
  39694. }
  39695. });
  39696. keys.sort(function (a, b) {
  39697. return a - b;
  39698. });
  39699. const visibleSeries = yAxisSeries.map((s) => s.visible);
  39700. keys.forEach(function (x, idx) {
  39701. let y = 0, stackPoint, stackedValues;
  39702. if (pointMap[x] && !pointMap[x].isNull) {
  39703. segment.push(pointMap[x]);
  39704. // Find left and right cliff. -1 goes left, 1 goes
  39705. // right.
  39706. [-1, 1].forEach(function (direction) {
  39707. const nullName = direction === 1 ?
  39708. 'rightNull' :
  39709. 'leftNull', cliffName = direction === 1 ?
  39710. 'rightCliff' :
  39711. 'leftCliff', otherStack = stack[keys[idx + direction]];
  39712. let cliff = 0;
  39713. // If there is a stack next to this one,
  39714. // to the left or to the right...
  39715. if (otherStack) {
  39716. let i = seriesIndex;
  39717. // Can go either up or down,
  39718. // depending on reversedStacks
  39719. while (i >= 0 && i < seriesLength) {
  39720. const si = yAxisSeries[i].index;
  39721. stackPoint = otherStack.points[si];
  39722. if (!stackPoint) {
  39723. // If the next point in this series is
  39724. // missing, mark the point with
  39725. // point.leftNull or point.rightNull = true.
  39726. if (si === series.index) {
  39727. pointMap[x][nullName] = true;
  39728. // If there are missing points in the next
  39729. // stack in any of the series below this
  39730. // one, we need to substract the missing
  39731. // values and add a hiatus to the left or
  39732. // right.
  39733. }
  39734. else if (visibleSeries[i]) {
  39735. stackedValues = stack[x].points[si];
  39736. if (stackedValues) {
  39737. cliff -= (stackedValues[1] -
  39738. stackedValues[0]);
  39739. }
  39740. }
  39741. }
  39742. // When reversedStacks is true, loop up,
  39743. // else loop down
  39744. i += upOrDown;
  39745. }
  39746. }
  39747. pointMap[x][cliffName] = cliff;
  39748. });
  39749. // There is no point for this X value in this series, so we
  39750. // insert a dummy point in order for the areas to be drawn
  39751. // correctly.
  39752. }
  39753. else {
  39754. // Loop down the stack to find the series below this
  39755. // one that has a value (#1991)
  39756. let i = seriesIndex;
  39757. while (i >= 0 && i < seriesLength) {
  39758. const si = yAxisSeries[i].index;
  39759. stackPoint = stack[x].points[si];
  39760. if (stackPoint) {
  39761. y = stackPoint[1];
  39762. break;
  39763. }
  39764. // When reversedStacks is true, loop up, else loop
  39765. // down
  39766. i += upOrDown;
  39767. }
  39768. y = pick(y, 0);
  39769. y = yAxis.translate(// #6272
  39770. y, 0, 1, 0, 1);
  39771. segment.push({
  39772. isNull: true,
  39773. plotX: xAxis.translate(// #6272
  39774. x, 0, 0, 0, 1),
  39775. x: x,
  39776. plotY: y,
  39777. yBottom: y
  39778. });
  39779. }
  39780. });
  39781. }
  39782. return segment;
  39783. }
  39784. }
  39785. /**
  39786. * The area series type.
  39787. *
  39788. * @sample {highcharts} highcharts/demo/area-basic/
  39789. * Area chart
  39790. * @sample {highstock} stock/demo/area/
  39791. * Area chart
  39792. *
  39793. * @extends plotOptions.line
  39794. * @excluding useOhlcData
  39795. * @product highcharts highstock
  39796. * @optionparent plotOptions.area
  39797. */
  39798. AreaSeries.defaultOptions = merge(LineSeries.defaultOptions, {
  39799. /**
  39800. * @see [fillColor](#plotOptions.area.fillColor)
  39801. * @see [fillOpacity](#plotOptions.area.fillOpacity)
  39802. *
  39803. * @apioption plotOptions.area.color
  39804. */
  39805. /**
  39806. * Fill color or gradient for the area. When `null`, the series' `color`
  39807. * is used with the series' `fillOpacity`.
  39808. *
  39809. * In styled mode, the fill color can be set with the `.highcharts-area`
  39810. * class name.
  39811. *
  39812. * @see [color](#plotOptions.area.color)
  39813. * @see [fillOpacity](#plotOptions.area.fillOpacity)
  39814. *
  39815. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  39816. * Null by default
  39817. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  39818. * Gradient
  39819. *
  39820. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39821. * @product highcharts highstock
  39822. * @apioption plotOptions.area.fillColor
  39823. */
  39824. /**
  39825. * Fill opacity for the area. When you set an explicit `fillColor`,
  39826. * the `fillOpacity` is not applied. Instead, you should define the
  39827. * opacity in the `fillColor` with an rgba color definition. The
  39828. * `fillOpacity` setting, also the default setting, overrides the alpha
  39829. * component of the `color` setting.
  39830. *
  39831. * In styled mode, the fill opacity can be set with the
  39832. * `.highcharts-area` class name.
  39833. *
  39834. * @see [color](#plotOptions.area.color)
  39835. * @see [fillColor](#plotOptions.area.fillColor)
  39836. *
  39837. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  39838. * Automatic fill color and fill opacity of 0.1
  39839. *
  39840. * @type {number}
  39841. * @default {highcharts} 0.75
  39842. * @default {highstock} 0.75
  39843. * @product highcharts highstock
  39844. * @apioption plotOptions.area.fillOpacity
  39845. */
  39846. /**
  39847. * A separate color for the graph line. By default the line takes the
  39848. * `color` of the series, but the lineColor setting allows setting a
  39849. * separate color for the line without altering the `fillColor`.
  39850. *
  39851. * In styled mode, the line stroke can be set with the
  39852. * `.highcharts-graph` class name.
  39853. *
  39854. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  39855. * Dark gray line
  39856. *
  39857. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39858. * @product highcharts highstock
  39859. * @apioption plotOptions.area.lineColor
  39860. */
  39861. /**
  39862. * A separate color for the negative part of the area.
  39863. *
  39864. * In styled mode, a negative color is set with the
  39865. * `.highcharts-negative` class name.
  39866. *
  39867. * @see [negativeColor](#plotOptions.area.negativeColor)
  39868. *
  39869. * @sample {highcharts} highcharts/css/series-negative-color/
  39870. * Negative color in styled mode
  39871. *
  39872. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39873. * @since 3.0
  39874. * @product highcharts
  39875. * @apioption plotOptions.area.negativeFillColor
  39876. */
  39877. /**
  39878. * Whether the whole area or just the line should respond to mouseover
  39879. * tooltips and other mouse or touch events.
  39880. *
  39881. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  39882. * Display the tooltip when the area is hovered
  39883. *
  39884. * @type {boolean}
  39885. * @default false
  39886. * @since 1.1.6
  39887. * @product highcharts highstock
  39888. * @apioption plotOptions.area.trackByArea
  39889. */
  39890. /**
  39891. * The Y axis value to serve as the base for the area, for
  39892. * distinguishing between values above and below a threshold. The area
  39893. * between the graph and the threshold is filled.
  39894. *
  39895. * * If a number is given, the Y axis will scale to the threshold.
  39896. * * If `null`, the scaling behaves like a line series with fill between
  39897. * the graph and the Y axis minimum.
  39898. * * If `Infinity` or `-Infinity`, the area between the graph and the
  39899. * corresponding Y axis extreme is filled (since v6.1.0).
  39900. *
  39901. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  39902. * A threshold of 100
  39903. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  39904. * A threshold of Infinity
  39905. *
  39906. * @type {number|null}
  39907. * @since 2.0
  39908. * @product highcharts highstock
  39909. */
  39910. threshold: 0,
  39911. legendSymbol: 'rectangle'
  39912. });
  39913. extend(AreaSeries.prototype, {
  39914. singleStacks: false
  39915. });
  39916. SeriesRegistry.registerSeriesType('area', AreaSeries);
  39917. /* *
  39918. *
  39919. * Default Export
  39920. *
  39921. * */
  39922. /* *
  39923. *
  39924. * API Options
  39925. *
  39926. * */
  39927. /**
  39928. * A `area` series. If the [type](#series.area.type) option is not
  39929. * specified, it is inherited from [chart.type](#chart.type).
  39930. *
  39931. * @extends series,plotOptions.area
  39932. * @excluding dataParser, dataURL, useOhlcData
  39933. * @product highcharts highstock
  39934. * @apioption series.area
  39935. */
  39936. /**
  39937. * @see [fillColor](#series.area.fillColor)
  39938. * @see [fillOpacity](#series.area.fillOpacity)
  39939. *
  39940. * @apioption series.area.color
  39941. */
  39942. /**
  39943. * An array of data points for the series. For the `area` series type,
  39944. * points can be given in the following ways:
  39945. *
  39946. * 1. An array of numerical values. In this case, the numerical values will be
  39947. * interpreted as `y` options. The `x` values will be automatically
  39948. * calculated, either starting at 0 and incremented by 1, or from
  39949. * `pointStart` * and `pointInterval` given in the series options. If the
  39950. * axis has categories, these will be used. Example:
  39951. * ```js
  39952. * data: [0, 5, 3, 5]
  39953. * ```
  39954. *
  39955. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39956. * `x,y`. If the first value is a string, it is applied as the name of the
  39957. * point, and the `x` value is inferred.
  39958. * ```js
  39959. * data: [
  39960. * [0, 9],
  39961. * [1, 7],
  39962. * [2, 6]
  39963. * ]
  39964. * ```
  39965. *
  39966. * 3. An array of objects with named values. The following snippet shows only a
  39967. * few settings, see the complete options set below. If the total number of
  39968. * data points exceeds the series'
  39969. * [turboThreshold](#series.area.turboThreshold), this option is not
  39970. * available.
  39971. * ```js
  39972. * data: [{
  39973. * x: 1,
  39974. * y: 9,
  39975. * name: "Point2",
  39976. * color: "#00FF00"
  39977. * }, {
  39978. * x: 1,
  39979. * y: 6,
  39980. * name: "Point1",
  39981. * color: "#FF00FF"
  39982. * }]
  39983. * ```
  39984. *
  39985. * @sample {highcharts} highcharts/chart/reflow-true/
  39986. * Numerical values
  39987. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39988. * Arrays of numeric x and y
  39989. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39990. * Arrays of datetime x and y
  39991. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39992. * Arrays of point.name and y
  39993. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39994. * Config objects
  39995. *
  39996. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39997. * @extends series.line.data
  39998. * @product highcharts highstock
  39999. * @apioption series.area.data
  40000. */
  40001. /**
  40002. * @see [color](#series.area.color)
  40003. * @see [fillOpacity](#series.area.fillOpacity)
  40004. *
  40005. * @apioption series.area.fillColor
  40006. */
  40007. /**
  40008. * @see [color](#series.area.color)
  40009. * @see [fillColor](#series.area.fillColor)
  40010. *
  40011. * @default {highcharts} 0.75
  40012. * @default {highstock} 0.75
  40013. * @apioption series.area.fillOpacity
  40014. */
  40015. ''; // adds doclets above to transpilat
  40016. return AreaSeries;
  40017. });
  40018. _registerModule(_modules, 'Series/Spline/SplineSeries.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  40019. /* *
  40020. *
  40021. * (c) 2010-2021 Torstein Honsi
  40022. *
  40023. * License: www.highcharts.com/license
  40024. *
  40025. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40026. *
  40027. * */
  40028. const { line: LineSeries } = SeriesRegistry.seriesTypes;
  40029. const { merge, pick } = U;
  40030. /* *
  40031. *
  40032. * Class
  40033. *
  40034. * */
  40035. /**
  40036. * Spline series type.
  40037. *
  40038. * @private
  40039. */
  40040. class SplineSeries extends LineSeries {
  40041. constructor() {
  40042. /* *
  40043. *
  40044. * Static Properties
  40045. *
  40046. * */
  40047. super(...arguments);
  40048. /* *
  40049. *
  40050. * Properties
  40051. *
  40052. * */
  40053. this.data = void 0;
  40054. this.options = void 0;
  40055. this.points = void 0;
  40056. /* eslint-enable valid-jsdoc */
  40057. }
  40058. /* *
  40059. *
  40060. * Functions
  40061. *
  40062. * */
  40063. /* eslint-disable valid-jsdoc */
  40064. /**
  40065. * Get the spline segment from a given point's previous neighbour to the
  40066. * given point.
  40067. *
  40068. * @private
  40069. * @function Highcharts.seriesTypes.spline#getPointSpline
  40070. */
  40071. getPointSpline(points, point, i) {
  40072. const
  40073. // 1 means control points midway between points, 2 means 1/3
  40074. // from the point, 3 is 1/4 etc
  40075. smoothing = 1.5, denom = smoothing + 1, plotX = point.plotX || 0, plotY = point.plotY || 0, lastPoint = points[i - 1], nextPoint = points[i + 1];
  40076. let leftContX, leftContY, rightContX, rightContY;
  40077. /**
  40078. * @private
  40079. */
  40080. function doCurve(otherPoint) {
  40081. return otherPoint &&
  40082. !otherPoint.isNull &&
  40083. otherPoint.doCurve !== false &&
  40084. // #6387, area splines next to null:
  40085. !point.isCliff;
  40086. }
  40087. // Find control points
  40088. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  40089. const lastX = lastPoint.plotX || 0, lastY = lastPoint.plotY || 0, nextX = nextPoint.plotX || 0, nextY = nextPoint.plotY || 0;
  40090. let correction = 0;
  40091. leftContX = (smoothing * plotX + lastX) / denom;
  40092. leftContY = (smoothing * plotY + lastY) / denom;
  40093. rightContX = (smoothing * plotX + nextX) / denom;
  40094. rightContY = (smoothing * plotY + nextY) / denom;
  40095. // Have the two control points make a straight line through main
  40096. // point
  40097. if (rightContX !== leftContX) { // #5016, division by zero
  40098. correction = (((rightContY - leftContY) *
  40099. (rightContX - plotX)) /
  40100. (rightContX - leftContX) + plotY - rightContY);
  40101. }
  40102. leftContY += correction;
  40103. rightContY += correction;
  40104. // to prevent false extremes, check that control points are
  40105. // between neighbouring points' y values
  40106. if (leftContY > lastY && leftContY > plotY) {
  40107. leftContY = Math.max(lastY, plotY);
  40108. // mirror of left control point
  40109. rightContY = 2 * plotY - leftContY;
  40110. }
  40111. else if (leftContY < lastY && leftContY < plotY) {
  40112. leftContY = Math.min(lastY, plotY);
  40113. rightContY = 2 * plotY - leftContY;
  40114. }
  40115. if (rightContY > nextY && rightContY > plotY) {
  40116. rightContY = Math.max(nextY, plotY);
  40117. leftContY = 2 * plotY - rightContY;
  40118. }
  40119. else if (rightContY < nextY && rightContY < plotY) {
  40120. rightContY = Math.min(nextY, plotY);
  40121. leftContY = 2 * plotY - rightContY;
  40122. }
  40123. // record for drawing in next point
  40124. point.rightContX = rightContX;
  40125. point.rightContY = rightContY;
  40126. }
  40127. // Visualize control points for debugging
  40128. /*
  40129. if (leftContX) {
  40130. this.chart.renderer.circle(
  40131. leftContX + this.chart.plotLeft,
  40132. leftContY + this.chart.plotTop,
  40133. 2
  40134. )
  40135. .attr({
  40136. stroke: 'red',
  40137. 'stroke-width': 2,
  40138. fill: 'none',
  40139. zIndex: 9
  40140. })
  40141. .add();
  40142. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  40143. leftContY + this.chart.plotTop,
  40144. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  40145. .attr({
  40146. stroke: 'red',
  40147. 'stroke-width': 2,
  40148. zIndex: 9
  40149. })
  40150. .add();
  40151. }
  40152. if (rightContX) {
  40153. this.chart.renderer.circle(
  40154. rightContX + this.chart.plotLeft,
  40155. rightContY + this.chart.plotTop,
  40156. 2
  40157. )
  40158. .attr({
  40159. stroke: 'green',
  40160. 'stroke-width': 2,
  40161. fill: 'none',
  40162. zIndex: 9
  40163. })
  40164. .add();
  40165. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  40166. rightContY + this.chart.plotTop,
  40167. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  40168. .attr({
  40169. stroke: 'green',
  40170. 'stroke-width': 2,
  40171. zIndex: 9
  40172. })
  40173. .add();
  40174. }
  40175. // */
  40176. const ret = [
  40177. 'C',
  40178. pick(lastPoint.rightContX, lastPoint.plotX, 0),
  40179. pick(lastPoint.rightContY, lastPoint.plotY, 0),
  40180. pick(leftContX, plotX, 0),
  40181. pick(leftContY, plotY, 0),
  40182. plotX,
  40183. plotY
  40184. ];
  40185. // reset for updating series later
  40186. lastPoint.rightContX = lastPoint.rightContY = void 0;
  40187. return ret;
  40188. }
  40189. }
  40190. /**
  40191. * A spline series is a special type of line series, where the segments
  40192. * between the data points are smoothed.
  40193. *
  40194. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  40195. * Spline chart
  40196. * @sample {highstock} stock/demo/spline/
  40197. * Spline chart
  40198. *
  40199. * @extends plotOptions.series
  40200. * @excluding step, boostThreshold, boostBlending
  40201. * @product highcharts highstock
  40202. * @optionparent plotOptions.spline
  40203. */
  40204. SplineSeries.defaultOptions = merge(LineSeries.defaultOptions);
  40205. SeriesRegistry.registerSeriesType('spline', SplineSeries);
  40206. /* *
  40207. *
  40208. * Default Export
  40209. *
  40210. * */
  40211. /* *
  40212. *
  40213. * API Options
  40214. *
  40215. * */
  40216. /**
  40217. * A `spline` series. If the [type](#series.spline.type) option is
  40218. * not specified, it is inherited from [chart.type](#chart.type).
  40219. *
  40220. * @extends series,plotOptions.spline
  40221. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  40222. * @product highcharts highstock
  40223. * @apioption series.spline
  40224. */
  40225. /**
  40226. * An array of data points for the series. For the `spline` series type,
  40227. * points can be given in the following ways:
  40228. *
  40229. * 1. An array of numerical values. In this case, the numerical values will be
  40230. * interpreted as `y` options. The `x` values will be automatically
  40231. * calculated, either starting at 0 and incremented by 1, or from
  40232. * `pointStart` and `pointInterval` given in the series options. If the axis
  40233. * has categories, these will be used. Example:
  40234. * ```js
  40235. * data: [0, 5, 3, 5]
  40236. * ```
  40237. *
  40238. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40239. * `x,y`. If the first value is a string, it is applied as the name of the
  40240. * point, and the `x` value is inferred.
  40241. * ```js
  40242. * data: [
  40243. * [0, 9],
  40244. * [1, 2],
  40245. * [2, 8]
  40246. * ]
  40247. * ```
  40248. *
  40249. * 3. An array of objects with named values. The following snippet shows only a
  40250. * few settings, see the complete options set below. If the total number of
  40251. * data points exceeds the series'
  40252. * [turboThreshold](#series.spline.turboThreshold),
  40253. * this option is not available.
  40254. * ```js
  40255. * data: [{
  40256. * x: 1,
  40257. * y: 9,
  40258. * name: "Point2",
  40259. * color: "#00FF00"
  40260. * }, {
  40261. * x: 1,
  40262. * y: 0,
  40263. * name: "Point1",
  40264. * color: "#FF00FF"
  40265. * }]
  40266. * ```
  40267. *
  40268. * @sample {highcharts} highcharts/chart/reflow-true/
  40269. * Numerical values
  40270. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40271. * Arrays of numeric x and y
  40272. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40273. * Arrays of datetime x and y
  40274. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40275. * Arrays of point.name and y
  40276. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40277. * Config objects
  40278. *
  40279. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40280. * @extends series.line.data
  40281. * @product highcharts highstock
  40282. * @apioption series.spline.data
  40283. */
  40284. ''; // adds doclets above intro transpilat
  40285. return SplineSeries;
  40286. });
  40287. _registerModule(_modules, 'Series/AreaSpline/AreaSplineSeries.js', [_modules['Series/Spline/SplineSeries.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SplineSeries, SeriesRegistry, U) {
  40288. /* *
  40289. *
  40290. * (c) 2010-2021 Torstein Honsi
  40291. *
  40292. * License: www.highcharts.com/license
  40293. *
  40294. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40295. *
  40296. * */
  40297. const { area: AreaSeries, area: { prototype: areaProto } } = SeriesRegistry.seriesTypes;
  40298. const { extend, merge } = U;
  40299. /* *
  40300. *
  40301. * Class
  40302. *
  40303. * */
  40304. /**
  40305. * AreaSpline series type.
  40306. *
  40307. * @private
  40308. * @class
  40309. * @name Highcharts.seriesTypes.areaspline
  40310. *
  40311. * @augments Highcharts.Series
  40312. */
  40313. class AreaSplineSeries extends SplineSeries {
  40314. constructor() {
  40315. /* *
  40316. *
  40317. * Static Properties
  40318. *
  40319. * */
  40320. super(...arguments);
  40321. /* *
  40322. *
  40323. * Properties
  40324. *
  40325. * */
  40326. this.data = void 0;
  40327. this.points = void 0;
  40328. this.options = void 0;
  40329. }
  40330. }
  40331. AreaSplineSeries.defaultOptions = merge(SplineSeries.defaultOptions, AreaSeries.defaultOptions);
  40332. extend(AreaSplineSeries.prototype, {
  40333. getGraphPath: areaProto.getGraphPath,
  40334. getStackPoints: areaProto.getStackPoints,
  40335. drawGraph: areaProto.drawGraph
  40336. });
  40337. SeriesRegistry.registerSeriesType('areaspline', AreaSplineSeries);
  40338. /* *
  40339. *
  40340. * Default Export
  40341. *
  40342. * */
  40343. /* *
  40344. *
  40345. * API Options
  40346. *
  40347. * */
  40348. /**
  40349. * The area spline series is an area series where the graph between the
  40350. * points is smoothed into a spline.
  40351. *
  40352. * @sample {highcharts} highcharts/demo/areaspline/
  40353. * Area spline chart
  40354. * @sample {highstock} stock/demo/areaspline/
  40355. * Area spline chart
  40356. *
  40357. * @extends plotOptions.area
  40358. * @excluding step, boostThreshold, boostBlending
  40359. * @product highcharts highstock
  40360. * @apioption plotOptions.areaspline
  40361. */
  40362. /**
  40363. * @see [fillColor](#plotOptions.areaspline.fillColor)
  40364. * @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
  40365. *
  40366. * @apioption plotOptions.areaspline.color
  40367. */
  40368. /**
  40369. * @see [color](#plotOptions.areaspline.color)
  40370. * @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
  40371. *
  40372. * @apioption plotOptions.areaspline.fillColor
  40373. */
  40374. /**
  40375. * @see [color](#plotOptions.areaspline.color)
  40376. * @see [fillColor](#plotOptions.areaspline.fillColor)
  40377. *
  40378. * @default 0.75
  40379. * @apioption plotOptions.areaspline.fillOpacity
  40380. */
  40381. /**
  40382. * A `areaspline` series. If the [type](#series.areaspline.type) option
  40383. * is not specified, it is inherited from [chart.type](#chart.type).
  40384. *
  40385. *
  40386. * @extends series,plotOptions.areaspline
  40387. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  40388. * @product highcharts highstock
  40389. * @apioption series.areaspline
  40390. */
  40391. /**
  40392. * @see [fillColor](#series.areaspline.fillColor)
  40393. * @see [fillOpacity](#series.areaspline.fillOpacity)
  40394. *
  40395. * @apioption series.areaspline.color
  40396. */
  40397. /**
  40398. * An array of data points for the series. For the `areaspline` series
  40399. * type, points can be given in the following ways:
  40400. *
  40401. * 1. An array of numerical values. In this case, the numerical values will be
  40402. * interpreted as `y` options. The `x` values will be automatically
  40403. * calculated, either starting at 0 and incremented by 1, or from
  40404. * `pointStart` and `pointInterval` given in the series options. If the axis
  40405. * has categories, these will be used. Example:
  40406. * ```js
  40407. * data: [0, 5, 3, 5]
  40408. * ```
  40409. *
  40410. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40411. * `x,y`. If the first value is a string, it is applied as the name of the
  40412. * point, and the `x` value is inferred.
  40413. * ```js
  40414. * data: [
  40415. * [0, 10],
  40416. * [1, 9],
  40417. * [2, 3]
  40418. * ]
  40419. * ```
  40420. *
  40421. * 3. An array of objects with named values. The following snippet shows only a
  40422. * few settings, see the complete options set below. If the total number of
  40423. * data points exceeds the series'
  40424. * [turboThreshold](#series.areaspline.turboThreshold), this option is not
  40425. * available.
  40426. * ```js
  40427. * data: [{
  40428. * x: 1,
  40429. * y: 4,
  40430. * name: "Point2",
  40431. * color: "#00FF00"
  40432. * }, {
  40433. * x: 1,
  40434. * y: 4,
  40435. * name: "Point1",
  40436. * color: "#FF00FF"
  40437. * }]
  40438. * ```
  40439. *
  40440. * @sample {highcharts} highcharts/chart/reflow-true/
  40441. * Numerical values
  40442. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40443. * Arrays of numeric x and y
  40444. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40445. * Arrays of datetime x and y
  40446. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40447. * Arrays of point.name and y
  40448. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40449. * Config objects
  40450. *
  40451. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40452. * @extends series.line.data
  40453. * @product highcharts highstock
  40454. * @apioption series.areaspline.data
  40455. */
  40456. /**
  40457. * @see [color](#series.areaspline.color)
  40458. * @see [fillOpacity](#series.areaspline.fillOpacity)
  40459. *
  40460. * @apioption series.areaspline.fillColor
  40461. */
  40462. /**
  40463. * @see [color](#series.areaspline.color)
  40464. * @see [fillColor](#series.areaspline.fillColor)
  40465. *
  40466. * @default 0.75
  40467. * @apioption series.areaspline.fillOpacity
  40468. */
  40469. ''; // adds doclets above into transpilat
  40470. return AreaSplineSeries;
  40471. });
  40472. _registerModule(_modules, 'Series/Column/ColumnSeriesDefaults.js', [], function () {
  40473. /* *
  40474. *
  40475. * (c) 2010-2021 Torstein Honsi
  40476. *
  40477. * License: www.highcharts.com/license
  40478. *
  40479. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40480. *
  40481. * */
  40482. /* *
  40483. *
  40484. * API Options
  40485. *
  40486. * */
  40487. /**
  40488. * Column series display one column per value along an X axis.
  40489. *
  40490. * @sample {highcharts} highcharts/demo/column-basic/
  40491. * Column chart
  40492. * @sample {highstock} stock/demo/column/
  40493. * Column chart
  40494. *
  40495. * @extends plotOptions.line
  40496. * @excluding connectEnds, connectNulls, gapSize, gapUnit, linecap,
  40497. * lineWidth, marker, step, useOhlcData
  40498. * @product highcharts highstock
  40499. * @optionparent plotOptions.column
  40500. */
  40501. const ColumnSeriesDefaults = {
  40502. /**
  40503. * The corner radius of the border surrounding each column or bar. A number
  40504. * signifies pixels. A percentage string, like for example `50%`, signifies
  40505. * a relative size. For columns this is relative to the column width, for
  40506. * pies it is relative to the radius and the inner radius.
  40507. *
  40508. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  40509. * Rounded columns
  40510. * @sample highcharts/plotoptions/series-border-radius
  40511. * Column and pie with rounded border
  40512. *
  40513. * @type {number|string|Highcharts.BorderRadiusOptionsObject}
  40514. * @product highcharts highstock gantt
  40515. */
  40516. borderRadius: 3,
  40517. /**
  40518. * When using automatic point colors pulled from the global
  40519. * [colors](colors) or series-specific
  40520. * [plotOptions.column.colors](series.colors) collections, this option
  40521. * determines whether the chart should receive one color per series or
  40522. * one color per point.
  40523. *
  40524. * In styled mode, the `colors` or `series.colors` arrays are not
  40525. * supported, and instead this option gives the points individual color
  40526. * class names on the form `highcharts-color-{n}`.
  40527. *
  40528. * @see [series colors](#plotOptions.column.colors)
  40529. *
  40530. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
  40531. * False by default
  40532. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
  40533. * True
  40534. *
  40535. * @type {boolean}
  40536. * @default false
  40537. * @since 2.0
  40538. * @product highcharts highstock gantt
  40539. * @apioption plotOptions.column.colorByPoint
  40540. */
  40541. /**
  40542. * A series specific or series type specific color set to apply instead
  40543. * of the global [colors](#colors) when [colorByPoint](
  40544. * #plotOptions.column.colorByPoint) is true.
  40545. *
  40546. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  40547. * @since 3.0
  40548. * @product highcharts highstock gantt
  40549. * @apioption plotOptions.column.colors
  40550. */
  40551. /**
  40552. * When `true`, the columns will center in the category, ignoring null
  40553. * or missing points. When `false`, space will be reserved for null or
  40554. * missing points.
  40555. *
  40556. * @sample {highcharts} highcharts/series-column/centerincategory/
  40557. * Center in category
  40558. *
  40559. * @since 8.0.1
  40560. * @product highcharts highstock gantt
  40561. */
  40562. centerInCategory: false,
  40563. /**
  40564. * Padding between each value groups, in x axis units.
  40565. *
  40566. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
  40567. * 0.2 by default
  40568. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
  40569. * No group padding - all columns are evenly spaced
  40570. *
  40571. * @product highcharts highstock gantt
  40572. */
  40573. groupPadding: 0.2,
  40574. /**
  40575. * Whether to group non-stacked columns or to let them render
  40576. * independent of each other. Non-grouped columns will be laid out
  40577. * individually and overlap each other.
  40578. *
  40579. * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
  40580. * Grouping disabled
  40581. * @sample {highstock} highcharts/plotoptions/column-grouping-false/
  40582. * Grouping disabled
  40583. *
  40584. * @type {boolean}
  40585. * @default true
  40586. * @since 2.3.0
  40587. * @product highcharts highstock gantt
  40588. * @apioption plotOptions.column.grouping
  40589. */
  40590. /** @ignore-option */
  40591. marker: null,
  40592. /**
  40593. * The maximum allowed pixel width for a column, translated to the
  40594. * height of a bar in a bar chart. This prevents the columns from
  40595. * becoming too wide when there is a small number of points in the
  40596. * chart.
  40597. *
  40598. * @see [pointWidth](#plotOptions.column.pointWidth)
  40599. *
  40600. * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
  40601. * Limited to 50
  40602. * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
  40603. * Limited to 50
  40604. *
  40605. * @type {number}
  40606. * @since 4.1.8
  40607. * @product highcharts highstock gantt
  40608. * @apioption plotOptions.column.maxPointWidth
  40609. */
  40610. /**
  40611. * Padding between each column or bar, in x axis units.
  40612. *
  40613. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
  40614. * 0.1 by default
  40615. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
  40616. * 0.25
  40617. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
  40618. * 0 for tightly packed columns
  40619. *
  40620. * @product highcharts highstock gantt
  40621. */
  40622. pointPadding: 0.1,
  40623. /**
  40624. * A pixel value specifying a fixed width for each column or bar point.
  40625. * When set to `undefined`, the width is calculated from the
  40626. * `pointPadding` and `groupPadding`. The width effects the dimension
  40627. * that is not based on the point value. For column series it is the
  40628. * hoizontal length and for bar series it is the vertical length.
  40629. *
  40630. * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
  40631. *
  40632. * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
  40633. * 20px wide columns regardless of chart width or the amount of
  40634. * data points
  40635. *
  40636. * @type {number}
  40637. * @since 1.2.5
  40638. * @product highcharts highstock gantt
  40639. * @apioption plotOptions.column.pointWidth
  40640. */
  40641. /**
  40642. * A pixel value specifying a fixed width for the column or bar.
  40643. * Overrides pointWidth on the series.
  40644. *
  40645. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  40646. *
  40647. * @type {number}
  40648. * @default undefined
  40649. * @since 7.0.0
  40650. * @product highcharts highstock gantt
  40651. * @apioption series.column.data.pointWidth
  40652. */
  40653. /**
  40654. * The minimal height for a column or width for a bar. By default,
  40655. * 0 values are not shown. To visualize a 0 (or close to zero) point,
  40656. * set the minimal point length to a pixel value like 3\. In stacked
  40657. * column charts, minPointLength might not be respected for tightly
  40658. * packed values.
  40659. *
  40660. * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
  40661. * Zero base value
  40662. * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
  40663. * Positive and negative close to zero values
  40664. *
  40665. * @product highcharts highstock gantt
  40666. */
  40667. minPointLength: 0,
  40668. /**
  40669. * When the series contains less points than the crop threshold, all
  40670. * points are drawn, event if the points fall outside the visible plot
  40671. * area at the current zoom. The advantage of drawing all points
  40672. * (including markers and columns), is that animation is performed on
  40673. * updates. On the other hand, when the series contains more points than
  40674. * the crop threshold, the series data is cropped to only contain points
  40675. * that fall within the plot area. The advantage of cropping away
  40676. * invisible points is to increase performance on large series.
  40677. *
  40678. * @product highcharts highstock gantt
  40679. */
  40680. cropThreshold: 50,
  40681. /**
  40682. * The X axis range that each point is valid for. This determines the
  40683. * width of the column. On a categorized axis, the range will be 1
  40684. * by default (one category unit). On linear and datetime axes, the
  40685. * range will be computed as the distance between the two closest data
  40686. * points.
  40687. *
  40688. * The default `null` means it is computed automatically, but this
  40689. * option can be used to override the automatic value.
  40690. *
  40691. * This option is set by default to 1 if data sorting is enabled.
  40692. *
  40693. * @sample {highcharts} highcharts/plotoptions/column-pointrange/
  40694. * Set the point range to one day on a data set with one week
  40695. * between the points
  40696. *
  40697. * @type {number|null}
  40698. * @since 2.3
  40699. * @product highcharts highstock gantt
  40700. */
  40701. pointRange: null,
  40702. states: {
  40703. /**
  40704. * Options for the hovered point. These settings override the normal
  40705. * state options when a point is moused over or touched.
  40706. *
  40707. * @extends plotOptions.series.states.hover
  40708. * @excluding halo, lineWidth, lineWidthPlus, marker
  40709. * @product highcharts highstock gantt
  40710. */
  40711. hover: {
  40712. /** @ignore-option */
  40713. halo: false,
  40714. /**
  40715. * A specific border color for the hovered point. Defaults to
  40716. * inherit the normal state border color.
  40717. *
  40718. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40719. * @product highcharts gantt
  40720. * @apioption plotOptions.column.states.hover.borderColor
  40721. */
  40722. /**
  40723. * A specific color for the hovered point.
  40724. *
  40725. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40726. * @product highcharts gantt
  40727. * @apioption plotOptions.column.states.hover.color
  40728. */
  40729. /**
  40730. * How much to brighten the point on interaction. Requires the
  40731. * main color to be defined in hex or rgb(a) format.
  40732. *
  40733. * In styled mode, the hover brightening is by default replaced
  40734. * with a fill-opacity set in the `.highcharts-point:hover`
  40735. * rule.
  40736. *
  40737. * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
  40738. * Brighten by 0.5
  40739. *
  40740. * @product highcharts highstock gantt
  40741. */
  40742. brightness: 0.1
  40743. },
  40744. /**
  40745. * Options for the selected point. These settings override the
  40746. * normal state options when a point is selected.
  40747. *
  40748. * @extends plotOptions.series.states.select
  40749. * @excluding halo, lineWidth, lineWidthPlus, marker
  40750. * @product highcharts highstock gantt
  40751. */
  40752. select: {
  40753. /**
  40754. * A specific color for the selected point.
  40755. *
  40756. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40757. * @default #cccccc
  40758. * @product highcharts highstock gantt
  40759. */
  40760. color: "#cccccc" /* Palette.neutralColor20 */,
  40761. /**
  40762. * A specific border color for the selected point.
  40763. *
  40764. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40765. * @default #000000
  40766. * @product highcharts highstock gantt
  40767. */
  40768. borderColor: "#000000" /* Palette.neutralColor100 */
  40769. }
  40770. },
  40771. dataLabels: {
  40772. align: void 0,
  40773. verticalAlign: void 0,
  40774. /**
  40775. * The y position offset of the label relative to the point in
  40776. * pixels.
  40777. *
  40778. * @type {number}
  40779. */
  40780. y: void 0
  40781. },
  40782. // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
  40783. /** @ignore-option */
  40784. startFromThreshold: true,
  40785. stickyTracking: false,
  40786. tooltip: {
  40787. distance: 6
  40788. },
  40789. /**
  40790. * The Y axis value to serve as the base for the columns, for
  40791. * distinguishing between values above and below a threshold. If `null`,
  40792. * the columns extend from the padding Y axis minimum.
  40793. *
  40794. * @type {number|null}
  40795. * @since 2.0
  40796. * @product highcharts
  40797. */
  40798. threshold: 0,
  40799. /**
  40800. * The width of the border surrounding each column or bar. Defaults to
  40801. * `1` when there is room for a border, but to `0` when the columns are
  40802. * so dense that a border would cover the next column.
  40803. *
  40804. * In styled mode, the stroke width can be set with the
  40805. * `.highcharts-point` rule.
  40806. *
  40807. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  40808. * 2px black border
  40809. *
  40810. * @type {number}
  40811. * @default undefined
  40812. * @product highcharts highstock gantt
  40813. * @apioption plotOptions.column.borderWidth
  40814. */
  40815. /**
  40816. * The color of the border surrounding each column or bar.
  40817. *
  40818. * In styled mode, the border stroke can be set with the
  40819. * `.highcharts-point` rule.
  40820. *
  40821. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  40822. * Dark gray border
  40823. *
  40824. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40825. * @default #ffffff
  40826. * @product highcharts highstock gantt
  40827. */
  40828. borderColor: "#ffffff" /* Palette.backgroundColor */
  40829. };
  40830. /**
  40831. * A `column` series. If the [type](#series.column.type) option is
  40832. * not specified, it is inherited from [chart.type](#chart.type).
  40833. *
  40834. * @extends series,plotOptions.column
  40835. * @excluding connectNulls, dataParser, dataURL, gapSize, gapUnit, linecap,
  40836. * lineWidth, marker, connectEnds, step
  40837. * @product highcharts highstock
  40838. * @apioption series.column
  40839. */
  40840. /**
  40841. * An array of data points for the series. For the `column` series type,
  40842. * points can be given in the following ways:
  40843. *
  40844. * 1. An array of numerical values. In this case, the numerical values will be
  40845. * interpreted as `y` options. The `x` values will be automatically
  40846. * calculated, either starting at 0 and incremented by 1, or from
  40847. * `pointStart` and `pointInterval` given in the series options. If the axis
  40848. * has categories, these will be used. Example:
  40849. * ```js
  40850. * data: [0, 5, 3, 5]
  40851. * ```
  40852. *
  40853. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40854. * `x,y`. If the first value is a string, it is applied as the name of the
  40855. * point, and the `x` value is inferred.
  40856. * ```js
  40857. * data: [
  40858. * [0, 6],
  40859. * [1, 2],
  40860. * [2, 6]
  40861. * ]
  40862. * ```
  40863. *
  40864. * 3. An array of objects with named values. The following snippet shows only a
  40865. * few settings, see the complete options set below. If the total number of
  40866. * data points exceeds the series'
  40867. * [turboThreshold](#series.column.turboThreshold), this option is not
  40868. * available.
  40869. * ```js
  40870. * data: [{
  40871. * x: 1,
  40872. * y: 9,
  40873. * name: "Point2",
  40874. * color: "#00FF00"
  40875. * }, {
  40876. * x: 1,
  40877. * y: 6,
  40878. * name: "Point1",
  40879. * color: "#FF00FF"
  40880. * }]
  40881. * ```
  40882. *
  40883. * @sample {highcharts} highcharts/chart/reflow-true/
  40884. * Numerical values
  40885. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40886. * Arrays of numeric x and y
  40887. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40888. * Arrays of datetime x and y
  40889. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40890. * Arrays of point.name and y
  40891. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40892. * Config objects
  40893. *
  40894. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40895. * @extends series.line.data
  40896. * @excluding marker
  40897. * @product highcharts highstock
  40898. * @apioption series.column.data
  40899. */
  40900. /**
  40901. * The color of the border surrounding the column or bar.
  40902. *
  40903. * In styled mode, the border stroke can be set with the `.highcharts-point`
  40904. * rule.
  40905. *
  40906. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  40907. * Dark gray border
  40908. *
  40909. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40910. * @product highcharts highstock
  40911. * @apioption series.column.data.borderColor
  40912. */
  40913. /**
  40914. * The width of the border surrounding the column or bar.
  40915. *
  40916. * In styled mode, the stroke width can be set with the `.highcharts-point`
  40917. * rule.
  40918. *
  40919. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  40920. * 2px black border
  40921. *
  40922. * @type {number}
  40923. * @product highcharts highstock
  40924. * @apioption series.column.data.borderWidth
  40925. */
  40926. /**
  40927. * A name for the dash style to use for the column or bar. Overrides
  40928. * dashStyle on the series.
  40929. *
  40930. * In styled mode, the stroke dash-array can be set with the same classes as
  40931. * listed under [data.color](#series.column.data.color).
  40932. *
  40933. * @see [series.pointWidth](#plotOptions.column.dashStyle)
  40934. *
  40935. * @type {Highcharts.DashStyleValue}
  40936. * @apioption series.column.data.dashStyle
  40937. */
  40938. /**
  40939. * A pixel value specifying a fixed width for the column or bar. Overrides
  40940. * pointWidth on the series. The width effects the dimension that is not based
  40941. * on the point value.
  40942. *
  40943. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  40944. *
  40945. * @type {number}
  40946. * @apioption series.column.data.pointWidth
  40947. */
  40948. /**
  40949. * @excluding halo, lineWidth, lineWidthPlus, marker
  40950. * @product highcharts highstock
  40951. * @apioption series.column.states.hover
  40952. */
  40953. /**
  40954. * @excluding halo, lineWidth, lineWidthPlus, marker
  40955. * @product highcharts highstock
  40956. * @apioption series.column.states.select
  40957. */
  40958. ''; // keeps doclets above in JS file
  40959. /* *
  40960. *
  40961. * Default Export
  40962. *
  40963. * */
  40964. return ColumnSeriesDefaults;
  40965. });
  40966. _registerModule(_modules, 'Series/Column/ColumnSeries.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Color/Color.js'], _modules['Series/Column/ColumnSeriesDefaults.js'], _modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (A, Color, ColumnSeriesDefaults, H, Series, SeriesRegistry, U) {
  40967. /* *
  40968. *
  40969. * (c) 2010-2021 Torstein Honsi
  40970. *
  40971. * License: www.highcharts.com/license
  40972. *
  40973. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40974. *
  40975. * */
  40976. const { animObject } = A;
  40977. const { parse: color } = Color;
  40978. const { hasTouch, noop } = H;
  40979. const { clamp, defined, extend, fireEvent, isArray, isNumber, merge, pick, objectEach, relativeLength } = U;
  40980. /* *
  40981. *
  40982. * Class
  40983. *
  40984. * */
  40985. /**
  40986. * The column series type.
  40987. *
  40988. * @private
  40989. * @class
  40990. * @name Highcharts.seriesTypes.column
  40991. *
  40992. * @augments Highcharts.Series
  40993. */
  40994. class ColumnSeries extends Series {
  40995. constructor() {
  40996. /* *
  40997. *
  40998. * Static Properties
  40999. *
  41000. * */
  41001. super(...arguments);
  41002. /* *
  41003. *
  41004. * Properties
  41005. *
  41006. * */
  41007. this.borderWidth = void 0;
  41008. this.data = void 0;
  41009. this.group = void 0;
  41010. this.options = void 0;
  41011. this.points = void 0;
  41012. /* eslint-enable valid-jsdoc */
  41013. }
  41014. /* *
  41015. *
  41016. * Functions
  41017. *
  41018. * */
  41019. /* eslint-disable valid-jsdoc */
  41020. /**
  41021. * Animate the column heights one by one from zero.
  41022. *
  41023. * @private
  41024. * @function Highcharts.seriesTypes.column#animate
  41025. *
  41026. * @param {boolean} init
  41027. * Whether to initialize the animation or run it
  41028. */
  41029. animate(init) {
  41030. const series = this, yAxis = this.yAxis, yAxisPos = yAxis.pos, options = series.options, inverted = this.chart.inverted, attr = {}, translateProp = inverted ?
  41031. 'translateX' :
  41032. 'translateY';
  41033. let translateStart, translatedThreshold;
  41034. if (init) {
  41035. attr.scaleY = 0.001;
  41036. translatedThreshold = clamp(yAxis.toPixels(options.threshold), yAxisPos, yAxisPos + yAxis.len);
  41037. if (inverted) {
  41038. attr.translateX = translatedThreshold - yAxis.len;
  41039. }
  41040. else {
  41041. attr.translateY = translatedThreshold;
  41042. }
  41043. // apply finnal clipping (used in Highcharts Stock) (#7083)
  41044. // animation is done by scaleY, so cliping is for panes
  41045. if (series.clipBox) {
  41046. series.setClip();
  41047. }
  41048. series.group.attr(attr);
  41049. }
  41050. else { // run the animation
  41051. translateStart = Number(series.group.attr(translateProp));
  41052. series.group.animate({ scaleY: 1 }, extend(animObject(series.options.animation), {
  41053. // Do the scale synchronously to ensure smooth
  41054. // updating (#5030, #7228)
  41055. step: function (val, fx) {
  41056. if (series.group) {
  41057. attr[translateProp] = translateStart +
  41058. fx.pos * (yAxisPos - translateStart);
  41059. series.group.attr(attr);
  41060. }
  41061. }
  41062. }));
  41063. }
  41064. }
  41065. /**
  41066. * Initialize the series. Extends the basic Series.init method by
  41067. * marking other series of the same type as dirty.
  41068. *
  41069. * @private
  41070. * @function Highcharts.seriesTypes.column#init
  41071. */
  41072. init(chart, options) {
  41073. super.init.apply(this, arguments);
  41074. const series = this;
  41075. chart = series.chart;
  41076. // if the series is added dynamically, force redraw of other
  41077. // series affected by a new column
  41078. if (chart.hasRendered) {
  41079. chart.series.forEach(function (otherSeries) {
  41080. if (otherSeries.type === series.type) {
  41081. otherSeries.isDirty = true;
  41082. }
  41083. });
  41084. }
  41085. }
  41086. /**
  41087. * Return the width and x offset of the columns adjusted for grouping,
  41088. * groupPadding, pointPadding, pointWidth etc.
  41089. *
  41090. * @private
  41091. * @function Highcharts.seriesTypes.column#getColumnMetrics
  41092. */
  41093. getColumnMetrics() {
  41094. const series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, reversedStacks = xAxis.options.reversedStacks,
  41095. // Keep backward compatibility: reversed xAxis had reversed
  41096. // stacks
  41097. reverseStacks = (xAxis.reversed && !reversedStacks) ||
  41098. (!xAxis.reversed && reversedStacks), stackGroups = {};
  41099. let stackKey, columnCount = 0;
  41100. // Get the total number of column type series. This is called on
  41101. // every series. Consider moving this logic to a chart.orderStacks()
  41102. // function and call it on init, addSeries and removeSeries
  41103. if (options.grouping === false) {
  41104. columnCount = 1;
  41105. }
  41106. else {
  41107. series.chart.series.forEach(function (otherSeries) {
  41108. const otherYAxis = otherSeries.yAxis, otherOptions = otherSeries.options;
  41109. let columnIndex;
  41110. if (otherSeries.type === series.type &&
  41111. (otherSeries.visible ||
  41112. !series.chart.options.chart.ignoreHiddenSeries) &&
  41113. yAxis.len === otherYAxis.len &&
  41114. yAxis.pos === otherYAxis.pos) { // #642, #2086
  41115. if (otherOptions.stacking &&
  41116. otherOptions.stacking !== 'group') {
  41117. stackKey = otherSeries.stackKey;
  41118. if (typeof stackGroups[stackKey] ===
  41119. 'undefined') {
  41120. stackGroups[stackKey] = columnCount++;
  41121. }
  41122. columnIndex = stackGroups[stackKey];
  41123. }
  41124. else if (otherOptions.grouping !== false) { // #1162
  41125. columnIndex = columnCount++;
  41126. }
  41127. otherSeries.columnIndex = columnIndex;
  41128. }
  41129. });
  41130. }
  41131. const categoryWidth = Math.min(Math.abs(xAxis.transA) * ((xAxis.ordinal && xAxis.ordinal.slope) ||
  41132. options.pointRange ||
  41133. xAxis.closestPointRange ||
  41134. xAxis.tickInterval ||
  41135. 1), // #2610
  41136. xAxis.len // #1535
  41137. ), groupPadding = categoryWidth * options.groupPadding, groupWidth = categoryWidth - 2 * groupPadding, pointOffsetWidth = groupWidth / (columnCount || 1), pointWidth = Math.min(options.maxPointWidth || xAxis.len, pick(options.pointWidth, pointOffsetWidth * (1 - 2 * options.pointPadding))), pointPadding = (pointOffsetWidth - pointWidth) / 2,
  41138. // #1251, #3737
  41139. colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0), pointXOffset = pointPadding +
  41140. (groupPadding +
  41141. colIndex * pointOffsetWidth -
  41142. (categoryWidth / 2)) * (reverseStacks ? -1 : 1);
  41143. // Save it for reading in linked series (Error bars particularly)
  41144. series.columnMetrics = {
  41145. width: pointWidth,
  41146. offset: pointXOffset,
  41147. paddedWidth: pointOffsetWidth,
  41148. columnCount
  41149. };
  41150. return series.columnMetrics;
  41151. }
  41152. /**
  41153. * Make the columns crisp. The edges are rounded to the nearest full
  41154. * pixel.
  41155. *
  41156. * @private
  41157. * @function Highcharts.seriesTypes.column#crispCol
  41158. */
  41159. crispCol(x, y, w, h) {
  41160. const chart = this.chart, borderWidth = this.borderWidth, xCrisp = -(borderWidth % 2 ? 0.5 : 0);
  41161. let right, yCrisp = borderWidth % 2 ? 0.5 : 1;
  41162. // Horizontal. We need to first compute the exact right edge, then
  41163. // round it and compute the width from there.
  41164. if (this.options.crisp) {
  41165. right = Math.round(x + w) + xCrisp;
  41166. x = Math.round(x) + xCrisp;
  41167. w = right - x;
  41168. }
  41169. // Vertical
  41170. const bottom = Math.round(y + h) + yCrisp, fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
  41171. y = Math.round(y) + yCrisp;
  41172. h = bottom - y;
  41173. // Top edges are exceptions
  41174. if (fromTop && h) { // #5146
  41175. y -= 1;
  41176. h += 1;
  41177. }
  41178. return {
  41179. x: x,
  41180. y: y,
  41181. width: w,
  41182. height: h
  41183. };
  41184. }
  41185. /**
  41186. * Adjust for missing columns, according to the `centerInCategory`
  41187. * option. Missing columns are either single points or stacks where the
  41188. * point or points are either missing or null.
  41189. *
  41190. * @private
  41191. * @function Highcharts.seriesTypes.column#adjustForMissingColumns
  41192. * @param {number} x
  41193. * The x coordinate of the column, left side
  41194. *
  41195. * @param {number} pointWidth
  41196. * The pointWidth, already computed upstream
  41197. *
  41198. * @param {Highcharts.ColumnPoint} point
  41199. * The point instance
  41200. *
  41201. * @param {Highcharts.ColumnMetricsObject} metrics
  41202. * The series-wide column metrics
  41203. *
  41204. * @return {number}
  41205. * The adjusted x position, or the original if not adjusted
  41206. */
  41207. adjustForMissingColumns(x, pointWidth, point, metrics) {
  41208. const stacking = this.options.stacking;
  41209. if (!point.isNull && metrics.columnCount > 1) {
  41210. const reversedStacks = this.yAxis.options.reversedStacks;
  41211. let indexInCategory = 0, totalInCategory = reversedStacks ? 0 : -metrics.columnCount;
  41212. // Loop over all the stacks on the Y axis. When stacking is enabled,
  41213. // these are real point stacks. When stacking is not enabled, but
  41214. // `centerInCategory` is true, there is one stack handling the
  41215. // grouping of points in each category. This is done in the
  41216. // `setGroupedPoints` function.
  41217. objectEach(this.yAxis.stacking && this.yAxis.stacking.stacks, (stack) => {
  41218. if (typeof point.x === 'number') {
  41219. const stackItem = stack[point.x.toString()];
  41220. if (stackItem) {
  41221. const pointValues = stackItem.points[this.index];
  41222. // If true `stacking` is enabled, count the total
  41223. // number of non-null stacks in the category, and
  41224. // note which index this point is within those
  41225. // stacks.
  41226. if (stacking) {
  41227. if (pointValues) {
  41228. indexInCategory = totalInCategory;
  41229. }
  41230. if (stackItem.hasValidPoints) {
  41231. reversedStacks ? // #16169
  41232. totalInCategory++ : totalInCategory--;
  41233. }
  41234. // If `stacking` is not enabled, look for the index
  41235. }
  41236. else if (isArray(pointValues)) {
  41237. // If there are multiple points with the same X
  41238. // then gather all series in category, and
  41239. // assign index
  41240. let seriesIndexes = Object
  41241. .keys(stackItem.points)
  41242. .filter((pointKey) =>
  41243. // Filter out duplicate X's
  41244. !pointKey.match(',') &&
  41245. // Filter out null points
  41246. stackItem.points[pointKey] &&
  41247. stackItem.points[pointKey].length > 1)
  41248. .map(parseFloat)
  41249. .sort((a, b) => b - a);
  41250. indexInCategory = seriesIndexes.indexOf(this.index);
  41251. totalInCategory = seriesIndexes.length;
  41252. }
  41253. }
  41254. }
  41255. });
  41256. // Compute the adjusted x position
  41257. const boxWidth = (totalInCategory - 1) * metrics.paddedWidth +
  41258. pointWidth;
  41259. x = (point.plotX || 0) + boxWidth / 2 - pointWidth -
  41260. indexInCategory * metrics.paddedWidth;
  41261. }
  41262. return x;
  41263. }
  41264. /**
  41265. * Translate each point to the plot area coordinate system and find
  41266. * shape positions
  41267. *
  41268. * @private
  41269. * @function Highcharts.seriesTypes.column#translate
  41270. */
  41271. translate() {
  41272. const series = this, chart = series.chart, options = series.options, dense = series.dense =
  41273. series.closestPointRange * series.xAxis.transA < 2, borderWidth = series.borderWidth = pick(options.borderWidth, dense ? 0 : 1 // #3635
  41274. ), xAxis = series.xAxis, yAxis = series.yAxis, threshold = options.threshold, minPointLength = pick(options.minPointLength, 5), metrics = series.getColumnMetrics(), seriesPointWidth = metrics.width, seriesXOffset = series.pointXOffset = metrics.offset, dataMin = series.dataMin, dataMax = series.dataMax;
  41275. // postprocessed for border width
  41276. let seriesBarW = series.barW =
  41277. Math.max(seriesPointWidth, 1 + 2 * borderWidth), translatedThreshold = series.translatedThreshold =
  41278. yAxis.getThreshold(threshold);
  41279. if (chart.inverted) {
  41280. translatedThreshold -= 0.5; // #3355
  41281. }
  41282. // When the pointPadding is 0, we want the columns to be packed
  41283. // tightly, so we allow individual columns to have individual sizes.
  41284. // When pointPadding is greater, we strive for equal-width columns
  41285. // (#2694).
  41286. if (options.pointPadding) {
  41287. seriesBarW = Math.ceil(seriesBarW);
  41288. }
  41289. Series.prototype.translate.apply(series);
  41290. // Record the new values
  41291. series.points.forEach(function (point) {
  41292. const yBottom = pick(point.yBottom, translatedThreshold), safeDistance = 999 + Math.abs(yBottom), plotX = point.plotX || 0,
  41293. // Don't draw too far outside plot area (#1303, #2241,
  41294. // #4264)
  41295. plotY = clamp(point.plotY, -safeDistance, yAxis.len + safeDistance), stackBox = point.stackBox;
  41296. let up, barY = Math.min(plotY, yBottom), barH = Math.max(plotY, yBottom) - barY, pointWidth = seriesPointWidth, barX = plotX + seriesXOffset, barW = seriesBarW;
  41297. // Handle options.minPointLength
  41298. if (minPointLength && Math.abs(barH) < minPointLength) {
  41299. barH = minPointLength;
  41300. up = (!yAxis.reversed && !point.negative) ||
  41301. (yAxis.reversed && point.negative);
  41302. // Reverse zeros if there's no positive value in the series
  41303. // in visible range (#7046)
  41304. if (isNumber(threshold) &&
  41305. isNumber(dataMax) &&
  41306. point.y === threshold &&
  41307. dataMax <= threshold &&
  41308. // and if there's room for it (#7311)
  41309. (yAxis.min || 0) < threshold &&
  41310. // if all points are the same value (i.e zero) not draw
  41311. // as negative points (#10646), but only if there's room
  41312. // for it (#14876)
  41313. (dataMin !== dataMax || (yAxis.max || 0) <= threshold)) {
  41314. up = !up;
  41315. point.negative = !point.negative;
  41316. }
  41317. // If stacked...
  41318. barY = (Math.abs(barY - translatedThreshold) > minPointLength ?
  41319. // ...keep position
  41320. yBottom - minPointLength :
  41321. // #1485, #4051
  41322. translatedThreshold -
  41323. (up ? minPointLength : 0));
  41324. }
  41325. // Handle point.options.pointWidth
  41326. // @todo Handle grouping/stacking too. Calculate offset properly
  41327. if (defined(point.options.pointWidth)) {
  41328. pointWidth = barW =
  41329. Math.ceil(point.options.pointWidth);
  41330. barX -= Math.round((pointWidth - seriesPointWidth) / 2);
  41331. }
  41332. // Adjust for null or missing points
  41333. if (options.centerInCategory) {
  41334. barX = series.adjustForMissingColumns(barX, pointWidth, point, metrics);
  41335. }
  41336. // Cache for access in polar
  41337. point.barX = barX;
  41338. point.pointWidth = pointWidth;
  41339. // Fix the tooltip on center of grouped columns (#1216, #424,
  41340. // #3648)
  41341. point.tooltipPos = chart.inverted ?
  41342. [
  41343. clamp(yAxis.len + yAxis.pos - chart.plotLeft - plotY, yAxis.pos - chart.plotLeft, yAxis.len + yAxis.pos - chart.plotLeft),
  41344. xAxis.len + xAxis.pos - chart.plotTop - barX - barW / 2,
  41345. barH
  41346. ] :
  41347. [
  41348. xAxis.left - chart.plotLeft + barX + barW / 2,
  41349. clamp(plotY + yAxis.pos -
  41350. chart.plotTop, yAxis.pos - chart.plotTop, yAxis.len + yAxis.pos - chart.plotTop),
  41351. barH
  41352. ];
  41353. // Register shape type and arguments to be used in drawPoints. Allow
  41354. // `shapeType` defined on `pointClass` level.
  41355. point.shapeType = series.pointClass.prototype.shapeType ||
  41356. 'roundedRect';
  41357. point.shapeArgs = series.crispCol(barX,
  41358. // #3169, drilldown from null must have a position to work from.
  41359. // #6585, dataLabel should be placed on xAxis, not floating in
  41360. // the middle of the chart.
  41361. point.isNull ? translatedThreshold : barY, barW, point.isNull ? 0 : barH);
  41362. });
  41363. // Fire a specific event after column translate. We could instead apply
  41364. // all the column logic in an `afterTranslate` event handler, but there
  41365. // are so many other series types that use the column translation, that
  41366. // it is more convenient to have a specific event for it.
  41367. fireEvent(this, 'afterColumnTranslate');
  41368. }
  41369. /**
  41370. * Columns have no graph
  41371. *
  41372. * @private
  41373. * @function Highcharts.seriesTypes.column#drawGraph
  41374. */
  41375. drawGraph() {
  41376. this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data');
  41377. }
  41378. /**
  41379. * Get presentational attributes
  41380. *
  41381. * @private
  41382. * @function Highcharts.seriesTypes.column#pointAttribs
  41383. */
  41384. pointAttribs(point, state) {
  41385. const options = this.options, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth';
  41386. let stateOptions, zone, brightness, fill = (point && point.color) || this.color,
  41387. // set to fill when borderColor null:
  41388. stroke = ((point && point[strokeOption]) ||
  41389. options[strokeOption] ||
  41390. fill), dashstyle = (point && point.options.dashStyle) || options.dashStyle, strokeWidth = (point && point[strokeWidthOption]) ||
  41391. options[strokeWidthOption] ||
  41392. this[strokeWidthOption] || 0, opacity = pick(point && point.opacity, options.opacity, 1);
  41393. // Handle zone colors
  41394. if (point && this.zones.length) {
  41395. zone = point.getZone();
  41396. // When zones are present, don't use point.color (#4267).
  41397. // Changed order (#6527), added support for colorAxis (#10670)
  41398. fill = (point.options.color ||
  41399. (zone && (zone.color || point.nonZonedColor)) ||
  41400. this.color);
  41401. if (zone) {
  41402. stroke = zone.borderColor || stroke;
  41403. dashstyle = zone.dashStyle || dashstyle;
  41404. strokeWidth = zone.borderWidth || strokeWidth;
  41405. }
  41406. }
  41407. // Select or hover states
  41408. if (state && point) {
  41409. stateOptions = merge(options.states[state],
  41410. // #6401
  41411. point.options.states &&
  41412. point.options.states[state] ||
  41413. {});
  41414. brightness = stateOptions.brightness;
  41415. fill =
  41416. stateOptions.color || (typeof brightness !== 'undefined' &&
  41417. color(fill)
  41418. .brighten(stateOptions.brightness)
  41419. .get()) || fill;
  41420. stroke = stateOptions[strokeOption] || stroke;
  41421. strokeWidth =
  41422. stateOptions[strokeWidthOption] || strokeWidth;
  41423. dashstyle = stateOptions.dashStyle || dashstyle;
  41424. opacity = pick(stateOptions.opacity, opacity);
  41425. }
  41426. const ret = {
  41427. fill: fill,
  41428. stroke: stroke,
  41429. 'stroke-width': strokeWidth,
  41430. opacity: opacity
  41431. };
  41432. if (dashstyle) {
  41433. ret.dashstyle = dashstyle;
  41434. }
  41435. return ret;
  41436. }
  41437. /**
  41438. * Draw the columns. For bars, the series.group is rotated, so the same
  41439. * coordinates apply for columns and bars. This method is inherited by
  41440. * scatter series.
  41441. *
  41442. * @private
  41443. * @function Highcharts.seriesTypes.column#drawPoints
  41444. */
  41445. drawPoints(points = this.points) {
  41446. const series = this, chart = this.chart, options = series.options, renderer = chart.renderer, animationLimit = options.animationLimit || 250;
  41447. let shapeArgs;
  41448. // draw the columns
  41449. points.forEach(function (point) {
  41450. const plotY = point.plotY;
  41451. let graphic = point.graphic, hasGraphic = !!graphic, verb = graphic && chart.pointCount < animationLimit ?
  41452. 'animate' : 'attr';
  41453. if (isNumber(plotY) && point.y !== null) {
  41454. shapeArgs = point.shapeArgs;
  41455. // When updating a series between 2d and 3d or cartesian and
  41456. // polar, the shape type changes.
  41457. if (graphic && point.hasNewShapeType()) {
  41458. graphic = graphic.destroy();
  41459. }
  41460. // Set starting position for point sliding animation.
  41461. if (series.enabledDataSorting) {
  41462. point.startXPos = series.xAxis.reversed ?
  41463. -(shapeArgs ? (shapeArgs.width || 0) : 0) :
  41464. series.xAxis.width;
  41465. }
  41466. if (!graphic) {
  41467. point.graphic = graphic =
  41468. renderer[point.shapeType](shapeArgs)
  41469. .add(point.group || series.group);
  41470. if (graphic &&
  41471. series.enabledDataSorting &&
  41472. chart.hasRendered &&
  41473. chart.pointCount < animationLimit) {
  41474. graphic.attr({
  41475. x: point.startXPos
  41476. });
  41477. hasGraphic = true;
  41478. verb = 'animate';
  41479. }
  41480. }
  41481. if (graphic && hasGraphic) { // update
  41482. graphic[verb](merge(shapeArgs));
  41483. }
  41484. // Presentational
  41485. if (!chart.styledMode) {
  41486. graphic[verb](series.pointAttribs(point, (point.selected && 'select')))
  41487. .shadow(point.allowShadow !== false && options.shadow);
  41488. }
  41489. if (graphic) {
  41490. graphic.addClass(point.getClassName(), true);
  41491. graphic.attr({
  41492. visibility: point.visible ? 'inherit' : 'hidden'
  41493. });
  41494. }
  41495. }
  41496. else if (graphic) {
  41497. point.graphic = graphic.destroy(); // #1269
  41498. }
  41499. });
  41500. }
  41501. /**
  41502. * Draw the tracker for a point.
  41503. * @private
  41504. */
  41505. drawTracker(points = this.points) {
  41506. const series = this, chart = series.chart, pointer = chart.pointer, onMouseOver = function (e) {
  41507. const point = pointer.getPointFromEvent(e);
  41508. // undefined on graph in scatterchart
  41509. if (typeof point !== 'undefined' &&
  41510. series.options.enableMouseTracking) {
  41511. pointer.isDirectTouch = true;
  41512. point.onMouseOver(e);
  41513. }
  41514. };
  41515. let dataLabels;
  41516. // Add reference to the point
  41517. points.forEach(function (point) {
  41518. dataLabels = (isArray(point.dataLabels) ?
  41519. point.dataLabels :
  41520. (point.dataLabel ? [point.dataLabel] : []));
  41521. if (point.graphic) {
  41522. point.graphic.element.point = point;
  41523. }
  41524. dataLabels.forEach(function (dataLabel) {
  41525. if (dataLabel.div) {
  41526. dataLabel.div.point = point;
  41527. }
  41528. else {
  41529. dataLabel.element.point = point;
  41530. }
  41531. });
  41532. });
  41533. // Add the event listeners, we need to do this only once
  41534. if (!series._hasTracking) {
  41535. series.trackerGroups.forEach(function (key) {
  41536. if (series[key]) {
  41537. // we don't always have dataLabelsGroup
  41538. series[key]
  41539. .addClass('highcharts-tracker')
  41540. .on('mouseover', onMouseOver)
  41541. .on('mouseout', function (e) {
  41542. pointer.onTrackerMouseOut(e);
  41543. });
  41544. if (hasTouch) {
  41545. series[key].on('touchstart', onMouseOver);
  41546. }
  41547. if (!chart.styledMode && series.options.cursor) {
  41548. series[key]
  41549. .css({ cursor: series.options.cursor });
  41550. }
  41551. }
  41552. });
  41553. series._hasTracking = true;
  41554. }
  41555. fireEvent(this, 'afterDrawTracker');
  41556. }
  41557. /**
  41558. * Remove this series from the chart
  41559. *
  41560. * @private
  41561. * @function Highcharts.seriesTypes.column#remove
  41562. */
  41563. remove() {
  41564. const series = this, chart = series.chart;
  41565. // column and bar series affects other series of the same type
  41566. // as they are either stacked or grouped
  41567. if (chart.hasRendered) {
  41568. chart.series.forEach(function (otherSeries) {
  41569. if (otherSeries.type === series.type) {
  41570. otherSeries.isDirty = true;
  41571. }
  41572. });
  41573. }
  41574. Series.prototype.remove.apply(series, arguments);
  41575. }
  41576. }
  41577. ColumnSeries.defaultOptions = merge(Series.defaultOptions, ColumnSeriesDefaults);
  41578. extend(ColumnSeries.prototype, {
  41579. cropShoulder: 0,
  41580. // When tooltip is not shared, this series (and derivatives) requires
  41581. // direct touch/hover. KD-tree does not apply.
  41582. directTouch: true,
  41583. getSymbol: noop,
  41584. // use separate negative stacks, unlike area stacks where a negative
  41585. // point is substracted from previous (#1910)
  41586. negStacks: true,
  41587. trackerGroups: ['group', 'dataLabelsGroup']
  41588. });
  41589. SeriesRegistry.registerSeriesType('column', ColumnSeries);
  41590. /* *
  41591. *
  41592. * Default Export
  41593. *
  41594. * */
  41595. /* *
  41596. *
  41597. * API Declarations
  41598. *
  41599. * */
  41600. /**
  41601. * Adjusted width and x offset of the columns for grouping.
  41602. *
  41603. * @private
  41604. * @interface Highcharts.ColumnMetricsObject
  41605. */ /**
  41606. * Width of the columns.
  41607. * @name Highcharts.ColumnMetricsObject#width
  41608. * @type {number}
  41609. */ /**
  41610. * Offset of the columns.
  41611. * @name Highcharts.ColumnMetricsObject#offset
  41612. * @type {number}
  41613. */
  41614. ''; // detach doclets above
  41615. return ColumnSeries;
  41616. });
  41617. _registerModule(_modules, 'Core/Series/DataLabel.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Templating.js'], _modules['Core/Utilities.js']], function (A, F, U) {
  41618. /* *
  41619. *
  41620. * (c) 2010-2021 Torstein Honsi
  41621. *
  41622. * License: www.highcharts.com/license
  41623. *
  41624. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41625. *
  41626. * */
  41627. const { getDeferredAnimation } = A;
  41628. const { format } = F;
  41629. const { defined, extend, fireEvent, isArray, isString, merge, objectEach, pick, splat } = U;
  41630. /* *
  41631. *
  41632. * Composition
  41633. *
  41634. * */
  41635. /* eslint-disable valid-jsdoc */
  41636. var DataLabel;
  41637. (function (DataLabel) {
  41638. /* *
  41639. *
  41640. * Declarations
  41641. *
  41642. * */
  41643. /* *
  41644. *
  41645. * Constants
  41646. *
  41647. * */
  41648. const composedMembers = [];
  41649. /* *
  41650. *
  41651. * Functions
  41652. *
  41653. * */
  41654. /* eslint-disable valid-jsdoc */
  41655. /**
  41656. * Align each individual data label.
  41657. * @private
  41658. */
  41659. function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
  41660. const series = this, chart = this.chart, inverted = this.isCartesian && chart.inverted, enabledDataSorting = this.enabledDataSorting, plotX = point.plotX, plotY = point.plotY, rotation = options.rotation, align = options.align, isInsidePlot = defined(plotX) &&
  41661. defined(plotY) &&
  41662. chart.isInsidePlot(plotX, Math.round(plotY), {
  41663. inverted,
  41664. paneCoordinates: true,
  41665. series
  41666. }), setStartPos = (alignOptions) => {
  41667. if (enabledDataSorting && series.xAxis && !justify) {
  41668. series.setDataLabelStartPos(point, dataLabel, isNew, isInsidePlot, alignOptions);
  41669. }
  41670. };
  41671. let baseline, rotCorr, // rotation correction
  41672. // Math.round for rounding errors (#2683), alignTo to allow column
  41673. // labels (#2700)
  41674. alignAttr, // the final position;
  41675. justify = pick(options.overflow, (enabledDataSorting ? 'none' : 'justify')) === 'justify', visible = this.visible &&
  41676. point.visible !== false &&
  41677. defined(plotX) &&
  41678. (point.series.forceDL ||
  41679. (enabledDataSorting && !justify) ||
  41680. isInsidePlot ||
  41681. (
  41682. // If the data label is inside the align box, it is
  41683. // enough that parts of the align box is inside the
  41684. // plot area (#12370). When stacking, it is always
  41685. // inside regardless of the option (#15148).
  41686. pick(options.inside, !!this.options.stacking) &&
  41687. alignTo &&
  41688. chart.isInsidePlot(plotX, inverted ?
  41689. alignTo.x + 1 :
  41690. alignTo.y + alignTo.height - 1, {
  41691. inverted,
  41692. paneCoordinates: true,
  41693. series
  41694. })));
  41695. const pos = point.pos();
  41696. if (visible && pos) {
  41697. if (rotation) {
  41698. dataLabel.attr({ align });
  41699. }
  41700. let bBox = dataLabel.getBBox(true), bBoxCorrection = [0, 0];
  41701. baseline = chart.renderer.fontMetrics(dataLabel).b;
  41702. // The alignment box is a singular point
  41703. alignTo = extend({
  41704. x: pos[0],
  41705. y: Math.round(pos[1]),
  41706. width: 0,
  41707. height: 0
  41708. }, alignTo);
  41709. // Add the text size for alignment calculation
  41710. extend(options, {
  41711. width: bBox.width,
  41712. height: bBox.height
  41713. });
  41714. // Allow a hook for changing alignment in the last moment, then do
  41715. // the alignment
  41716. if (rotation) {
  41717. justify = false; // Not supported for rotated text
  41718. rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
  41719. alignAttr = {
  41720. x: (alignTo.x +
  41721. (options.x || 0) +
  41722. alignTo.width / 2 +
  41723. rotCorr.x),
  41724. y: (alignTo.y +
  41725. (options.y || 0) +
  41726. { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
  41727. alignTo.height)
  41728. };
  41729. bBoxCorrection = [
  41730. bBox.x - Number(dataLabel.attr('x')),
  41731. bBox.y - Number(dataLabel.attr('y'))
  41732. ];
  41733. setStartPos(alignAttr); // data sorting
  41734. dataLabel[isNew ? 'attr' : 'animate'](alignAttr);
  41735. }
  41736. else {
  41737. setStartPos(alignTo); // data sorting
  41738. dataLabel.align(options, void 0, alignTo);
  41739. alignAttr = dataLabel.alignAttr;
  41740. }
  41741. // Handle justify or crop
  41742. if (justify && alignTo.height >= 0) { // #8830
  41743. this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew);
  41744. // Now check that the data label is within the plot area
  41745. }
  41746. else if (pick(options.crop, true)) {
  41747. let { x, y } = alignAttr;
  41748. x += bBoxCorrection[0];
  41749. y += bBoxCorrection[1];
  41750. // Uncomment this block to visualize the bounding boxes used for
  41751. // determining visibility
  41752. /*
  41753. chart.renderer.rect(
  41754. chart.plotLeft + alignAttr.x + bBox.x,
  41755. chart.plotTop + alignAttr.y + bBox.y + 9999,
  41756. bBox.width,
  41757. bBox.height
  41758. ).attr({
  41759. stroke: 'rgba(0, 0, 0, 0.3)',
  41760. 'stroke-width': 0.5
  41761. }).add();
  41762. chart.renderer.circle(
  41763. chart.plotLeft + alignAttr.x,
  41764. chart.plotTop + alignAttr.y,
  41765. 2
  41766. ).attr({
  41767. fill: 'red',
  41768. zIndex: 20
  41769. }).add();
  41770. // */
  41771. visible =
  41772. chart.isInsidePlot(x, y, {
  41773. paneCoordinates: true,
  41774. series
  41775. }) &&
  41776. chart.isInsidePlot(x + bBox.width, y + bBox.height, {
  41777. paneCoordinates: true,
  41778. series
  41779. });
  41780. }
  41781. // When we're using a shape, make it possible with a connector or an
  41782. // arrow pointing to thie point
  41783. if (options.shape && !rotation) {
  41784. dataLabel[isNew ? 'attr' : 'animate']({
  41785. anchorX: pos[0],
  41786. anchorY: pos[1]
  41787. });
  41788. }
  41789. }
  41790. // To use alignAttr property in hideOverlappingLabels
  41791. if (isNew && enabledDataSorting) {
  41792. dataLabel.placed = false;
  41793. }
  41794. // Show or hide based on the final aligned position
  41795. if (!visible && (!enabledDataSorting || justify)) {
  41796. dataLabel.hide();
  41797. dataLabel.placed = false; // don't animate back in
  41798. }
  41799. else {
  41800. dataLabel.show();
  41801. }
  41802. }
  41803. /**
  41804. * Handle the dataLabels.filter option.
  41805. * @private
  41806. */
  41807. function applyFilter(point, options) {
  41808. const filter = options.filter;
  41809. if (filter) {
  41810. const op = filter.operator;
  41811. const prop = point[filter.property];
  41812. const val = filter.value;
  41813. if ((op === '>' && prop > val) ||
  41814. (op === '<' && prop < val) ||
  41815. (op === '>=' && prop >= val) ||
  41816. (op === '<=' && prop <= val) ||
  41817. (op === '==' && prop == val) || // eslint-disable-line eqeqeq
  41818. (op === '===' && prop === val)) {
  41819. return true;
  41820. }
  41821. return false;
  41822. }
  41823. return true;
  41824. }
  41825. /**
  41826. * @private
  41827. */
  41828. function compose(SeriesClass) {
  41829. if (U.pushUnique(composedMembers, SeriesClass)) {
  41830. const seriesProto = SeriesClass.prototype;
  41831. seriesProto.initDataLabelsGroup = initDataLabelsGroup;
  41832. seriesProto.initDataLabels = initDataLabels;
  41833. seriesProto.alignDataLabel = alignDataLabel;
  41834. seriesProto.drawDataLabels = drawDataLabels;
  41835. seriesProto.justifyDataLabel = justifyDataLabel;
  41836. seriesProto.setDataLabelStartPos = setDataLabelStartPos;
  41837. }
  41838. }
  41839. DataLabel.compose = compose;
  41840. /**
  41841. * Create the SVGElement group for dataLabels
  41842. * @private
  41843. */
  41844. function initDataLabelsGroup() {
  41845. return this.plotGroup('dataLabelsGroup', 'data-labels', this.hasRendered ? 'inherit' : 'hidden', // #5133, #10220
  41846. this.options.dataLabels.zIndex || 6);
  41847. }
  41848. /**
  41849. * Init the data labels with the correct animation
  41850. * @private
  41851. */
  41852. function initDataLabels(animationConfig) {
  41853. const series = this, hasRendered = series.hasRendered || 0;
  41854. // Create a separate group for the data labels to avoid rotation
  41855. const dataLabelsGroup = this.initDataLabelsGroup()
  41856. .attr({ opacity: +hasRendered }); // #3300
  41857. if (!hasRendered && dataLabelsGroup) {
  41858. if (series.visible) { // #2597, #3023, #3024
  41859. dataLabelsGroup.show();
  41860. }
  41861. if (series.options.animation) {
  41862. dataLabelsGroup.animate({ opacity: 1 }, animationConfig);
  41863. }
  41864. else {
  41865. dataLabelsGroup.attr({ opacity: 1 });
  41866. }
  41867. }
  41868. return dataLabelsGroup;
  41869. }
  41870. /**
  41871. * Draw the data labels
  41872. * @private
  41873. */
  41874. function drawDataLabels(points = this.points) {
  41875. var _a,
  41876. _b;
  41877. const series = this, chart = series.chart, seriesOptions = series.options, renderer = chart.renderer, { backgroundColor, plotBackgroundColor } = chart.options.chart, plotOptions = chart.options.plotOptions, contrastColor = renderer.getContrast((isString(plotBackgroundColor) && plotBackgroundColor) ||
  41878. (isString(backgroundColor) && backgroundColor) ||
  41879. "#000000" /* Palette.neutralColor100 */);
  41880. let seriesDlOptions = seriesOptions.dataLabels, pointOptions, dataLabelsGroup;
  41881. const firstDLOptions = splat(seriesDlOptions)[0], dataLabelAnim = firstDLOptions.animation, animationConfig = firstDLOptions.defer ?
  41882. getDeferredAnimation(chart, dataLabelAnim, series) :
  41883. { defer: 0, duration: 0 };
  41884. // Merge in plotOptions.dataLabels for series
  41885. seriesDlOptions = mergeArrays(mergeArrays((_a = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions.series) === null || _a === void 0 ? void 0 : _a.dataLabels, (_b = plotOptions === null || plotOptions === void 0 ? void 0 : plotOptions[series.type]) === null || _b === void 0 ? void 0 : _b.dataLabels), seriesDlOptions);
  41886. fireEvent(this, 'drawDataLabels');
  41887. if (isArray(seriesDlOptions) ||
  41888. seriesDlOptions.enabled ||
  41889. series._hasPointLabels) {
  41890. dataLabelsGroup = this.initDataLabels(animationConfig);
  41891. // Make the labels for each point
  41892. points.forEach((point) => {
  41893. var _a;
  41894. const dataLabels = point.dataLabels || [];
  41895. // Merge in series options for the point.
  41896. // @note dataLabelAttribs (like pointAttribs) would eradicate
  41897. // the need for dlOptions, and simplify the section below.
  41898. pointOptions = splat(mergeArrays(seriesDlOptions,
  41899. // dlOptions is used in treemaps
  41900. point.dlOptions || ((_a = point.options) === null || _a === void 0 ? void 0 : _a.dataLabels)));
  41901. // Handle each individual data label for this point
  41902. pointOptions.forEach((labelOptions, i) => {
  41903. var _a;
  41904. // Options for one datalabel
  41905. const labelEnabled = (labelOptions.enabled &&
  41906. // #2282, #4641, #7112, #10049
  41907. (!point.isNull || point.dataLabelOnNull) &&
  41908. applyFilter(point, labelOptions)), connector = point.connectors ?
  41909. point.connectors[i] :
  41910. point.connector, style = labelOptions.style || {};
  41911. let labelConfig, formatString, labelText, rotation, attr = {}, dataLabel = dataLabels[i], isNew = !dataLabel;
  41912. const labelDistance = pick(labelOptions.distance, point.labelDistance);
  41913. if (labelEnabled) {
  41914. // Create individual options structure that can be
  41915. // extended without affecting others
  41916. formatString = pick(labelOptions[point.formatPrefix + 'Format'], labelOptions.format);
  41917. labelConfig = point.getLabelConfig();
  41918. labelText = defined(formatString) ?
  41919. format(formatString, labelConfig, chart) :
  41920. (labelOptions[point.formatPrefix + 'Formatter'] ||
  41921. labelOptions.formatter).call(labelConfig, labelOptions);
  41922. rotation = labelOptions.rotation;
  41923. if (!chart.styledMode) {
  41924. // Determine the color
  41925. style.color = pick(labelOptions.color, style.color, isString(series.color) ? series.color : void 0, "#000000" /* Palette.neutralColor100 */);
  41926. // Get automated contrast color
  41927. if (style.color === 'contrast') {
  41928. point.contrastColor = renderer.getContrast((point.color || series.color));
  41929. style.color = ((!defined(labelDistance) &&
  41930. labelOptions.inside) ||
  41931. (labelDistance || 0) < 0 ||
  41932. seriesOptions.stacking) ?
  41933. point.contrastColor :
  41934. contrastColor;
  41935. }
  41936. else {
  41937. delete point.contrastColor;
  41938. }
  41939. if (seriesOptions.cursor) {
  41940. style.cursor = seriesOptions.cursor;
  41941. }
  41942. }
  41943. attr = {
  41944. r: labelOptions.borderRadius || 0,
  41945. rotation,
  41946. padding: labelOptions.padding,
  41947. zIndex: 1
  41948. };
  41949. if (!chart.styledMode) {
  41950. const { backgroundColor, borderColor } = labelOptions;
  41951. attr.fill = backgroundColor === 'auto' ?
  41952. point.color :
  41953. backgroundColor;
  41954. attr.stroke = borderColor === 'auto' ?
  41955. point.color :
  41956. borderColor;
  41957. attr['stroke-width'] = labelOptions.borderWidth;
  41958. }
  41959. // Remove unused attributes (#947)
  41960. objectEach(attr, (val, name) => {
  41961. if (typeof val === 'undefined') {
  41962. delete attr[name];
  41963. }
  41964. });
  41965. }
  41966. // If the point is outside the plot area, or the label
  41967. // changes properties that we cannot change, destroy it and
  41968. // build a new one below. #678, #820.
  41969. if (dataLabel && (!labelEnabled ||
  41970. !defined(labelText) ||
  41971. !!dataLabel.div !== !!labelOptions.useHTML ||
  41972. (
  41973. // Change from no rotation to rotation and
  41974. // vice versa. Don't use defined() because
  41975. // rotation = 0 means also rotation = undefined
  41976. (!dataLabel.rotation ||
  41977. !labelOptions.rotation) &&
  41978. dataLabel.rotation !== labelOptions.rotation))) {
  41979. dataLabel = void 0;
  41980. isNew = true;
  41981. if (connector && point.connector) {
  41982. point.connector = point.connector.destroy();
  41983. if (point.connectors) {
  41984. // Remove point.connectors if this was the last
  41985. // one
  41986. if (point.connectors.length === 1) {
  41987. delete point.connectors;
  41988. }
  41989. else {
  41990. delete point.connectors[i];
  41991. }
  41992. }
  41993. }
  41994. }
  41995. // Individual labels are disabled if the are explicitly
  41996. // disabled in the point options, or if they fall outside
  41997. // the plot area.
  41998. if (labelEnabled && defined(labelText)) {
  41999. if (!dataLabel) {
  42000. // Create new label element
  42001. dataLabel = rotation ?
  42002. // Labels don't rotate, use text element
  42003. renderer.text(labelText, 0, 0, labelOptions.useHTML)
  42004. .addClass('highcharts-data-label') :
  42005. // We can use label
  42006. renderer.label(labelText, 0, 0, labelOptions.shape, void 0, void 0, labelOptions.useHTML, void 0, 'data-label');
  42007. if (dataLabel) {
  42008. dataLabel.addClass(' highcharts-data-label-color-' +
  42009. point.colorIndex +
  42010. ' ' + (labelOptions.className || '') +
  42011. ( // #3398
  42012. labelOptions.useHTML ?
  42013. ' highcharts-tracker' :
  42014. ''));
  42015. }
  42016. }
  42017. else {
  42018. // Use old element and just update text
  42019. attr.text = labelText;
  42020. }
  42021. // Store data label options for later access
  42022. if (dataLabel) {
  42023. dataLabel.options = labelOptions;
  42024. dataLabel.attr(attr);
  42025. if (!chart.styledMode) {
  42026. // Styles must be applied before add in order to
  42027. // read text bounding box
  42028. dataLabel.css(style).shadow(labelOptions.shadow);
  42029. }
  42030. const textPathOptions = labelOptions[point.formatPrefix + 'TextPath'] || labelOptions.textPath;
  42031. if (textPathOptions && !labelOptions.useHTML) {
  42032. dataLabel.setTextPath(((_a = point.getDataLabelPath) === null || _a === void 0 ? void 0 : _a.call(point, dataLabel)) ||
  42033. point.graphic, textPathOptions);
  42034. if (point.dataLabelPath &&
  42035. !textPathOptions.enabled) {
  42036. // clean the DOM
  42037. point.dataLabelPath = (point.dataLabelPath.destroy());
  42038. }
  42039. }
  42040. if (!dataLabel.added) {
  42041. dataLabel.add(dataLabelsGroup);
  42042. }
  42043. // Now the data label is created and placed at 0,0,
  42044. // so we need to align it
  42045. series.alignDataLabel(point, dataLabel, labelOptions, void 0, isNew);
  42046. dataLabel.isActive = true;
  42047. if (dataLabels[i] && dataLabels[i] !== dataLabel) {
  42048. dataLabels[i].destroy();
  42049. }
  42050. dataLabels[i] = dataLabel;
  42051. }
  42052. }
  42053. });
  42054. // Destroy and remove the inactive ones
  42055. let j = dataLabels.length;
  42056. while (j--) {
  42057. if (dataLabels[j].isActive) {
  42058. dataLabels[j].isActive = false;
  42059. }
  42060. else {
  42061. dataLabels[j].destroy();
  42062. dataLabels.splice(j, 1);
  42063. }
  42064. }
  42065. // Write back
  42066. point.dataLabel = dataLabels[0];
  42067. point.dataLabels = dataLabels;
  42068. });
  42069. }
  42070. fireEvent(this, 'afterDrawDataLabels');
  42071. }
  42072. /**
  42073. * If data labels fall partly outside the plot area, align them back in, in
  42074. * a way that doesn't hide the point.
  42075. * @private
  42076. */
  42077. function justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew) {
  42078. const chart = this.chart, align = options.align, verticalAlign = options.verticalAlign, padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
  42079. let { x = 0, y = 0 } = options, off, justified;
  42080. // Off left
  42081. off = (alignAttr.x || 0) + padding;
  42082. if (off < 0) {
  42083. if (align === 'right' && x >= 0) {
  42084. options.align = 'left';
  42085. options.inside = true;
  42086. }
  42087. else {
  42088. x -= off;
  42089. }
  42090. justified = true;
  42091. }
  42092. // Off right
  42093. off = (alignAttr.x || 0) + bBox.width - padding;
  42094. if (off > chart.plotWidth) {
  42095. if (align === 'left' && x <= 0) {
  42096. options.align = 'right';
  42097. options.inside = true;
  42098. }
  42099. else {
  42100. x += chart.plotWidth - off;
  42101. }
  42102. justified = true;
  42103. }
  42104. // Off top
  42105. off = alignAttr.y + padding;
  42106. if (off < 0) {
  42107. if (verticalAlign === 'bottom' && y >= 0) {
  42108. options.verticalAlign = 'top';
  42109. options.inside = true;
  42110. }
  42111. else {
  42112. y -= off;
  42113. }
  42114. justified = true;
  42115. }
  42116. // Off bottom
  42117. off = (alignAttr.y || 0) + bBox.height - padding;
  42118. if (off > chart.plotHeight) {
  42119. if (verticalAlign === 'top' && y <= 0) {
  42120. options.verticalAlign = 'bottom';
  42121. options.inside = true;
  42122. }
  42123. else {
  42124. y += chart.plotHeight - off;
  42125. }
  42126. justified = true;
  42127. }
  42128. if (justified) {
  42129. options.x = x;
  42130. options.y = y;
  42131. dataLabel.placed = !isNew;
  42132. dataLabel.align(options, void 0, alignTo);
  42133. }
  42134. return justified;
  42135. }
  42136. /**
  42137. * Merge two objects that can be arrays. If one of them is an array, the
  42138. * other is merged into each element. If both are arrays, each element is
  42139. * merged by index. If neither are arrays, we use normal merge.
  42140. * @private
  42141. */
  42142. function mergeArrays(one, two) {
  42143. let res = [], i;
  42144. if (isArray(one) && !isArray(two)) {
  42145. res = one.map(function (el) {
  42146. return merge(el, two);
  42147. });
  42148. }
  42149. else if (isArray(two) && !isArray(one)) {
  42150. res = two.map(function (el) {
  42151. return merge(one, el);
  42152. });
  42153. }
  42154. else if (!isArray(one) && !isArray(two)) {
  42155. res = merge(one, two);
  42156. }
  42157. else if (isArray(one) && isArray(two)) {
  42158. i = Math.max(one.length, two.length);
  42159. while (i--) {
  42160. res[i] = merge(one[i], two[i]);
  42161. }
  42162. }
  42163. return res;
  42164. }
  42165. /**
  42166. * Set starting position for data label sorting animation.
  42167. * @private
  42168. */
  42169. function setDataLabelStartPos(point, dataLabel, isNew, isInside, alignOptions) {
  42170. const chart = this.chart, inverted = chart.inverted, xAxis = this.xAxis, reversed = xAxis.reversed, labelCenter = inverted ? dataLabel.height / 2 : dataLabel.width / 2, pointWidth = point.pointWidth, halfWidth = pointWidth ? pointWidth / 2 : 0;
  42171. dataLabel.startXPos = inverted ?
  42172. alignOptions.x :
  42173. (reversed ?
  42174. -labelCenter - halfWidth :
  42175. xAxis.width - labelCenter + halfWidth);
  42176. dataLabel.startYPos = inverted ?
  42177. (reversed ?
  42178. this.yAxis.height - labelCenter + halfWidth :
  42179. -labelCenter - halfWidth) : alignOptions.y;
  42180. // We need to handle visibility in case of sorting point outside plot
  42181. // area
  42182. if (!isInside) {
  42183. dataLabel
  42184. .attr({ opacity: 1 })
  42185. .animate({ opacity: 0 }, void 0, dataLabel.hide);
  42186. }
  42187. else if (dataLabel.visibility === 'hidden') {
  42188. dataLabel.show();
  42189. dataLabel
  42190. .attr({ opacity: 0 })
  42191. .animate({ opacity: 1 });
  42192. }
  42193. // Save start position on first render, but do not change position
  42194. if (!chart.hasRendered) {
  42195. return;
  42196. }
  42197. // Set start position
  42198. if (isNew) {
  42199. dataLabel.attr({ x: dataLabel.startXPos, y: dataLabel.startYPos });
  42200. }
  42201. dataLabel.placed = true;
  42202. }
  42203. })(DataLabel || (DataLabel = {}));
  42204. /* *
  42205. *
  42206. * Default Export
  42207. *
  42208. * */
  42209. /* *
  42210. *
  42211. * API Declarations
  42212. *
  42213. * */
  42214. /**
  42215. * Callback JavaScript function to format the data label as a string. Note that
  42216. * if a `format` is defined, the format takes precedence and the formatter is
  42217. * ignored.
  42218. *
  42219. * @callback Highcharts.DataLabelsFormatterCallbackFunction
  42220. *
  42221. * @param {Highcharts.PointLabelObject} this
  42222. * Data label context to format
  42223. *
  42224. * @param {Highcharts.DataLabelsOptions} options
  42225. * [API options](/highcharts/plotOptions.series.dataLabels) of the data label
  42226. *
  42227. * @return {number|string|null|undefined}
  42228. * Formatted data label text
  42229. */
  42230. /**
  42231. * Values for handling data labels that flow outside the plot area.
  42232. *
  42233. * @typedef {"allow"|"justify"} Highcharts.DataLabelsOverflowValue
  42234. */
  42235. ''; // keeps doclets above in JS file
  42236. return DataLabel;
  42237. });
  42238. _registerModule(_modules, 'Series/Column/ColumnDataLabel.js', [_modules['Core/Series/DataLabel.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DataLabel, SeriesRegistry, U) {
  42239. /* *
  42240. *
  42241. * (c) 2010-2021 Torstein Honsi
  42242. *
  42243. * License: www.highcharts.com/license
  42244. *
  42245. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42246. *
  42247. * */
  42248. const { series: Series } = SeriesRegistry;
  42249. const { merge, pick } = U;
  42250. /* *
  42251. *
  42252. * Composition
  42253. *
  42254. * */
  42255. var ColumnDataLabel;
  42256. (function (ColumnDataLabel) {
  42257. /* *
  42258. *
  42259. * Constants
  42260. *
  42261. * */
  42262. const composedMembers = [];
  42263. /* *
  42264. *
  42265. * Functions
  42266. *
  42267. * */
  42268. /* eslint-disable valid-jsdoc */
  42269. /**
  42270. * Override the basic data label alignment by adjusting for the position of
  42271. * the column.
  42272. * @private
  42273. */
  42274. function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
  42275. let inverted = this.chart.inverted, series = point.series, xLen = (series.xAxis ? series.xAxis.len : this.chart.plotSizeX) || 0, yLen = (series.yAxis ? series.yAxis.len : this.chart.plotSizeY) || 0,
  42276. // data label box for alignment
  42277. dlBox = point.dlBox || point.shapeArgs, below = pick(point.below, // range series
  42278. point.plotY >
  42279. pick(this.translatedThreshold, yLen)),
  42280. // draw it inside the box?
  42281. inside = pick(options.inside, !!this.options.stacking), overshoot;
  42282. // Align to the column itself, or the top of it
  42283. if (dlBox) { // Area range uses this method but not alignTo
  42284. alignTo = merge(dlBox);
  42285. if (alignTo.y < 0) {
  42286. alignTo.height += alignTo.y;
  42287. alignTo.y = 0;
  42288. }
  42289. // If parts of the box overshoots outside the plot area, modify the
  42290. // box to center the label inside
  42291. overshoot = alignTo.y + alignTo.height - yLen;
  42292. if (overshoot > 0 && overshoot < alignTo.height) {
  42293. alignTo.height -= overshoot;
  42294. }
  42295. if (inverted) {
  42296. alignTo = {
  42297. x: yLen - alignTo.y - alignTo.height,
  42298. y: xLen - alignTo.x - alignTo.width,
  42299. width: alignTo.height,
  42300. height: alignTo.width
  42301. };
  42302. }
  42303. // Compute the alignment box
  42304. if (!inside) {
  42305. if (inverted) {
  42306. alignTo.x += below ? 0 : alignTo.width;
  42307. alignTo.width = 0;
  42308. }
  42309. else {
  42310. alignTo.y += below ? alignTo.height : 0;
  42311. alignTo.height = 0;
  42312. }
  42313. }
  42314. }
  42315. // When alignment is undefined (typically columns and bars), display the
  42316. // individual point below or above the point depending on the threshold
  42317. options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
  42318. options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
  42319. // Call the parent method
  42320. Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
  42321. // If label was justified and we have contrast, set it:
  42322. if (options.inside && point.contrastColor) {
  42323. dataLabel.css({
  42324. color: point.contrastColor
  42325. });
  42326. }
  42327. }
  42328. /** @private */
  42329. function compose(ColumnSeriesClass) {
  42330. DataLabel.compose(Series);
  42331. if (U.pushUnique(composedMembers, ColumnSeriesClass)) {
  42332. ColumnSeriesClass.prototype.alignDataLabel = alignDataLabel;
  42333. }
  42334. }
  42335. ColumnDataLabel.compose = compose;
  42336. })(ColumnDataLabel || (ColumnDataLabel = {}));
  42337. /* *
  42338. *
  42339. * Default Export
  42340. *
  42341. * */
  42342. return ColumnDataLabel;
  42343. });
  42344. _registerModule(_modules, 'Series/Bar/BarSeries.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (ColumnSeries, SeriesRegistry, U) {
  42345. /* *
  42346. *
  42347. * (c) 2010-2021 Torstein Honsi
  42348. *
  42349. * License: www.highcharts.com/license
  42350. *
  42351. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42352. *
  42353. * */
  42354. const { extend, merge } = U;
  42355. /* *
  42356. *
  42357. * Class
  42358. *
  42359. * */
  42360. /**
  42361. * Bar series type.
  42362. *
  42363. * @private
  42364. * @class
  42365. * @name Highcharts.seriesTypes.bar
  42366. *
  42367. * @augments Highcharts.Series
  42368. */
  42369. class BarSeries extends ColumnSeries {
  42370. constructor() {
  42371. /* *
  42372. *
  42373. * Static Properties
  42374. *
  42375. * */
  42376. super(...arguments);
  42377. /* *
  42378. *
  42379. * Properties
  42380. *
  42381. * */
  42382. this.data = void 0;
  42383. this.options = void 0;
  42384. this.points = void 0;
  42385. }
  42386. }
  42387. /**
  42388. * A bar series is a special type of column series where the columns are
  42389. * horizontal.
  42390. *
  42391. * @sample highcharts/demo/bar-basic/
  42392. * Bar chart
  42393. *
  42394. * @extends plotOptions.column
  42395. * @product highcharts
  42396. * @optionparent plotOptions.bar
  42397. */
  42398. BarSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  42399. // nothing here yet
  42400. });
  42401. extend(BarSeries.prototype, {
  42402. inverted: true
  42403. });
  42404. SeriesRegistry.registerSeriesType('bar', BarSeries);
  42405. /* *
  42406. *
  42407. * Default Export
  42408. *
  42409. * */
  42410. /* *
  42411. *
  42412. * API Options
  42413. *
  42414. * */
  42415. /**
  42416. * A `bar` series. If the [type](#series.bar.type) option is not specified,
  42417. * it is inherited from [chart.type](#chart.type).
  42418. *
  42419. * @extends series,plotOptions.bar
  42420. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  42421. * linecap, lineWidth, marker, connectEnds, step
  42422. * @product highcharts
  42423. * @apioption series.bar
  42424. */
  42425. /**
  42426. * An array of data points for the series. For the `bar` series type,
  42427. * points can be given in the following ways:
  42428. *
  42429. * 1. An array of numerical values. In this case, the numerical values will be
  42430. * interpreted as `y` options. The `x` values will be automatically
  42431. * calculated, either starting at 0 and incremented by 1, or from
  42432. * `pointStart` and `pointInterval` given in the series options. If the axis
  42433. * has categories, these will be used. Example:
  42434. * ```js
  42435. * data: [0, 5, 3, 5]
  42436. * ```
  42437. *
  42438. * 2. An array of arrays with 2 values. In this case, the values correspond to
  42439. * `x,y`. If the first value is a string, it is applied as the name of the
  42440. * point, and the `x` value is inferred.
  42441. * ```js
  42442. * data: [
  42443. * [0, 5],
  42444. * [1, 10],
  42445. * [2, 3]
  42446. * ]
  42447. * ```
  42448. *
  42449. * 3. An array of objects with named values. The following snippet shows only a
  42450. * few settings, see the complete options set below. If the total number of
  42451. * data points exceeds the series'
  42452. * [turboThreshold](#series.bar.turboThreshold), this option is not
  42453. * available.
  42454. * ```js
  42455. * data: [{
  42456. * x: 1,
  42457. * y: 1,
  42458. * name: "Point2",
  42459. * color: "#00FF00"
  42460. * }, {
  42461. * x: 1,
  42462. * y: 10,
  42463. * name: "Point1",
  42464. * color: "#FF00FF"
  42465. * }]
  42466. * ```
  42467. *
  42468. * @sample {highcharts} highcharts/chart/reflow-true/
  42469. * Numerical values
  42470. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  42471. * Arrays of numeric x and y
  42472. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  42473. * Arrays of datetime x and y
  42474. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  42475. * Arrays of point.name and y
  42476. * @sample {highcharts} highcharts/series/data-array-of-objects/
  42477. * Config objects
  42478. *
  42479. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  42480. * @extends series.column.data
  42481. * @product highcharts
  42482. * @apioption series.bar.data
  42483. */
  42484. /**
  42485. * @excluding halo,lineWidth,lineWidthPlus,marker
  42486. * @product highcharts highstock
  42487. * @apioption series.bar.states.hover
  42488. */
  42489. /**
  42490. * @excluding halo,lineWidth,lineWidthPlus,marker
  42491. * @product highcharts highstock
  42492. * @apioption series.bar.states.select
  42493. */
  42494. ''; // gets doclets above into transpilat
  42495. return BarSeries;
  42496. });
  42497. _registerModule(_modules, 'Series/Scatter/ScatterSeriesDefaults.js', [], function () {
  42498. /* *
  42499. *
  42500. * (c) 2010-2021 Torstein Honsi
  42501. *
  42502. * License: www.highcharts.com/license
  42503. *
  42504. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42505. *
  42506. * */
  42507. /* *
  42508. *
  42509. * API Options
  42510. *
  42511. * */
  42512. /**
  42513. * A scatter plot uses cartesian coordinates to display values for two
  42514. * variables for a set of data.
  42515. *
  42516. * @sample {highcharts} highcharts/demo/scatter/
  42517. * Scatter plot
  42518. *
  42519. * @extends plotOptions.line
  42520. * @excluding cropThreshold, pointPlacement, shadow, useOhlcData
  42521. * @product highcharts highstock
  42522. * @optionparent plotOptions.scatter
  42523. */
  42524. const ScatterSeriesDefaults = {
  42525. /**
  42526. * The width of the line connecting the data points.
  42527. *
  42528. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
  42529. * 0 by default
  42530. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
  42531. * 1px
  42532. *
  42533. * @product highcharts highstock
  42534. */
  42535. lineWidth: 0,
  42536. findNearestPointBy: 'xy',
  42537. /**
  42538. * Apply a jitter effect for the rendered markers. When plotting
  42539. * discrete values, a little random noise may help telling the points
  42540. * apart. The jitter setting applies a random displacement of up to `n`
  42541. * axis units in either direction. So for example on a horizontal X
  42542. * axis, setting the `jitter.x` to 0.24 will render the point in a
  42543. * random position between 0.24 units to the left and 0.24 units to the
  42544. * right of the true axis position. On a category axis, setting it to
  42545. * 0.5 will fill up the bin and make the data appear continuous.
  42546. *
  42547. * When rendered on top of a box plot or a column series, a jitter value
  42548. * of 0.24 will correspond to the underlying series' default
  42549. * [groupPadding](
  42550. * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
  42551. * and [pointPadding](
  42552. * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
  42553. * settings.
  42554. *
  42555. * @sample {highcharts} highcharts/demo/scatter-jitter
  42556. * Jitter on a scatter plot
  42557. *
  42558. * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
  42559. * Jittered scatter plot on top of a box plot
  42560. *
  42561. * @product highcharts highstock
  42562. * @since 7.0.2
  42563. */
  42564. jitter: {
  42565. /**
  42566. * The maximal X offset for the random jitter effect.
  42567. */
  42568. x: 0,
  42569. /**
  42570. * The maximal Y offset for the random jitter effect.
  42571. */
  42572. y: 0
  42573. },
  42574. marker: {
  42575. enabled: true // Overrides auto-enabling in line series (#3647)
  42576. },
  42577. /**
  42578. * Sticky tracking of mouse events. When true, the `mouseOut` event
  42579. * on a series isn't triggered until the mouse moves over another
  42580. * series, or out of the plot area. When false, the `mouseOut` event on
  42581. * a series is triggered when the mouse leaves the area around the
  42582. * series' graph or markers. This also implies the tooltip. When
  42583. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  42584. * will be hidden when moving the mouse between series.
  42585. *
  42586. * @type {boolean}
  42587. * @default false
  42588. * @product highcharts highstock highmaps
  42589. * @apioption plotOptions.scatter.stickyTracking
  42590. */
  42591. /**
  42592. * A configuration object for the tooltip rendering of each single
  42593. * series. Properties are inherited from [tooltip](#tooltip).
  42594. * Overridable properties are `headerFormat`, `pointFormat`,
  42595. * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
  42596. * series, in a scatter plot the series.name by default shows in the
  42597. * headerFormat and point.x and point.y in the pointFormat.
  42598. *
  42599. * @product highcharts highstock highmaps
  42600. */
  42601. tooltip: {
  42602. /**
  42603. * @product highcharts highstock
  42604. */
  42605. headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  42606. '<span style="font-size: 0.8em"> {series.name}</span><br/>',
  42607. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
  42608. }
  42609. };
  42610. /**
  42611. * A `scatter` series. If the [type](#series.scatter.type) option is
  42612. * not specified, it is inherited from [chart.type](#chart.type).
  42613. *
  42614. * @extends series,plotOptions.scatter
  42615. * @excluding cropThreshold, dataParser, dataURL, useOhlcData
  42616. * @product highcharts highstock
  42617. * @apioption series.scatter
  42618. */
  42619. /**
  42620. * An array of data points for the series. For the `scatter` series
  42621. * type, points can be given in the following ways:
  42622. *
  42623. * 1. An array of numerical values. In this case, the numerical values will be
  42624. * interpreted as `y` options. The `x` values will be automatically
  42625. * calculated, either starting at 0 and incremented by 1, or from
  42626. * `pointStart` and `pointInterval` given in the series options. If the axis
  42627. * has categories, these will be used. Example:
  42628. * ```js
  42629. * data: [0, 5, 3, 5]
  42630. * ```
  42631. *
  42632. * 2. An array of arrays with 2 values. In this case, the values correspond to
  42633. * `x,y`. If the first value is a string, it is applied as the name of the
  42634. * point, and the `x` value is inferred.
  42635. * ```js
  42636. * data: [
  42637. * [0, 0],
  42638. * [1, 8],
  42639. * [2, 9]
  42640. * ]
  42641. * ```
  42642. *
  42643. * 3. An array of objects with named values. The following snippet shows only a
  42644. * few settings, see the complete options set below. If the total number of
  42645. * data points exceeds the series'
  42646. * [turboThreshold](#series.scatter.turboThreshold), this option is not
  42647. * available.
  42648. * ```js
  42649. * data: [{
  42650. * x: 1,
  42651. * y: 2,
  42652. * name: "Point2",
  42653. * color: "#00FF00"
  42654. * }, {
  42655. * x: 1,
  42656. * y: 4,
  42657. * name: "Point1",
  42658. * color: "#FF00FF"
  42659. * }]
  42660. * ```
  42661. *
  42662. * @sample {highcharts} highcharts/chart/reflow-true/
  42663. * Numerical values
  42664. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  42665. * Arrays of numeric x and y
  42666. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  42667. * Arrays of datetime x and y
  42668. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  42669. * Arrays of point.name and y
  42670. * @sample {highcharts} highcharts/series/data-array-of-objects/
  42671. * Config objects
  42672. *
  42673. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  42674. * @extends series.line.data
  42675. * @product highcharts highstock
  42676. * @apioption series.scatter.data
  42677. */
  42678. ''; // keeps doclets above in JS file
  42679. /* *
  42680. *
  42681. * Default Export
  42682. *
  42683. * */
  42684. return ScatterSeriesDefaults;
  42685. });
  42686. _registerModule(_modules, 'Series/Scatter/ScatterSeries.js', [_modules['Series/Scatter/ScatterSeriesDefaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (ScatterSeriesDefaults, SeriesRegistry, U) {
  42687. /* *
  42688. *
  42689. * (c) 2010-2021 Torstein Honsi
  42690. *
  42691. * License: www.highcharts.com/license
  42692. *
  42693. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42694. *
  42695. * */
  42696. const { column: ColumnSeries, line: LineSeries } = SeriesRegistry.seriesTypes;
  42697. const { addEvent, extend, merge } = U;
  42698. /* *
  42699. *
  42700. * Class
  42701. *
  42702. * */
  42703. /**
  42704. * Scatter series type.
  42705. *
  42706. * @private
  42707. */
  42708. class ScatterSeries extends LineSeries {
  42709. constructor() {
  42710. /* *
  42711. *
  42712. * Static Properties
  42713. *
  42714. * */
  42715. super(...arguments);
  42716. /* *
  42717. *
  42718. * Properties
  42719. *
  42720. * */
  42721. this.data = void 0;
  42722. this.options = void 0;
  42723. this.points = void 0;
  42724. /* eslint-enable valid-jsdoc */
  42725. }
  42726. /* *
  42727. *
  42728. * Functions
  42729. *
  42730. * */
  42731. /* eslint-disable valid-jsdoc */
  42732. /**
  42733. * Optionally add the jitter effect.
  42734. * @private
  42735. */
  42736. applyJitter() {
  42737. const series = this, jitter = this.options.jitter, len = this.points.length;
  42738. /**
  42739. * Return a repeatable, pseudo-random number based on an integer
  42740. * seed.
  42741. * @private
  42742. */
  42743. function unrandom(seed) {
  42744. const rand = Math.sin(seed) * 10000;
  42745. return rand - Math.floor(rand);
  42746. }
  42747. if (jitter) {
  42748. this.points.forEach(function (point, i) {
  42749. ['x', 'y'].forEach(function (dim, j) {
  42750. let axis, plotProp = 'plot' + dim.toUpperCase(), min, max, translatedJitter;
  42751. if (jitter[dim] && !point.isNull) {
  42752. axis = series[dim + 'Axis'];
  42753. translatedJitter =
  42754. jitter[dim] * axis.transA;
  42755. if (axis && !axis.isLog) {
  42756. // Identify the outer bounds of the jitter range
  42757. min = Math.max(0, point[plotProp] - translatedJitter);
  42758. max = Math.min(axis.len, point[plotProp] + translatedJitter);
  42759. // Find a random position within this range
  42760. point[plotProp] = min +
  42761. (max - min) * unrandom(i + j * len);
  42762. // Update clientX for the tooltip k-d-tree
  42763. if (dim === 'x') {
  42764. point.clientX = point.plotX;
  42765. }
  42766. }
  42767. }
  42768. });
  42769. });
  42770. }
  42771. }
  42772. /**
  42773. * @private
  42774. */
  42775. drawGraph() {
  42776. if (this.options.lineWidth) {
  42777. super.drawGraph();
  42778. }
  42779. else if (this.graph) {
  42780. this.graph = this.graph.destroy();
  42781. }
  42782. }
  42783. }
  42784. ScatterSeries.defaultOptions = merge(LineSeries.defaultOptions, ScatterSeriesDefaults);
  42785. extend(ScatterSeries.prototype, {
  42786. drawTracker: ColumnSeries.prototype.drawTracker,
  42787. sorted: false,
  42788. requireSorting: false,
  42789. noSharedTooltip: true,
  42790. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  42791. takeOrdinalPosition: false // #2342
  42792. });
  42793. /* *
  42794. *
  42795. * Events
  42796. *
  42797. * */
  42798. /* eslint-disable no-invalid-this */
  42799. addEvent(ScatterSeries, 'afterTranslate', function () {
  42800. this.applyJitter();
  42801. });
  42802. SeriesRegistry.registerSeriesType('scatter', ScatterSeries);
  42803. /* *
  42804. *
  42805. * Default Export
  42806. *
  42807. * */
  42808. return ScatterSeries;
  42809. });
  42810. _registerModule(_modules, 'Series/CenteredUtilities.js', [_modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (H, Series, U) {
  42811. /* *
  42812. *
  42813. * (c) 2010-2021 Torstein Honsi
  42814. *
  42815. * License: www.highcharts.com/license
  42816. *
  42817. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42818. *
  42819. * */
  42820. const { deg2rad } = H;
  42821. const { fireEvent, isNumber, pick, relativeLength } = U;
  42822. /**
  42823. * @private
  42824. */
  42825. var CenteredUtilities;
  42826. (function (CenteredUtilities) {
  42827. /* *
  42828. *
  42829. * Declarations
  42830. *
  42831. * */
  42832. /* *
  42833. *
  42834. * Functions
  42835. *
  42836. * */
  42837. /* eslint-disable valid-jsdoc */
  42838. /**
  42839. * Get the center of the pie based on the size and center options relative
  42840. * to the plot area. Borrowed by the polar and gauge series types.
  42841. *
  42842. * @private
  42843. * @function Highcharts.CenteredSeriesMixin.getCenter
  42844. */
  42845. function getCenter() {
  42846. const options = this.options, chart = this.chart, slicingRoom = 2 * (options.slicedOffset || 0), plotWidth = chart.plotWidth - 2 * slicingRoom, plotHeight = chart.plotHeight - 2 * slicingRoom, centerOption = options.center, smallestSize = Math.min(plotWidth, plotHeight), thickness = options.thickness;
  42847. let handleSlicingRoom, size = options.size, innerSize = options.innerSize || 0, i, value;
  42848. if (typeof size === 'string') {
  42849. size = parseFloat(size);
  42850. }
  42851. if (typeof innerSize === 'string') {
  42852. innerSize = parseFloat(innerSize);
  42853. }
  42854. const positions = [
  42855. pick(centerOption[0], '50%'),
  42856. pick(centerOption[1], '50%'),
  42857. // Prevent from negative values
  42858. pick(size && size < 0 ? void 0 : options.size, '100%'),
  42859. pick(innerSize && innerSize < 0 ? void 0 : options.innerSize || 0, '0%')
  42860. ];
  42861. // No need for inner size in angular (gauges) series but still required
  42862. // for pie series
  42863. if (chart.angular && !(this instanceof Series)) {
  42864. positions[3] = 0;
  42865. }
  42866. for (i = 0; i < 4; ++i) {
  42867. value = positions[i];
  42868. handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
  42869. // i == 0: centerX, relative to width
  42870. // i == 1: centerY, relative to height
  42871. // i == 2: size, relative to smallestSize
  42872. // i == 3: innerSize, relative to size
  42873. positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) + (handleSlicingRoom ? slicingRoom : 0);
  42874. }
  42875. // innerSize cannot be larger than size (#3632)
  42876. if (positions[3] > positions[2]) {
  42877. positions[3] = positions[2];
  42878. }
  42879. // thickness overrides innerSize, need to be less than pie size (#6647)
  42880. if (isNumber(thickness) &&
  42881. thickness * 2 < positions[2] && thickness > 0) {
  42882. positions[3] = positions[2] - thickness * 2;
  42883. }
  42884. fireEvent(this, 'afterGetCenter', { positions });
  42885. return positions;
  42886. }
  42887. CenteredUtilities.getCenter = getCenter;
  42888. /**
  42889. * getStartAndEndRadians - Calculates start and end angles in radians.
  42890. * Used in series types such as pie and sunburst.
  42891. *
  42892. * @private
  42893. * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
  42894. *
  42895. * @param {number} [start]
  42896. * Start angle in degrees.
  42897. *
  42898. * @param {number} [end]
  42899. * Start angle in degrees.
  42900. *
  42901. * @return {Highcharts.RadianAngles}
  42902. * Returns an object containing start and end angles as radians.
  42903. */
  42904. function getStartAndEndRadians(start, end) {
  42905. const startAngle = isNumber(start) ? start : 0, // must be a number
  42906. endAngle = ((isNumber(end) && // must be a number
  42907. end > startAngle && // must be larger than the start angle
  42908. // difference must be less than 360 degrees
  42909. (end - startAngle) < 360) ?
  42910. end :
  42911. startAngle + 360), correction = -90;
  42912. return {
  42913. start: deg2rad * (startAngle + correction),
  42914. end: deg2rad * (endAngle + correction)
  42915. };
  42916. }
  42917. CenteredUtilities.getStartAndEndRadians = getStartAndEndRadians;
  42918. })(CenteredUtilities || (CenteredUtilities = {}));
  42919. /* *
  42920. *
  42921. * Default Export
  42922. *
  42923. * */
  42924. /* *
  42925. *
  42926. * API Declarations
  42927. *
  42928. * */
  42929. /**
  42930. * @private
  42931. * @interface Highcharts.RadianAngles
  42932. */ /**
  42933. * @name Highcharts.RadianAngles#end
  42934. * @type {number}
  42935. */ /**
  42936. * @name Highcharts.RadianAngles#start
  42937. * @type {number}
  42938. */
  42939. ''; // keeps doclets above in JS file
  42940. return CenteredUtilities;
  42941. });
  42942. _registerModule(_modules, 'Series/Pie/PiePoint.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (A, Point, U) {
  42943. /* *
  42944. *
  42945. * (c) 2010-2021 Torstein Honsi
  42946. *
  42947. * License: www.highcharts.com/license
  42948. *
  42949. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42950. *
  42951. * */
  42952. const { setAnimation } = A;
  42953. const { addEvent, defined, extend, isNumber, pick, relativeLength } = U;
  42954. /* *
  42955. *
  42956. * Class
  42957. *
  42958. * */
  42959. class PiePoint extends Point {
  42960. constructor() {
  42961. /* *
  42962. *
  42963. * Properties
  42964. *
  42965. * */
  42966. super(...arguments);
  42967. this.labelDistance = void 0;
  42968. this.options = void 0;
  42969. this.series = void 0;
  42970. }
  42971. /* *
  42972. *
  42973. * Functions
  42974. *
  42975. * */
  42976. /* eslint-disable valid-jsdoc */
  42977. /**
  42978. * Extendable method for getting the path of the connector between the
  42979. * data label and the pie slice.
  42980. * @private
  42981. */
  42982. getConnectorPath() {
  42983. const labelPosition = this.labelPosition, options = this.series.options.dataLabels, predefinedShapes = this.connectorShapes;
  42984. let connectorShape = options.connectorShape;
  42985. // find out whether to use the predefined shape
  42986. if (predefinedShapes[connectorShape]) {
  42987. connectorShape = predefinedShapes[connectorShape];
  42988. }
  42989. return connectorShape.call(this, {
  42990. // pass simplified label position object for user's convenience
  42991. x: labelPosition.computed.x,
  42992. y: labelPosition.computed.y,
  42993. alignment: labelPosition.alignment
  42994. }, labelPosition.connectorPosition, options);
  42995. }
  42996. /**
  42997. * @private
  42998. */
  42999. getTranslate() {
  43000. return this.sliced ? this.slicedTranslation : {
  43001. translateX: 0,
  43002. translateY: 0
  43003. };
  43004. }
  43005. /**
  43006. * @private
  43007. */
  43008. haloPath(size) {
  43009. const shapeArgs = this.shapeArgs;
  43010. return this.sliced || !this.visible ?
  43011. [] :
  43012. this.series.chart.renderer.symbols.arc(shapeArgs.x, shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
  43013. // Substract 1px to ensure the background is not bleeding
  43014. // through between the halo and the slice (#7495).
  43015. innerR: shapeArgs.r - 1,
  43016. start: shapeArgs.start,
  43017. end: shapeArgs.end,
  43018. borderRadius: shapeArgs.borderRadius
  43019. });
  43020. }
  43021. /**
  43022. * Initialize the pie slice.
  43023. * @private
  43024. */
  43025. init() {
  43026. super.init.apply(this, arguments);
  43027. this.name = pick(this.name, 'Slice');
  43028. // add event listener for select
  43029. const toggleSlice = (e) => {
  43030. this.slice(e.type === 'select');
  43031. };
  43032. addEvent(this, 'select', toggleSlice);
  43033. addEvent(this, 'unselect', toggleSlice);
  43034. return this;
  43035. }
  43036. /**
  43037. * Negative points are not valid (#1530, #3623, #5322)
  43038. * @private
  43039. */
  43040. isValid() {
  43041. return isNumber(this.y) && this.y >= 0;
  43042. }
  43043. /**
  43044. * Toggle the visibility of a pie slice or other data point. Note that this
  43045. * method is available only for some series, like pie, treemap and sunburst.
  43046. *
  43047. * @function Highcharts.Point#setVisible
  43048. *
  43049. * @param {boolean} [vis]
  43050. * True to show the pie slice or other data point, false to hide. If
  43051. * undefined, the visibility is toggled.
  43052. *
  43053. * @param {boolean} [redraw] Whether to redraw the chart after the point is
  43054. * altered. If doing more operations on the chart, it is a good idea to set
  43055. * redraw to false and call {@link Chart#redraw|chart.redraw()} after.
  43056. *
  43057. */
  43058. setVisible(vis, redraw) {
  43059. const series = this.series, chart = series.chart, ignoreHiddenPoint = series.options.ignoreHiddenPoint;
  43060. redraw = pick(redraw, ignoreHiddenPoint);
  43061. if (vis !== this.visible) {
  43062. // If called without an argument, toggle visibility
  43063. this.visible = this.options.visible = vis =
  43064. typeof vis === 'undefined' ? !this.visible : vis;
  43065. // update userOptions.data
  43066. series.options.data[series.data.indexOf(this)] =
  43067. this.options;
  43068. // Show and hide associated elements. This is performed
  43069. // regardless of redraw or not, because chart.redraw only
  43070. // handles full series.
  43071. ['graphic', 'dataLabel', 'connector'].forEach((key) => {
  43072. if (this[key]) {
  43073. this[key][vis ? 'show' : 'hide'](vis);
  43074. }
  43075. });
  43076. if (this.legendItem) {
  43077. chart.legend.colorizeItem(this, vis);
  43078. }
  43079. // #4170, hide halo after hiding point
  43080. if (!vis && this.state === 'hover') {
  43081. this.setState('');
  43082. }
  43083. // Handle ignore hidden slices
  43084. if (ignoreHiddenPoint) {
  43085. series.isDirty = true;
  43086. }
  43087. if (redraw) {
  43088. chart.redraw();
  43089. }
  43090. }
  43091. }
  43092. /**
  43093. * Set or toggle whether the slice is cut out from the pie.
  43094. * @private
  43095. *
  43096. * @param {boolean} sliced
  43097. * When undefined, the slice state is toggled.
  43098. *
  43099. * @param {boolean} [redraw]
  43100. * Whether to redraw the chart. True by default.
  43101. *
  43102. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  43103. * Animation options.
  43104. */
  43105. slice(sliced, redraw, animation) {
  43106. const series = this.series, chart = series.chart;
  43107. setAnimation(animation, chart);
  43108. // redraw is true by default
  43109. redraw = pick(redraw, true);
  43110. /**
  43111. * Pie series only. Whether to display a slice offset from the
  43112. * center.
  43113. * @name Highcharts.Point#sliced
  43114. * @type {boolean|undefined}
  43115. */
  43116. // if called without an argument, toggle
  43117. this.sliced = this.options.sliced = sliced =
  43118. defined(sliced) ? sliced : !this.sliced;
  43119. // update userOptions.data
  43120. series.options.data[series.data.indexOf(this)] =
  43121. this.options;
  43122. if (this.graphic) {
  43123. this.graphic.animate(this.getTranslate());
  43124. }
  43125. }
  43126. }
  43127. extend(PiePoint.prototype, {
  43128. connectorShapes: {
  43129. // only one available before v7.0.0
  43130. fixedOffset: function (labelPosition, connectorPosition, options) {
  43131. const breakAt = connectorPosition.breakAt, touchingSliceAt = connectorPosition.touchingSliceAt, lineSegment = options.softConnector ? [
  43132. 'C',
  43133. // 1st control point (of the curve)
  43134. labelPosition.x +
  43135. // 5 gives the connector a little horizontal bend
  43136. (labelPosition.alignment === 'left' ? -5 : 5),
  43137. labelPosition.y,
  43138. 2 * breakAt.x - touchingSliceAt.x,
  43139. 2 * breakAt.y - touchingSliceAt.y,
  43140. breakAt.x,
  43141. breakAt.y //
  43142. ] : [
  43143. 'L',
  43144. breakAt.x,
  43145. breakAt.y
  43146. ];
  43147. // assemble the path
  43148. return ([
  43149. ['M', labelPosition.x, labelPosition.y],
  43150. lineSegment,
  43151. ['L', touchingSliceAt.x, touchingSliceAt.y]
  43152. ]);
  43153. },
  43154. straight: function (labelPosition, connectorPosition) {
  43155. const touchingSliceAt = connectorPosition.touchingSliceAt;
  43156. // direct line to the slice
  43157. return [
  43158. ['M', labelPosition.x, labelPosition.y],
  43159. ['L', touchingSliceAt.x, touchingSliceAt.y]
  43160. ];
  43161. },
  43162. crookedLine: function (labelPosition, connectorPosition, options) {
  43163. const { breakAt, touchingSliceAt } = connectorPosition, { series } = this, [cx, cy, diameter] = series.center, r = diameter / 2, plotWidth = series.chart.plotWidth, plotLeft = series.chart.plotLeft, leftAligned = labelPosition.alignment === 'left', { x, y } = labelPosition;
  43164. let crookX = breakAt.x;
  43165. if (options.crookDistance) {
  43166. const crookDistance = relativeLength(// % to fraction
  43167. options.crookDistance, 1);
  43168. crookX = leftAligned ?
  43169. cx +
  43170. r +
  43171. (plotWidth + plotLeft - cx - r) * (1 - crookDistance) :
  43172. plotLeft + (cx - r) * crookDistance;
  43173. // When the crookDistance option is undefined, make the bend in the
  43174. // intersection between the radial line in the middle of the slice,
  43175. // and the extension of the label position.
  43176. }
  43177. else {
  43178. crookX = cx + (cy - y) * Math.tan((this.angle || 0) - Math.PI / 2);
  43179. }
  43180. const path = [['M', x, y]];
  43181. // The crookedLine formula doesn't make sense if the path overlaps
  43182. // the label - use straight line instead in that case
  43183. if (leftAligned ?
  43184. (crookX <= x && crookX >= breakAt.x) :
  43185. (crookX >= x && crookX <= breakAt.x)) {
  43186. path.push(['L', crookX, y]);
  43187. }
  43188. path.push(['L', breakAt.x, breakAt.y], ['L', touchingSliceAt.x, touchingSliceAt.y]);
  43189. return path;
  43190. }
  43191. }
  43192. });
  43193. /* *
  43194. *
  43195. * Default Export
  43196. *
  43197. * */
  43198. return PiePoint;
  43199. });
  43200. _registerModule(_modules, 'Series/Pie/PieSeriesDefaults.js', [], function () {
  43201. /* *
  43202. *
  43203. * (c) 2010-2021 Torstein Honsi
  43204. *
  43205. * License: www.highcharts.com/license
  43206. *
  43207. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43208. *
  43209. * */
  43210. /* *
  43211. *
  43212. * API Options
  43213. *
  43214. * */
  43215. /**
  43216. * A pie chart is a circular graphic which is divided into slices to
  43217. * illustrate numerical proportion.
  43218. *
  43219. * @sample highcharts/demo/pie-basic/
  43220. * Pie chart
  43221. *
  43222. * @extends plotOptions.line
  43223. * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
  43224. * cropThreshold, dashStyle, dataSorting, dragDrop,
  43225. * findNearestPointBy, getExtremesFromAll, label, lineWidth,
  43226. * linkedTo, marker, negativeColor, pointInterval,
  43227. * pointIntervalUnit, pointPlacement, pointStart,
  43228. * softThreshold, stacking, step, threshold, turboThreshold,
  43229. * zoneAxis, zones, dataSorting, boostBlending
  43230. * @product highcharts highmaps
  43231. * @optionparent plotOptions.pie
  43232. *
  43233. * @private
  43234. */
  43235. const PieSeriesDefaults = {
  43236. /**
  43237. * The corner radius of the border surrounding each slice. A number
  43238. * signifies pixels. A percentage string, like for example `50%`, signifies
  43239. * a size relative to the radius and the inner radius.
  43240. *
  43241. * @sample highcharts/plotoptions/series-border-radius
  43242. * Column and pie with rounded border
  43243. *
  43244. * @since 11.0.0
  43245. *
  43246. * @type {number|string|Highcharts.BorderRadiusOptionsObject}
  43247. */
  43248. borderRadius: 3,
  43249. /**
  43250. * @excluding legendItemClick
  43251. * @apioption plotOptions.pie.events
  43252. */
  43253. /**
  43254. * Fires when the checkbox next to the point name in the legend is
  43255. * clicked. One parameter, event, is passed to the function. The state
  43256. * of the checkbox is found by event.checked. The checked item is found
  43257. * by event.item. Return false to prevent the default action which is to
  43258. * toggle the select state of the series.
  43259. *
  43260. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  43261. * Alert checkbox status
  43262. *
  43263. * @type {Function}
  43264. * @since 1.2.0
  43265. * @product highcharts highmaps
  43266. * @context Highcharts.Point
  43267. * @apioption plotOptions.pie.events.checkboxClick
  43268. */
  43269. /**
  43270. * Fires when the legend item belonging to the pie point (slice) is
  43271. * clicked. The `this` keyword refers to the point itself. One
  43272. * parameter, `event`, is passed to the function, containing common
  43273. * event information. The default action is to toggle the visibility of
  43274. * the point. This can be prevented by calling `event.preventDefault()`.
  43275. *
  43276. * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
  43277. * Confirm toggle visibility
  43278. *
  43279. * @type {Highcharts.PointLegendItemClickCallbackFunction}
  43280. * @since 1.2.0
  43281. * @product highcharts highmaps
  43282. * @apioption plotOptions.pie.point.events.legendItemClick
  43283. */
  43284. /**
  43285. * The center of the pie chart relative to the plot area. Can be
  43286. * percentages or pixel values. The default behaviour (as of 3.0) is to
  43287. * center the pie so that all slices and data labels are within the plot
  43288. * area. As a consequence, the pie may actually jump around in a chart
  43289. * with dynamic values, as the data labels move. In that case, the
  43290. * center should be explicitly set, for example to `["50%", "50%"]`.
  43291. *
  43292. * @sample {highcharts} highcharts/plotoptions/pie-center/
  43293. * Centered at 100, 100
  43294. *
  43295. * @type {Array<(number|string|null),(number|string|null)>}
  43296. * @default [null, null]
  43297. * @product highcharts highmaps
  43298. *
  43299. * @private
  43300. */
  43301. center: [null, null],
  43302. /**
  43303. * The color of the pie series. A pie series is represented as an empty
  43304. * circle if the total sum of its values is 0. Use this property to
  43305. * define the color of its border.
  43306. *
  43307. * In styled mode, the color can be defined by the
  43308. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  43309. * color can be set with the `.highcharts-series`,
  43310. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  43311. * `.highcharts-series-{n}` class, or individual classes given by the
  43312. * `className` option.
  43313. *
  43314. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  43315. * Empty pie series
  43316. *
  43317. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43318. * @default #cccccc
  43319. * @apioption plotOptions.pie.color
  43320. */
  43321. /**
  43322. * @product highcharts
  43323. *
  43324. * @private
  43325. */
  43326. clip: false,
  43327. /**
  43328. * @ignore-option
  43329. *
  43330. * @private
  43331. */
  43332. colorByPoint: true,
  43333. /**
  43334. * A series specific or series type specific color set to use instead
  43335. * of the global [colors](#colors).
  43336. *
  43337. * @sample {highcharts} highcharts/demo/pie-monochrome/
  43338. * Set default colors for all pies
  43339. *
  43340. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  43341. * @since 3.0
  43342. * @product highcharts highmaps
  43343. * @apioption plotOptions.pie.colors
  43344. */
  43345. /**
  43346. * @declare Highcharts.SeriesPieDataLabelsOptionsObject
  43347. * @extends plotOptions.series.dataLabels
  43348. * @excluding align, allowOverlap, inside, staggerLines, step
  43349. * @private
  43350. */
  43351. dataLabels: {
  43352. /**
  43353. * Alignment method for data labels. Possible values are:
  43354. *
  43355. * - `plotEdges`: Each label touches the nearest vertical edge of
  43356. * the plot area.
  43357. *
  43358. * - `connectors`: Connectors have the same x position and the
  43359. * widest label of each half (left & right) touches the nearest
  43360. * vertical edge of the plot area.
  43361. *
  43362. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/
  43363. * alignTo: connectors
  43364. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/
  43365. * alignTo: plotEdges
  43366. *
  43367. * @type {string}
  43368. * @since 7.0.0
  43369. * @product highcharts highmaps
  43370. * @apioption plotOptions.pie.dataLabels.alignTo
  43371. */
  43372. allowOverlap: true,
  43373. /**
  43374. * The color of the line connecting the data label to the pie slice.
  43375. * The default color is the same as the point's color.
  43376. *
  43377. * In styled mode, the connector stroke is given in the
  43378. * `.highcharts-data-label-connector` class.
  43379. *
  43380. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
  43381. * Blue connectors
  43382. * @sample {highcharts} highcharts/css/pie-point/
  43383. * Styled connectors
  43384. *
  43385. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43386. * @since 2.1
  43387. * @product highcharts highmaps
  43388. * @apioption plotOptions.pie.dataLabels.connectorColor
  43389. */
  43390. /**
  43391. * The distance from the data label to the connector. Note that
  43392. * data labels also have a default `padding`, so in order for the
  43393. * connector to touch the text, the `padding` must also be 0.
  43394. *
  43395. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
  43396. * No padding
  43397. *
  43398. * @since 2.1
  43399. * @product highcharts highmaps
  43400. */
  43401. connectorPadding: 5,
  43402. /**
  43403. * Specifies the method that is used to generate the connector path.
  43404. * Highcharts provides 3 built-in connector shapes: `'crookedLine'`
  43405. * (default since v11), `'fixedOffset'` and `'straight'`.
  43406. *
  43407. * Users can provide their own method by passing a function instead of a
  43408. * string. Three arguments are passed to the callback:
  43409. *
  43410. * - An object that holds the information about the coordinates of the
  43411. * label (`x` & `y` properties) and how the label is located in
  43412. * relation to the pie (`alignment` property). `alignment` can by one
  43413. * of the following: `'left'` (pie on the left side of the data
  43414. * label), `'right'` (pie on the right side of the data label) or
  43415. * `'center'` (data label overlaps the pie).
  43416. *
  43417. * - An object that holds the information about the position of the
  43418. * connector. Its `touchingSliceAt` porperty tells the position of
  43419. * the place where the connector touches the slice.
  43420. *
  43421. * - Data label options
  43422. *
  43423. * The function has to return an SVG path definition in array form (see
  43424. * the example).
  43425. *
  43426. * @sample {highcharts}
  43427. * highcharts/plotoptions/pie-datalabels-connectorshape-string/
  43428. * connectorShape is a String
  43429. * @sample {highcharts}
  43430. * highcharts/plotoptions/pie-datalabels-connectorshape-function/
  43431. * connectorShape is a function
  43432. *
  43433. * @type {string|Function}
  43434. * @since 7.0.0
  43435. * @product highcharts highmaps
  43436. */
  43437. connectorShape: 'crookedLine',
  43438. /**
  43439. * The width of the line connecting the data label to the pie slice.
  43440. *
  43441. * In styled mode, the connector stroke width is given in the
  43442. * `.highcharts-data-label-connector` class.
  43443. *
  43444. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
  43445. * Disable the connector
  43446. * @sample {highcharts} highcharts/css/pie-point/
  43447. * Styled connectors
  43448. *
  43449. * @type {number}
  43450. * @default 1
  43451. * @since 2.1
  43452. * @product highcharts highmaps
  43453. * @apioption plotOptions.pie.dataLabels.connectorWidth
  43454. */
  43455. /**
  43456. * Works only if `connectorShape` is `'crookedLine'`. It defines how
  43457. * far from the vertical plot edge the coonnector path should be
  43458. * crooked. With the default, `undefined`, the crook is placed so that
  43459. * the horizontal line from the label intersects with the radial line
  43460. * extending through the center of the pie slice.
  43461. *
  43462. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
  43463. * crookDistance set to 90%
  43464. *
  43465. * @since 7.0.0
  43466. * @product highcharts highmaps
  43467. */
  43468. crookDistance: void 0,
  43469. /**
  43470. * The distance of the data label from the pie's edge. Negative
  43471. * numbers put the data label on top of the pie slices. Can also be
  43472. * defined as a percentage of pie's radius. Connectors are only
  43473. * shown for data labels outside the pie.
  43474. *
  43475. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
  43476. * Data labels on top of the pie
  43477. *
  43478. * @type {number|string}
  43479. * @since 2.1
  43480. * @product highcharts highmaps
  43481. */
  43482. distance: 30,
  43483. enabled: true,
  43484. /**
  43485. * A
  43486. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  43487. * for the data label. Available variables are the same as for
  43488. * `formatter`.
  43489. *
  43490. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  43491. * Add a unit
  43492. *
  43493. * @type {string}
  43494. * @default undefined
  43495. * @since 3.0
  43496. * @apioption plotOptions.pie.dataLabels.format
  43497. */
  43498. // eslint-disable-next-line valid-jsdoc
  43499. /**
  43500. * Callback JavaScript function to format the data label. Note that
  43501. * if a `format` is defined, the format takes precedence and the
  43502. * formatter is ignored.
  43503. *
  43504. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  43505. * @default function () { return this.point.isNull ? void 0 : this.point.name; }
  43506. */
  43507. formatter: function () {
  43508. return this.point.isNull ? void 0 : this.point.name;
  43509. },
  43510. /**
  43511. * Whether to render the connector as a soft arc or a line with a sharp
  43512. * break. Works only if `connectorShape` equals to `fixedOffset`.
  43513. *
  43514. * @sample {highcharts}
  43515. * highcharts/plotoptions/pie-datalabels-softconnector-true/
  43516. * Soft
  43517. * @sample {highcharts}
  43518. * highcharts/plotoptions/pie-datalabels-softconnector-false/
  43519. * Non soft
  43520. *
  43521. * @since 2.1.7
  43522. * @product highcharts highmaps
  43523. */
  43524. softConnector: true,
  43525. /**
  43526. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow
  43527. * Long labels truncated with an ellipsis
  43528. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap
  43529. * Long labels are wrapped
  43530. *
  43531. * @type {Highcharts.CSSObject}
  43532. * @apioption plotOptions.pie.dataLabels.style
  43533. */
  43534. x: 0
  43535. },
  43536. /**
  43537. * If the total sum of the pie's values is 0, the series is represented
  43538. * as an empty circle . The `fillColor` option defines the color of that
  43539. * circle. Use [pie.borderWidth](#plotOptions.pie.borderWidth) to set
  43540. * the border thickness.
  43541. *
  43542. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  43543. * Empty pie series
  43544. *
  43545. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43546. * @private
  43547. */
  43548. fillColor: void 0,
  43549. /**
  43550. * The end angle of the pie in degrees where 0 is top and 90 is right.
  43551. * Defaults to `startAngle` plus 360.
  43552. *
  43553. * @sample {highcharts} highcharts/demo/pie-semi-circle/
  43554. * Semi-circle donut
  43555. *
  43556. * @type {number}
  43557. * @since 1.3.6
  43558. * @product highcharts highmaps
  43559. * @apioption plotOptions.pie.endAngle
  43560. */
  43561. /**
  43562. * Thickness describing the ring size for a donut type chart,
  43563. * overriding [innerSize](#plotOptions.pie.innerSize).
  43564. *
  43565. * @type {number}
  43566. * @default undefined
  43567. * @product highcharts
  43568. * @since 10.1.0
  43569. * @apioption plotOptions.pie.thickness
  43570. * @private
  43571. */
  43572. /**
  43573. * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
  43574. * this option tells whether the series shall be redrawn as if the
  43575. * hidden point were `null`.
  43576. *
  43577. * The default value changed from `false` to `true` with Highcharts
  43578. * 3.0.
  43579. *
  43580. * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
  43581. * True, the hiddden point is ignored
  43582. *
  43583. * @since 2.3.0
  43584. * @product highcharts highmaps
  43585. *
  43586. * @private
  43587. */
  43588. ignoreHiddenPoint: true,
  43589. /**
  43590. * @ignore-option
  43591. *
  43592. * @private
  43593. */
  43594. inactiveOtherPoints: true,
  43595. /**
  43596. * The size of the inner diameter for the pie. A size greater than 0
  43597. * renders a donut chart. Can be a percentage or pixel value.
  43598. * Percentages are relative to the pie size. Pixel values are given as
  43599. * integers. Setting overridden by thickness.
  43600. *
  43601. *
  43602. * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
  43603. * area, not the pie size.
  43604. *
  43605. * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
  43606. * 80px inner size
  43607. * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
  43608. * 50% of the plot area
  43609. * @sample {highcharts} highcharts/demo/3d-pie-donut/
  43610. * 3D donut
  43611. *
  43612. * @type {number|string}
  43613. * @default 0
  43614. * @since 2.0
  43615. * @product highcharts highmaps
  43616. * @apioption plotOptions.pie.innerSize
  43617. */
  43618. /**
  43619. * @ignore-option
  43620. *
  43621. * @private
  43622. */
  43623. legendType: 'point',
  43624. /**
  43625. * @ignore-option
  43626. *
  43627. * @private
  43628. */
  43629. marker: null,
  43630. /**
  43631. * The minimum size for a pie in response to auto margins. The pie will
  43632. * try to shrink to make room for data labels in side the plot area,
  43633. * but only to this size.
  43634. *
  43635. * @type {number|string}
  43636. * @default 80
  43637. * @since 3.0
  43638. * @product highcharts highmaps
  43639. * @apioption plotOptions.pie.minSize
  43640. */
  43641. /**
  43642. * The diameter of the pie relative to the plot area. Can be a
  43643. * percentage or pixel value. Pixel values are given as integers. The
  43644. * default behaviour (as of 3.0) is to scale to the plot area and give
  43645. * room for data labels within the plot area.
  43646. * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
  43647. * default size calculation. As a consequence, the size of the pie may
  43648. * vary when points are updated and data labels more around. In that
  43649. * case it is best to set a fixed value, for example `"75%"`.
  43650. *
  43651. * @sample {highcharts} highcharts/plotoptions/pie-size/
  43652. * Smaller pie
  43653. *
  43654. * @type {number|string|null}
  43655. * @product highcharts highmaps
  43656. *
  43657. * @private
  43658. */
  43659. size: null,
  43660. /**
  43661. * Whether to display this particular series or series type in the
  43662. * legend. Since 2.1, pies are not shown in the legend by default.
  43663. *
  43664. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  43665. * One series in the legend, one hidden
  43666. *
  43667. * @product highcharts highmaps
  43668. *
  43669. * @private
  43670. */
  43671. showInLegend: false,
  43672. /**
  43673. * If a point is sliced, moved out from the center, how many pixels
  43674. * should it be moved?.
  43675. *
  43676. * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
  43677. * 20px offset
  43678. *
  43679. * @product highcharts highmaps
  43680. *
  43681. * @private
  43682. */
  43683. slicedOffset: 10,
  43684. /**
  43685. * The start angle of the pie slices in degrees where 0 is top and 90
  43686. * right.
  43687. *
  43688. * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
  43689. * Start from right
  43690. *
  43691. * @type {number}
  43692. * @default 0
  43693. * @since 2.3.4
  43694. * @product highcharts highmaps
  43695. * @apioption plotOptions.pie.startAngle
  43696. */
  43697. /**
  43698. * Sticky tracking of mouse events. When true, the `mouseOut` event
  43699. * on a series isn't triggered until the mouse moves over another
  43700. * series, or out of the plot area. When false, the `mouseOut` event on
  43701. * a series is triggered when the mouse leaves the area around the
  43702. * series' graph or markers. This also implies the tooltip. When
  43703. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  43704. * will be hidden when moving the mouse between series.
  43705. *
  43706. * @product highcharts highmaps
  43707. *
  43708. * @private
  43709. */
  43710. stickyTracking: false,
  43711. tooltip: {
  43712. followPointer: true
  43713. },
  43714. /**
  43715. * The color of the border surrounding each slice. When `null`, the
  43716. * border takes the same color as the slice fill. This can be used
  43717. * together with a `borderWidth` to fill drawing gaps created by
  43718. * antialiazing artefacts in borderless pies.
  43719. *
  43720. * In styled mode, the border stroke is given in the `.highcharts-point`
  43721. * class.
  43722. *
  43723. * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
  43724. * Black border
  43725. *
  43726. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43727. * @default #ffffff
  43728. * @product highcharts highmaps
  43729. *
  43730. * @private
  43731. */
  43732. borderColor: "#ffffff" /* Palette.backgroundColor */,
  43733. /**
  43734. * The width of the border surrounding each slice.
  43735. *
  43736. * When setting the border width to 0, there may be small gaps between
  43737. * the slices due to SVG antialiasing artefacts. To work around this,
  43738. * keep the border width at 0.5 or 1, but set the `borderColor` to
  43739. * `null` instead.
  43740. *
  43741. * In styled mode, the border stroke width is given in the
  43742. * `.highcharts-point` class.
  43743. *
  43744. * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
  43745. * 3px border
  43746. *
  43747. * @product highcharts highmaps
  43748. *
  43749. * @private
  43750. */
  43751. borderWidth: 1,
  43752. /**
  43753. * @ignore-option
  43754. * @private
  43755. */
  43756. lineWidth: void 0,
  43757. states: {
  43758. /**
  43759. * @extends plotOptions.series.states.hover
  43760. * @excluding marker, lineWidth, lineWidthPlus
  43761. * @product highcharts highmaps
  43762. */
  43763. hover: {
  43764. /**
  43765. * How much to brighten the point on interaction. Requires the
  43766. * main color to be defined in hex or rgb(a) format.
  43767. *
  43768. * In styled mode, the hover brightness is by default replaced
  43769. * by a fill-opacity given in the `.highcharts-point-hover`
  43770. * class.
  43771. *
  43772. * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
  43773. * Brightened by 0.5
  43774. *
  43775. * @product highcharts highmaps
  43776. */
  43777. brightness: 0.1
  43778. }
  43779. }
  43780. };
  43781. /**
  43782. * A `pie` series. If the [type](#series.pie.type) option is not specified,
  43783. * it is inherited from [chart.type](#chart.type).
  43784. *
  43785. * @extends series,plotOptions.pie
  43786. * @excluding cropThreshold, dataParser, dataURL, linkedTo, stack, xAxis, yAxis,
  43787. * dataSorting, step, boostThreshold, boostBlending
  43788. * @product highcharts highmaps
  43789. * @apioption series.pie
  43790. */
  43791. /**
  43792. * An array of data points for the series. For the `pie` series type,
  43793. * points can be given in the following ways:
  43794. *
  43795. * 1. An array of numerical values. In this case, the numerical values will be
  43796. * interpreted as `y` options. Example:
  43797. * ```js
  43798. * data: [0, 5, 3, 5]
  43799. * ```
  43800. *
  43801. * 2. An array of objects with named values. The following snippet shows only a
  43802. * few settings, see the complete options set below. If the total number of
  43803. * data points exceeds the series'
  43804. * [turboThreshold](#series.pie.turboThreshold),
  43805. * this option is not available.
  43806. * ```js
  43807. * data: [{
  43808. * y: 1,
  43809. * name: "Point2",
  43810. * color: "#00FF00"
  43811. * }, {
  43812. * y: 7,
  43813. * name: "Point1",
  43814. * color: "#FF00FF"
  43815. * }]
  43816. * ```
  43817. *
  43818. * @sample {highcharts} highcharts/chart/reflow-true/
  43819. * Numerical values
  43820. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  43821. * Arrays of numeric x and y
  43822. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  43823. * Arrays of datetime x and y
  43824. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  43825. * Arrays of point.name and y
  43826. * @sample {highcharts} highcharts/series/data-array-of-objects/
  43827. * Config objects
  43828. *
  43829. * @type {Array<number|Array<string,(number|null)>|null|*>}
  43830. * @extends series.line.data
  43831. * @excluding marker, x
  43832. * @product highcharts highmaps
  43833. * @apioption series.pie.data
  43834. */
  43835. /**
  43836. * @type {Highcharts.SeriesPieDataLabelsOptionsObject}
  43837. * @product highcharts highmaps
  43838. * @apioption series.pie.data.dataLabels
  43839. */
  43840. /**
  43841. * The sequential index of the data point in the legend.
  43842. *
  43843. * @type {number}
  43844. * @product highcharts highmaps
  43845. * @apioption series.pie.data.legendIndex
  43846. */
  43847. /**
  43848. * Whether to display a slice offset from the center.
  43849. *
  43850. * @sample {highcharts} highcharts/point/sliced/
  43851. * One sliced point
  43852. *
  43853. * @type {boolean}
  43854. * @product highcharts highmaps
  43855. * @apioption series.pie.data.sliced
  43856. */
  43857. /**
  43858. * @extends plotOptions.pie.dataLabels
  43859. * @excluding align, allowOverlap, inside, staggerLines, step
  43860. * @product highcharts highmaps
  43861. * @apioption series.pie.dataLabels
  43862. */
  43863. /**
  43864. * @excluding legendItemClick
  43865. * @product highcharts highmaps
  43866. * @apioption series.pie.events
  43867. */
  43868. ''; // placeholder for transpiled doclets above
  43869. /* *
  43870. *
  43871. * Default Export
  43872. *
  43873. * */
  43874. return PieSeriesDefaults;
  43875. });
  43876. _registerModule(_modules, 'Series/Pie/PieSeries.js', [_modules['Series/CenteredUtilities.js'], _modules['Series/Column/ColumnSeries.js'], _modules['Core/Globals.js'], _modules['Series/Pie/PiePoint.js'], _modules['Series/Pie/PieSeriesDefaults.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/Symbols.js'], _modules['Core/Utilities.js']], function (CU, ColumnSeries, H, PiePoint, PieSeriesDefaults, Series, SeriesRegistry, Symbols, U) {
  43877. /* *
  43878. *
  43879. * (c) 2010-2021 Torstein Honsi
  43880. *
  43881. * License: www.highcharts.com/license
  43882. *
  43883. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43884. *
  43885. * */
  43886. const { getStartAndEndRadians } = CU;
  43887. const { noop } = H;
  43888. const { clamp, extend, fireEvent, merge, pick, relativeLength } = U;
  43889. /* *
  43890. *
  43891. * Class
  43892. *
  43893. * */
  43894. /**
  43895. * Pie series type.
  43896. *
  43897. * @private
  43898. * @class
  43899. * @name Highcharts.seriesTypes.pie
  43900. *
  43901. * @augments Highcharts.Series
  43902. */
  43903. class PieSeries extends Series {
  43904. constructor() {
  43905. /* *
  43906. *
  43907. * Static Properties
  43908. *
  43909. * */
  43910. super(...arguments);
  43911. /* *
  43912. *
  43913. * Properties
  43914. *
  43915. * */
  43916. this.center = void 0;
  43917. this.data = void 0;
  43918. this.maxLabelDistance = void 0;
  43919. this.options = void 0;
  43920. this.points = void 0;
  43921. /* eslint-enable valid-jsdoc */
  43922. }
  43923. /* *
  43924. *
  43925. * Functions
  43926. *
  43927. * */
  43928. /* eslint-disable valid-jsdoc */
  43929. /**
  43930. * Animates the pies in.
  43931. * @private
  43932. */
  43933. animate(init) {
  43934. const series = this, points = series.points, startAngleRad = series.startAngleRad;
  43935. if (!init) {
  43936. points.forEach(function (point) {
  43937. const graphic = point.graphic, args = point.shapeArgs;
  43938. if (graphic && args) {
  43939. // start values
  43940. graphic.attr({
  43941. // animate from inner radius (#779)
  43942. r: pick(point.startR, (series.center && series.center[3] / 2)),
  43943. start: startAngleRad,
  43944. end: startAngleRad
  43945. });
  43946. // animate
  43947. graphic.animate({
  43948. r: args.r,
  43949. start: args.start,
  43950. end: args.end
  43951. }, series.options.animation);
  43952. }
  43953. });
  43954. }
  43955. }
  43956. /**
  43957. * Called internally to draw auxiliary graph in pie-like series in
  43958. * situtation when the default graph is not sufficient enough to present
  43959. * the data well. Auxiliary graph is saved in the same object as
  43960. * regular graph.
  43961. * @private
  43962. */
  43963. drawEmpty() {
  43964. const start = this.startAngleRad, end = this.endAngleRad, options = this.options;
  43965. let centerX, centerY;
  43966. // Draw auxiliary graph if there're no visible points.
  43967. if (this.total === 0 && this.center) {
  43968. centerX = this.center[0];
  43969. centerY = this.center[1];
  43970. if (!this.graph) {
  43971. this.graph = this.chart.renderer
  43972. .arc(centerX, centerY, this.center[1] / 2, 0, start, end)
  43973. .addClass('highcharts-empty-series')
  43974. .add(this.group);
  43975. }
  43976. this.graph.attr({
  43977. d: Symbols.arc(centerX, centerY, this.center[2] / 2, 0, {
  43978. start,
  43979. end,
  43980. innerR: this.center[3] / 2
  43981. })
  43982. });
  43983. if (!this.chart.styledMode) {
  43984. this.graph.attr({
  43985. 'stroke-width': options.borderWidth,
  43986. fill: options.fillColor || 'none',
  43987. stroke: options.color || "#cccccc" /* Palette.neutralColor20 */
  43988. });
  43989. }
  43990. }
  43991. else if (this.graph) { // Destroy the graph object.
  43992. this.graph = this.graph.destroy();
  43993. }
  43994. }
  43995. /**
  43996. * Slices in pie chart are initialized in DOM, but it's shapes and
  43997. * animations are normally run in `drawPoints()`.
  43998. * @private
  43999. */
  44000. drawPoints() {
  44001. const renderer = this.chart.renderer;
  44002. this.points.forEach(function (point) {
  44003. // When updating a series between 2d and 3d or cartesian and
  44004. // polar, the shape type changes.
  44005. if (point.graphic && point.hasNewShapeType()) {
  44006. point.graphic = point.graphic.destroy();
  44007. }
  44008. if (!point.graphic) {
  44009. point.graphic = renderer[point.shapeType](point.shapeArgs)
  44010. .add(point.series.group);
  44011. point.delayedRendering = true;
  44012. }
  44013. });
  44014. }
  44015. /**
  44016. * Extend the generatePoints method by adding total and percentage
  44017. * properties to each point
  44018. * @private
  44019. */
  44020. generatePoints() {
  44021. super.generatePoints();
  44022. this.updateTotals();
  44023. }
  44024. /**
  44025. * Utility for getting the x value from a given y, used for
  44026. * anticollision logic in data labels. Added point for using specific
  44027. * points' label distance.
  44028. * @private
  44029. */
  44030. getX(y, left, point) {
  44031. const center = this.center,
  44032. // Variable pie has individual radius
  44033. radius = this.radii ?
  44034. this.radii[point.index] || 0 :
  44035. center[2] / 2;
  44036. const angle = Math.asin(clamp((y - center[1]) / (radius + point.labelDistance), -1, 1));
  44037. const x = center[0] +
  44038. (left ? -1 : 1) *
  44039. (Math.cos(angle) * (radius + point.labelDistance)) +
  44040. (point.labelDistance > 0 ?
  44041. (left ? -1 : 1) * this.options.dataLabels.padding :
  44042. 0);
  44043. return x;
  44044. }
  44045. /**
  44046. * Define hasData function for non-cartesian series. Returns true if the
  44047. * series has points at all.
  44048. * @private
  44049. */
  44050. hasData() {
  44051. return !!this.processedXData.length; // != 0
  44052. }
  44053. /**
  44054. * Draw the data points
  44055. * @private
  44056. */
  44057. redrawPoints() {
  44058. const series = this, chart = series.chart;
  44059. let groupTranslation, graphic, pointAttr, shapeArgs;
  44060. this.drawEmpty();
  44061. // Apply the drop-shadow to the group because otherwise each element
  44062. // would cast a shadow on others
  44063. if (series.group && !chart.styledMode) {
  44064. series.group.shadow(series.options.shadow);
  44065. }
  44066. // draw the slices
  44067. series.points.forEach(function (point) {
  44068. const animateTo = {};
  44069. graphic = point.graphic;
  44070. if (!point.isNull && graphic) {
  44071. shapeArgs = point.shapeArgs;
  44072. // If the point is sliced, use special translation, else use
  44073. // plot area translation
  44074. groupTranslation = point.getTranslate();
  44075. if (!chart.styledMode) {
  44076. pointAttr = series.pointAttribs(point, (point.selected && 'select'));
  44077. }
  44078. // Draw the slice
  44079. if (!point.delayedRendering) {
  44080. graphic
  44081. .setRadialReference(series.center);
  44082. if (!chart.styledMode) {
  44083. merge(true, animateTo, pointAttr);
  44084. }
  44085. merge(true, animateTo, shapeArgs, groupTranslation);
  44086. graphic.animate(animateTo);
  44087. }
  44088. else {
  44089. graphic
  44090. .setRadialReference(series.center)
  44091. .attr(shapeArgs)
  44092. .attr(groupTranslation);
  44093. if (!chart.styledMode) {
  44094. graphic
  44095. .attr(pointAttr)
  44096. .attr({ 'stroke-linejoin': 'round' });
  44097. }
  44098. point.delayedRendering = false;
  44099. }
  44100. graphic
  44101. .attr({
  44102. visibility: point.visible ? 'inherit' : 'hidden'
  44103. });
  44104. graphic.addClass(point.getClassName(), true);
  44105. }
  44106. else if (graphic) {
  44107. point.graphic = graphic.destroy();
  44108. }
  44109. });
  44110. }
  44111. /**
  44112. * Utility for sorting data labels.
  44113. * @private
  44114. */
  44115. sortByAngle(points, sign) {
  44116. points.sort(function (a, b) {
  44117. return ((typeof a.angle !== 'undefined') &&
  44118. (b.angle - a.angle) * sign);
  44119. });
  44120. }
  44121. /**
  44122. * Do translation for pie slices
  44123. * @private
  44124. */
  44125. translate(positions) {
  44126. fireEvent(this, 'translate');
  44127. this.generatePoints();
  44128. const series = this, precision = 1000, // issue #172
  44129. options = series.options, slicedOffset = options.slicedOffset, connectorOffset = slicedOffset + (options.borderWidth || 0), radians = getStartAndEndRadians(options.startAngle, options.endAngle), startAngleRad = series.startAngleRad = radians.start, endAngleRad = series.endAngleRad = radians.end, circ = endAngleRad - startAngleRad, // 2 * Math.PI,
  44130. points = series.points, labelDistance = options.dataLabels.distance, ignoreHiddenPoint = options.ignoreHiddenPoint, len = points.length;
  44131. let finalConnectorOffset, start, end, angle,
  44132. // the x component of the radius vector for a given point
  44133. radiusX, radiusY, i, point, cumulative = 0;
  44134. // Get positions - either an integer or a percentage string must be
  44135. // given. If positions are passed as a parameter, we're in a
  44136. // recursive loop for adjusting space for data labels.
  44137. if (!positions) {
  44138. series.center = positions = series.getCenter();
  44139. }
  44140. // Calculate the geometry for each point
  44141. for (i = 0; i < len; i++) {
  44142. point = points[i];
  44143. // set start and end angle
  44144. start = startAngleRad + (cumulative * circ);
  44145. if (point.isValid() &&
  44146. (!ignoreHiddenPoint || point.visible)) {
  44147. cumulative += point.percentage / 100;
  44148. }
  44149. end = startAngleRad + (cumulative * circ);
  44150. // set the shape
  44151. const shapeArgs = {
  44152. x: positions[0],
  44153. y: positions[1],
  44154. r: positions[2] / 2,
  44155. innerR: positions[3] / 2,
  44156. start: Math.round(start * precision) / precision,
  44157. end: Math.round(end * precision) / precision
  44158. };
  44159. point.shapeType = 'arc';
  44160. point.shapeArgs = shapeArgs;
  44161. // Used for distance calculation for specific point.
  44162. point.labelDistance = pick((point.options.dataLabels &&
  44163. point.options.dataLabels.distance), labelDistance);
  44164. // Compute point.labelDistance if it's defined as percentage
  44165. // of slice radius (#8854)
  44166. point.labelDistance = relativeLength(point.labelDistance, shapeArgs.r);
  44167. // Saved for later dataLabels distance calculation.
  44168. series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
  44169. // The angle must stay within -90 and 270 (#2645)
  44170. angle = (end + start) / 2;
  44171. if (angle > 1.5 * Math.PI) {
  44172. angle -= 2 * Math.PI;
  44173. }
  44174. else if (angle < -Math.PI / 2) {
  44175. angle += 2 * Math.PI;
  44176. }
  44177. // Center for the sliced out slice
  44178. point.slicedTranslation = {
  44179. translateX: Math.round(Math.cos(angle) * slicedOffset),
  44180. translateY: Math.round(Math.sin(angle) * slicedOffset)
  44181. };
  44182. // set the anchor point for tooltips
  44183. radiusX = Math.cos(angle) * positions[2] / 2;
  44184. radiusY = Math.sin(angle) * positions[2] / 2;
  44185. point.tooltipPos = [
  44186. positions[0] + radiusX * 0.7,
  44187. positions[1] + radiusY * 0.7
  44188. ];
  44189. point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
  44190. 1 :
  44191. 0;
  44192. point.angle = angle;
  44193. // Set the anchor point for data labels. Use point.labelDistance
  44194. // instead of labelDistance // #1174
  44195. // finalConnectorOffset - not override connectorOffset value.
  44196. finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
  44197. point.labelPosition = {
  44198. natural: {
  44199. // initial position of the data label - it's utilized for
  44200. // finding the final position for the label
  44201. x: positions[0] + radiusX + Math.cos(angle) *
  44202. point.labelDistance,
  44203. y: positions[1] + radiusY + Math.sin(angle) *
  44204. point.labelDistance
  44205. },
  44206. computed: {
  44207. // used for generating connector path -
  44208. // initialized later in drawDataLabels function
  44209. // x: undefined,
  44210. // y: undefined
  44211. },
  44212. // left - pie on the left side of the data label
  44213. // right - pie on the right side of the data label
  44214. // center - data label overlaps the pie
  44215. alignment: point.labelDistance < 0 ?
  44216. 'center' : point.half ? 'right' : 'left',
  44217. connectorPosition: {
  44218. breakAt: {
  44219. x: positions[0] + radiusX + Math.cos(angle) *
  44220. finalConnectorOffset,
  44221. y: positions[1] + radiusY + Math.sin(angle) *
  44222. finalConnectorOffset
  44223. },
  44224. touchingSliceAt: {
  44225. x: positions[0] + radiusX,
  44226. y: positions[1] + radiusY
  44227. }
  44228. }
  44229. };
  44230. }
  44231. fireEvent(series, 'afterTranslate');
  44232. }
  44233. /**
  44234. * Recompute total chart sum and update percentages of points.
  44235. * @private
  44236. */
  44237. updateTotals() {
  44238. const points = this.points, len = points.length, ignoreHiddenPoint = this.options.ignoreHiddenPoint;
  44239. let i, point, total = 0;
  44240. // Get the total sum
  44241. for (i = 0; i < len; i++) {
  44242. point = points[i];
  44243. if (point.isValid() &&
  44244. (!ignoreHiddenPoint || point.visible)) {
  44245. total += point.y;
  44246. }
  44247. }
  44248. this.total = total;
  44249. // Set each point's properties
  44250. for (i = 0; i < len; i++) {
  44251. point = points[i];
  44252. point.percentage =
  44253. (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
  44254. point.y / total * 100 :
  44255. 0;
  44256. point.total = total;
  44257. }
  44258. }
  44259. }
  44260. PieSeries.defaultOptions = merge(Series.defaultOptions, PieSeriesDefaults);
  44261. extend(PieSeries.prototype, {
  44262. axisTypes: [],
  44263. directTouch: true,
  44264. drawGraph: void 0,
  44265. drawTracker: ColumnSeries.prototype.drawTracker,
  44266. getCenter: CU.getCenter,
  44267. getSymbol: noop,
  44268. isCartesian: false,
  44269. noSharedTooltip: true,
  44270. pointAttribs: ColumnSeries.prototype.pointAttribs,
  44271. pointClass: PiePoint,
  44272. requireSorting: false,
  44273. searchPoint: noop,
  44274. trackerGroups: ['group', 'dataLabelsGroup']
  44275. });
  44276. SeriesRegistry.registerSeriesType('pie', PieSeries);
  44277. /* *
  44278. *
  44279. * Default Export
  44280. *
  44281. * */
  44282. return PieSeries;
  44283. });
  44284. _registerModule(_modules, 'Series/Pie/PieDataLabel.js', [_modules['Core/Series/DataLabel.js'], _modules['Core/Globals.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DataLabel, H, R, SeriesRegistry, U) {
  44285. /* *
  44286. *
  44287. * (c) 2010-2021 Torstein Honsi
  44288. *
  44289. * License: www.highcharts.com/license
  44290. *
  44291. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44292. *
  44293. * */
  44294. const { noop } = H;
  44295. const { distribute } = R;
  44296. const { series: Series } = SeriesRegistry;
  44297. const { arrayMax, clamp, defined, merge, pick, relativeLength } = U;
  44298. /* *
  44299. *
  44300. * Composition
  44301. *
  44302. * */
  44303. var ColumnDataLabel;
  44304. (function (ColumnDataLabel) {
  44305. /* *
  44306. *
  44307. * Constants
  44308. *
  44309. * */
  44310. const composedMembers = [];
  44311. const dataLabelPositioners = {
  44312. // Based on the value computed in Highcharts' distribute algorithm.
  44313. radialDistributionY: function (point) {
  44314. return point.top + point.distributeBox.pos;
  44315. },
  44316. // get the x - use the natural x position for labels near the
  44317. // top and bottom, to prevent the top and botton slice
  44318. // connectors from touching each other on either side
  44319. // Based on the value computed in Highcharts' distribute algorithm.
  44320. radialDistributionX: function (series, point, y, naturalY) {
  44321. return series.getX(y < point.top + 2 || y > point.bottom - 2 ?
  44322. naturalY :
  44323. y, point.half, point);
  44324. },
  44325. // dataLabels.distance determines the x position of the label
  44326. justify: function (point, radius, seriesCenter) {
  44327. return seriesCenter[0] + (point.half ? -1 : 1) *
  44328. (radius + point.labelDistance);
  44329. },
  44330. // Left edges of the left-half labels touch the left edge of the plot
  44331. // area. Right edges of the right-half labels touch the right edge of
  44332. // the plot area.
  44333. alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
  44334. const dataLabelWidth = dataLabel.getBBox().width;
  44335. return half ? dataLabelWidth + plotLeft :
  44336. plotWidth - dataLabelWidth - plotLeft;
  44337. },
  44338. // Connectors of each side end in the same x position. Labels are
  44339. // aligned to them. Left edge of the widest left-half label touches the
  44340. // left edge of the plot area. Right edge of the widest right-half label
  44341. // touches the right edge of the plot area.
  44342. alignToConnectors: function (points, half, plotWidth, plotLeft) {
  44343. let maxDataLabelWidth = 0, dataLabelWidth;
  44344. // find widest data label
  44345. points.forEach(function (point) {
  44346. dataLabelWidth = point.dataLabel.getBBox().width;
  44347. if (dataLabelWidth > maxDataLabelWidth) {
  44348. maxDataLabelWidth = dataLabelWidth;
  44349. }
  44350. });
  44351. return half ? maxDataLabelWidth + plotLeft :
  44352. plotWidth - maxDataLabelWidth - plotLeft;
  44353. }
  44354. };
  44355. /* *
  44356. *
  44357. * Functions
  44358. *
  44359. * */
  44360. /* eslint-disable valid-jsdoc */
  44361. /** @private */
  44362. function compose(PieSeriesClass) {
  44363. DataLabel.compose(Series);
  44364. if (U.pushUnique(composedMembers, PieSeriesClass)) {
  44365. const pieProto = PieSeriesClass.prototype;
  44366. pieProto.dataLabelPositioners = dataLabelPositioners;
  44367. pieProto.alignDataLabel = noop;
  44368. pieProto.drawDataLabels = drawDataLabels;
  44369. pieProto.placeDataLabels = placeDataLabels;
  44370. pieProto.verifyDataLabelOverflow = verifyDataLabelOverflow;
  44371. }
  44372. }
  44373. ColumnDataLabel.compose = compose;
  44374. /**
  44375. * Override the base drawDataLabels method by pie specific functionality
  44376. * @private
  44377. */
  44378. function drawDataLabels() {
  44379. const series = this, data = series.data, chart = series.chart, options = series.options.dataLabels || {}, connectorPadding = options.connectorPadding, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotLeft = chart.plotLeft, maxWidth = Math.round(chart.chartWidth / 3), seriesCenter = series.center, radius = seriesCenter[2] / 2, centerY = seriesCenter[1], halves = [
  44380. [],
  44381. [] // left
  44382. ], overflow = [0, 0, 0, 0], // top, right, bottom, left
  44383. dataLabelPositioners = series.dataLabelPositioners;
  44384. let point, connectorWidth, connector, dataLabel, dataLabelWidth,
  44385. // labelPos,
  44386. labelPosition, labelHeight,
  44387. // divide the points into right and left halves for anti collision
  44388. x, y, visibility, j, pointDataLabelsOptions;
  44389. // get out if not enabled
  44390. if (!series.visible ||
  44391. (!options.enabled &&
  44392. !series._hasPointLabels)) {
  44393. return;
  44394. }
  44395. // Reset all labels that have been shortened
  44396. data.forEach(function (point) {
  44397. if (point.dataLabel && point.visible && point.dataLabel.shortened) {
  44398. point.dataLabel
  44399. .attr({
  44400. width: 'auto'
  44401. }).css({
  44402. width: 'auto',
  44403. textOverflow: 'clip'
  44404. });
  44405. point.dataLabel.shortened = false;
  44406. }
  44407. });
  44408. // run parent method
  44409. Series.prototype.drawDataLabels.apply(series);
  44410. data.forEach(function (point) {
  44411. if (point.dataLabel) {
  44412. if (point.visible) { // #407, #2510
  44413. // Arrange points for detection collision
  44414. halves[point.half].push(point);
  44415. // Reset positions (#4905)
  44416. point.dataLabel._pos = null;
  44417. // Avoid long labels squeezing the pie size too far down
  44418. if (!defined(options.style.width) &&
  44419. !defined(point.options.dataLabels &&
  44420. point.options.dataLabels.style &&
  44421. point.options.dataLabels.style.width)) {
  44422. if (point.dataLabel.getBBox().width > maxWidth) {
  44423. point.dataLabel.css({
  44424. // Use a fraction of the maxWidth to avoid
  44425. // wrapping close to the end of the string.
  44426. width: Math.round(maxWidth * 0.7) + 'px'
  44427. });
  44428. point.dataLabel.shortened = true;
  44429. }
  44430. }
  44431. }
  44432. else {
  44433. point.dataLabel = point.dataLabel.destroy();
  44434. // Workaround to make pies destroy multiple datalabels
  44435. // correctly. This logic needs rewriting to support multiple
  44436. // datalabels fully.
  44437. if (point.dataLabels && point.dataLabels.length === 1) {
  44438. delete point.dataLabels;
  44439. }
  44440. }
  44441. }
  44442. });
  44443. /* Loop over the points in each half, starting from the top and bottom
  44444. * of the pie to detect overlapping labels.
  44445. */
  44446. halves.forEach((points, i) => {
  44447. const length = points.length, positions = [];
  44448. let top, bottom, naturalY, sideOverflow, size = 0, distributionLength;
  44449. if (!length) {
  44450. return;
  44451. }
  44452. // Sort by angle
  44453. series.sortByAngle(points, i - 0.5);
  44454. // Only do anti-collision when we have dataLabels outside the pie
  44455. // and have connectors. (#856)
  44456. if (series.maxLabelDistance > 0) {
  44457. top = Math.max(0, centerY - radius - series.maxLabelDistance);
  44458. bottom = Math.min(centerY + radius + series.maxLabelDistance, chart.plotHeight);
  44459. points.forEach(function (point) {
  44460. // check if specific points' label is outside the pie
  44461. if (point.labelDistance > 0 && point.dataLabel) {
  44462. // point.top depends on point.labelDistance value
  44463. // Used for calculation of y value in getX method
  44464. point.top = Math.max(0, centerY - radius - point.labelDistance);
  44465. point.bottom = Math.min(centerY + radius + point.labelDistance, chart.plotHeight);
  44466. size = point.dataLabel.getBBox().height || 21;
  44467. // point.positionsIndex is needed for getting index of
  44468. // parameter related to specific point inside positions
  44469. // array - not every point is in positions array.
  44470. point.distributeBox = {
  44471. target: point.labelPosition.natural.y -
  44472. point.top + size / 2,
  44473. size,
  44474. rank: point.y
  44475. };
  44476. positions.push(point.distributeBox);
  44477. }
  44478. });
  44479. distributionLength = bottom + size - top;
  44480. distribute(positions, distributionLength, distributionLength / 5);
  44481. }
  44482. // Now the used slots are sorted, fill them up sequentially
  44483. for (j = 0; j < length; j++) {
  44484. point = points[j];
  44485. // labelPos = point.labelPos;
  44486. labelPosition = point.labelPosition;
  44487. dataLabel = point.dataLabel;
  44488. visibility = point.visible === false ? 'hidden' : 'inherit';
  44489. naturalY = labelPosition.natural.y;
  44490. y = naturalY;
  44491. if (positions && defined(point.distributeBox)) {
  44492. if (typeof point.distributeBox.pos === 'undefined') {
  44493. visibility = 'hidden';
  44494. }
  44495. else {
  44496. labelHeight = point.distributeBox.size;
  44497. // Find label's y position
  44498. y = dataLabelPositioners
  44499. .radialDistributionY(point);
  44500. }
  44501. }
  44502. // It is needed to delete point.positionIndex for
  44503. // dynamically added points etc.
  44504. delete point.positionIndex; // @todo unused
  44505. // Find label's x position
  44506. // justify is undocumented in the API - preserve support for it
  44507. if (options.justify) {
  44508. x = dataLabelPositioners.justify(point, radius, seriesCenter);
  44509. }
  44510. else {
  44511. switch (options.alignTo) {
  44512. case 'connectors':
  44513. x = dataLabelPositioners.alignToConnectors(points, i, plotWidth, plotLeft);
  44514. break;
  44515. case 'plotEdges':
  44516. x = dataLabelPositioners.alignToPlotEdges(dataLabel, i, plotWidth, plotLeft);
  44517. break;
  44518. default:
  44519. x = dataLabelPositioners.radialDistributionX(series, point, y, naturalY);
  44520. }
  44521. }
  44522. // Record the placement and visibility
  44523. dataLabel._attr = {
  44524. visibility: visibility,
  44525. align: labelPosition.alignment
  44526. };
  44527. pointDataLabelsOptions = point.options.dataLabels || {};
  44528. dataLabel._pos = {
  44529. x: (x +
  44530. pick(pointDataLabelsOptions.x, options.x) + // (#12985)
  44531. ({
  44532. left: connectorPadding,
  44533. right: -connectorPadding
  44534. }[labelPosition.alignment] || 0)),
  44535. y: (y +
  44536. pick(pointDataLabelsOptions.y, options.y) - // (#12985)
  44537. // Vertically center
  44538. dataLabel.getBBox().height / 2)
  44539. };
  44540. // labelPos.x = x;
  44541. // labelPos.y = y;
  44542. if (labelPosition) {
  44543. labelPosition.computed.x = x;
  44544. labelPosition.computed.y = y;
  44545. }
  44546. // Detect overflowing data labels
  44547. if (pick(options.crop, true)) {
  44548. dataLabelWidth = dataLabel.getBBox().width;
  44549. sideOverflow = null;
  44550. // Overflow left
  44551. if (x - dataLabelWidth < connectorPadding &&
  44552. i === 1 // left half
  44553. ) {
  44554. sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
  44555. overflow[3] = Math.max(sideOverflow, overflow[3]);
  44556. // Overflow right
  44557. }
  44558. else if (x + dataLabelWidth > plotWidth - connectorPadding &&
  44559. i === 0 // right half
  44560. ) {
  44561. sideOverflow = Math.round(x + dataLabelWidth - plotWidth + connectorPadding);
  44562. overflow[1] = Math.max(sideOverflow, overflow[1]);
  44563. }
  44564. // Overflow top
  44565. if (y - labelHeight / 2 < 0) {
  44566. overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
  44567. // Overflow left
  44568. }
  44569. else if (y + labelHeight / 2 > plotHeight) {
  44570. overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
  44571. }
  44572. dataLabel.sideOverflow = sideOverflow;
  44573. }
  44574. } // for each point
  44575. }); // for each half
  44576. // Do not apply the final placement and draw the connectors until we
  44577. // have verified that labels are not spilling over.
  44578. if (arrayMax(overflow) === 0 ||
  44579. this.verifyDataLabelOverflow(overflow)) {
  44580. // Place the labels in the final position
  44581. this.placeDataLabels();
  44582. this.points.forEach(function (point) {
  44583. // #8864: every connector can have individual options
  44584. pointDataLabelsOptions =
  44585. merge(options, point.options.dataLabels);
  44586. connectorWidth =
  44587. pick(pointDataLabelsOptions.connectorWidth, 1);
  44588. // Draw the connector
  44589. if (connectorWidth) {
  44590. let isNew;
  44591. connector = point.connector;
  44592. dataLabel = point.dataLabel;
  44593. if (dataLabel &&
  44594. dataLabel._pos &&
  44595. point.visible &&
  44596. point.labelDistance > 0) {
  44597. visibility = dataLabel._attr.visibility;
  44598. isNew = !connector;
  44599. if (isNew) {
  44600. point.connector = connector = chart.renderer
  44601. .path()
  44602. .addClass('highcharts-data-label-connector ' +
  44603. ' highcharts-color-' + point.colorIndex +
  44604. (point.className ?
  44605. ' ' + point.className :
  44606. ''))
  44607. .add(series.dataLabelsGroup);
  44608. if (!chart.styledMode) {
  44609. connector.attr({
  44610. 'stroke-width': connectorWidth,
  44611. 'stroke': (pointDataLabelsOptions.connectorColor ||
  44612. point.color ||
  44613. "#666666" /* Palette.neutralColor60 */)
  44614. });
  44615. }
  44616. }
  44617. connector[isNew ? 'attr' : 'animate']({
  44618. d: point.getConnectorPath()
  44619. });
  44620. connector.attr('visibility', visibility);
  44621. }
  44622. else if (connector) {
  44623. point.connector = connector.destroy();
  44624. }
  44625. }
  44626. });
  44627. }
  44628. }
  44629. /**
  44630. * Perform the final placement of the data labels after we have verified
  44631. * that they fall within the plot area.
  44632. * @private
  44633. */
  44634. function placeDataLabels() {
  44635. this.points.forEach(function (point) {
  44636. let dataLabel = point.dataLabel, _pos;
  44637. if (dataLabel && point.visible) {
  44638. _pos = dataLabel._pos;
  44639. if (_pos) {
  44640. // Shorten data labels with ellipsis if they still overflow
  44641. // after the pie has reached minSize (#223).
  44642. if (dataLabel.sideOverflow) {
  44643. dataLabel._attr.width =
  44644. Math.max(dataLabel.getBBox().width -
  44645. dataLabel.sideOverflow, 0);
  44646. dataLabel.css({
  44647. width: dataLabel._attr.width + 'px',
  44648. textOverflow: ((this.options.dataLabels.style || {})
  44649. .textOverflow ||
  44650. 'ellipsis')
  44651. });
  44652. dataLabel.shortened = true;
  44653. }
  44654. dataLabel.attr(dataLabel._attr);
  44655. dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
  44656. dataLabel.moved = true;
  44657. }
  44658. else if (dataLabel) {
  44659. dataLabel.attr({ y: -9999 });
  44660. }
  44661. }
  44662. // Clear for update
  44663. delete point.distributeBox;
  44664. }, this);
  44665. }
  44666. /**
  44667. * Verify whether the data labels are allowed to draw, or we should run more
  44668. * translation and data label positioning to keep them inside the plot area.
  44669. * Returns true when data labels are ready to draw.
  44670. * @private
  44671. */
  44672. function verifyDataLabelOverflow(overflow) {
  44673. let center = this.center, options = this.options, centerOption = options.center, minSize = options.minSize || 80, newSize = minSize,
  44674. // If a size is set, return true and don't try to shrink the pie
  44675. // to fit the labels.
  44676. ret = options.size !== null;
  44677. if (!ret) {
  44678. // Handle horizontal size and center
  44679. if (centerOption[0] !== null) { // Fixed center
  44680. newSize = Math.max(center[2] -
  44681. Math.max(overflow[1], overflow[3]), minSize);
  44682. }
  44683. else { // Auto center
  44684. newSize = Math.max(
  44685. // horizontal overflow
  44686. center[2] - overflow[1] - overflow[3], minSize);
  44687. // horizontal center
  44688. center[0] += (overflow[3] - overflow[1]) / 2;
  44689. }
  44690. // Handle vertical size and center
  44691. if (centerOption[1] !== null) { // Fixed center
  44692. newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
  44693. }
  44694. else { // Auto center
  44695. newSize = clamp(newSize, minSize,
  44696. // vertical overflow
  44697. center[2] - overflow[0] - overflow[2]);
  44698. // vertical center
  44699. center[1] += (overflow[0] - overflow[2]) / 2;
  44700. }
  44701. // If the size must be decreased, we need to run translate and
  44702. // drawDataLabels again
  44703. if (newSize < center[2]) {
  44704. center[2] = newSize;
  44705. center[3] = Math.min(// #3632
  44706. options.thickness ?
  44707. Math.max(0, newSize - options.thickness * 2) :
  44708. Math.max(0, relativeLength(options.innerSize || 0, newSize)), newSize); // #6647
  44709. this.translate(center);
  44710. if (this.drawDataLabels) {
  44711. this.drawDataLabels();
  44712. }
  44713. // Else, return true to indicate that the pie and its labels is
  44714. // within the plot area
  44715. }
  44716. else {
  44717. ret = true;
  44718. }
  44719. }
  44720. return ret;
  44721. }
  44722. })(ColumnDataLabel || (ColumnDataLabel = {}));
  44723. /* *
  44724. *
  44725. * Default Export
  44726. *
  44727. * */
  44728. return ColumnDataLabel;
  44729. });
  44730. _registerModule(_modules, 'Extensions/OverlappingDataLabels.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js']], function (Chart, U) {
  44731. /* *
  44732. *
  44733. * Highcharts module to hide overlapping data labels.
  44734. * This module is included in Highcharts.
  44735. *
  44736. * (c) 2009-2021 Torstein Honsi
  44737. *
  44738. * License: www.highcharts.com/license
  44739. *
  44740. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44741. *
  44742. * */
  44743. const { addEvent, fireEvent, isArray, isNumber, objectEach, pick } = U;
  44744. /**
  44745. * Internal type
  44746. * @private
  44747. */
  44748. /* eslint-disable no-invalid-this */
  44749. // Collect potensial overlapping data labels. Stack labels probably don't need
  44750. // to be considered because they are usually accompanied by data labels that lie
  44751. // inside the columns.
  44752. addEvent(Chart, 'render', function collectAndHide() {
  44753. let chart = this, labels = [];
  44754. // Consider external label collectors
  44755. (this.labelCollectors || []).forEach(function (collector) {
  44756. labels = labels.concat(collector());
  44757. });
  44758. (this.yAxis || []).forEach(function (yAxis) {
  44759. if (yAxis.stacking &&
  44760. yAxis.options.stackLabels &&
  44761. !yAxis.options.stackLabels.allowOverlap) {
  44762. objectEach(yAxis.stacking.stacks, function (stack) {
  44763. objectEach(stack, function (stackItem) {
  44764. if (stackItem.label) {
  44765. labels.push(stackItem.label);
  44766. }
  44767. });
  44768. });
  44769. }
  44770. });
  44771. (this.series || []).forEach(function (series) {
  44772. const dlOptions = series.options.dataLabels;
  44773. if (series.visible &&
  44774. !(dlOptions.enabled === false && !series._hasPointLabels)) { // #3866
  44775. const push = (points) => points.forEach((point) => {
  44776. if (point.visible) {
  44777. const dataLabels = (isArray(point.dataLabels) ?
  44778. point.dataLabels :
  44779. (point.dataLabel ? [point.dataLabel] : []));
  44780. dataLabels.forEach(function (label) {
  44781. const options = label.options;
  44782. label.labelrank = pick(options.labelrank, point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
  44783. if (!options.allowOverlap) {
  44784. labels.push(label);
  44785. }
  44786. else { // #13449
  44787. label.oldOpacity = label.opacity;
  44788. label.newOpacity = 1;
  44789. hideOrShow(label, chart);
  44790. }
  44791. });
  44792. }
  44793. });
  44794. push(series.nodes || []);
  44795. push(series.points);
  44796. }
  44797. });
  44798. this.hideOverlappingLabels(labels);
  44799. });
  44800. /**
  44801. * Hide overlapping labels. Labels are moved and faded in and out on zoom to
  44802. * provide a smooth visual imression.
  44803. *
  44804. * @private
  44805. * @function Highcharts.Chart#hideOverlappingLabels
  44806. * @param {Array<Highcharts.SVGElement>} labels
  44807. * Rendered data labels
  44808. * @requires modules/overlapping-datalabels
  44809. */
  44810. Chart.prototype.hideOverlappingLabels = function (labels) {
  44811. let chart = this, len = labels.length, ren = chart.renderer, label, i, j, label1, label2, box1, box2, isLabelAffected = false, isIntersectRect = function (box1, box2) {
  44812. return !(box2.x >= box1.x + box1.width ||
  44813. box2.x + box2.width <= box1.x ||
  44814. box2.y >= box1.y + box1.height ||
  44815. box2.y + box2.height <= box1.y);
  44816. },
  44817. // Get the box with its position inside the chart, as opposed to getBBox
  44818. // that only reports the position relative to the parent.
  44819. getAbsoluteBox = function (label) {
  44820. let pos, parent, bBox,
  44821. // Substract the padding if no background or border (#4333)
  44822. padding = label.box ? 0 : (label.padding || 0), lineHeightCorrection = 0, xOffset = 0, boxWidth, alignValue;
  44823. if (label &&
  44824. (!label.alignAttr || label.placed)) {
  44825. pos = label.alignAttr || {
  44826. x: label.attr('x'),
  44827. y: label.attr('y')
  44828. };
  44829. parent = label.parentGroup;
  44830. // Get width and height if pure text nodes (stack labels)
  44831. if (!label.width) {
  44832. bBox = label.getBBox();
  44833. label.width = bBox.width;
  44834. label.height = bBox.height;
  44835. // Labels positions are computed from top left corner, so we
  44836. // need to substract the text height from text nodes too.
  44837. lineHeightCorrection = ren.fontMetrics(label.element).h;
  44838. }
  44839. boxWidth = label.width - 2 * padding;
  44840. alignValue = {
  44841. left: '0',
  44842. center: '0.5',
  44843. right: '1'
  44844. }[label.alignValue];
  44845. if (alignValue) {
  44846. xOffset = +alignValue * boxWidth;
  44847. }
  44848. else if (isNumber(label.x) &&
  44849. Math.round(label.x) !== label.translateX) {
  44850. xOffset = label.x - label.translateX;
  44851. }
  44852. return {
  44853. x: pos.x + (parent.translateX || 0) + padding -
  44854. (xOffset || 0),
  44855. y: pos.y + (parent.translateY || 0) + padding -
  44856. lineHeightCorrection,
  44857. width: label.width - 2 * padding,
  44858. height: label.height - 2 * padding
  44859. };
  44860. }
  44861. };
  44862. for (i = 0; i < len; i++) {
  44863. label = labels[i];
  44864. if (label) {
  44865. // Mark with initial opacity
  44866. label.oldOpacity = label.opacity;
  44867. label.newOpacity = 1;
  44868. label.absoluteBox = getAbsoluteBox(label);
  44869. }
  44870. }
  44871. // Prevent a situation in a gradually rising slope, that each label will
  44872. // hide the previous one because the previous one always has lower rank.
  44873. labels.sort(function (a, b) {
  44874. return (b.labelrank || 0) - (a.labelrank || 0);
  44875. });
  44876. // Detect overlapping labels
  44877. for (i = 0; i < len; i++) {
  44878. label1 = labels[i];
  44879. box1 = label1 && label1.absoluteBox;
  44880. for (j = i + 1; j < len; ++j) {
  44881. label2 = labels[j];
  44882. box2 = label2 && label2.absoluteBox;
  44883. if (box1 &&
  44884. box2 &&
  44885. label1 !== label2 && // #6465, polar chart with connectEnds
  44886. label1.newOpacity !== 0 &&
  44887. label2.newOpacity !== 0 &&
  44888. // #15863 dataLabels are no longer hidden by translation
  44889. label1.visibility !== 'hidden' &&
  44890. label2.visibility !== 'hidden') {
  44891. if (isIntersectRect(box1, box2)) {
  44892. (label1.labelrank < label2.labelrank ? label1 : label2)
  44893. .newOpacity = 0;
  44894. }
  44895. }
  44896. }
  44897. }
  44898. // Hide or show
  44899. labels.forEach(function (label) {
  44900. if (hideOrShow(label, chart)) {
  44901. isLabelAffected = true;
  44902. }
  44903. });
  44904. if (isLabelAffected) {
  44905. fireEvent(chart, 'afterHideAllOverlappingLabels');
  44906. }
  44907. };
  44908. /**
  44909. * Hide or show labels based on opacity.
  44910. *
  44911. * @private
  44912. * @function hideOrShow
  44913. * @param {Highcharts.SVGElement} label
  44914. * The label.
  44915. * @param {Highcharts.Chart} chart
  44916. * The chart that contains the label.
  44917. * @return {boolean}
  44918. * Whether label is affected
  44919. */
  44920. function hideOrShow(label, chart) {
  44921. let complete, newOpacity, isLabelAffected = false;
  44922. if (label) {
  44923. newOpacity = label.newOpacity;
  44924. if (label.oldOpacity !== newOpacity) {
  44925. // Make sure the label is completely hidden to avoid catching clicks
  44926. // (#4362)
  44927. if (label.alignAttr && label.placed) { // data labels
  44928. label[newOpacity ? 'removeClass' : 'addClass']('highcharts-data-label-hidden');
  44929. complete = function () {
  44930. if (!chart.styledMode) {
  44931. label.css({
  44932. pointerEvents: newOpacity ? 'auto' : 'none'
  44933. });
  44934. }
  44935. };
  44936. isLabelAffected = true;
  44937. // Animate or set the opacity
  44938. label.alignAttr.opacity = newOpacity;
  44939. label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
  44940. fireEvent(chart, 'afterHideOverlappingLabel');
  44941. }
  44942. else { // other labels, tick labels
  44943. label.attr({
  44944. opacity: newOpacity
  44945. });
  44946. }
  44947. }
  44948. label.isOld = true;
  44949. }
  44950. return isLabelAffected;
  44951. }
  44952. });
  44953. _registerModule(_modules, 'Extensions/BorderRadius.js', [_modules['Core/Defaults.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (D, Series, SeriesRegistry, SVGElement, SVGRenderer, U) {
  44954. /* *
  44955. *
  44956. * Highcharts Border Radius module
  44957. *
  44958. * Author: Torstein Honsi
  44959. *
  44960. * License: www.highcharts.com/license
  44961. *
  44962. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44963. *
  44964. * */
  44965. const { defaultOptions } = D;
  44966. const { seriesTypes } = SeriesRegistry;
  44967. const { addEvent, extend, isObject, merge, relativeLength } = U;
  44968. /* *
  44969. *
  44970. * Constants
  44971. *
  44972. * */
  44973. const defaultBorderRadiusOptions = {
  44974. radius: 0,
  44975. scope: 'stack',
  44976. where: void 0
  44977. };
  44978. const optionsToObject = (options, seriesBROptions) => {
  44979. if (!isObject(options)) {
  44980. options = { radius: options || 0 };
  44981. }
  44982. return merge(defaultBorderRadiusOptions, seriesBROptions, options);
  44983. };
  44984. const applyBorderRadius = (path, i, r) => {
  44985. const a = path[i];
  44986. let b = path[i + 1];
  44987. if (b[0] === 'Z') {
  44988. b = path[0];
  44989. }
  44990. let line, arc, fromLineToArc;
  44991. // From straight line to arc
  44992. if ((a[0] === 'M' || a[0] === 'L') && b[0] === 'A') {
  44993. line = a;
  44994. arc = b;
  44995. fromLineToArc = true;
  44996. // From arc to straight line
  44997. }
  44998. else if (a[0] === 'A' && (b[0] === 'M' || b[0] === 'L')) {
  44999. line = b;
  45000. arc = a;
  45001. }
  45002. if (line && arc && arc.params) {
  45003. const bigR = arc[1],
  45004. // In our use cases, outer pie slice arcs are clockwise and inner
  45005. // arcs (donut/sunburst etc) are anti-clockwise
  45006. clockwise = arc[5], params = arc.params, { start, end, cx, cy } = params;
  45007. // Some geometric constants
  45008. const relativeR = clockwise ? (bigR - r) : (bigR + r),
  45009. // The angle, on the big arc, that the border radius arc takes up
  45010. angleOfBorderRadius = relativeR ? Math.asin(r / relativeR) : 0, angleOffset = clockwise ?
  45011. angleOfBorderRadius :
  45012. -angleOfBorderRadius,
  45013. // The distance along the radius of the big arc to the starting
  45014. // point of the small border radius arc
  45015. distanceBigCenterToStartArc = (Math.cos(angleOfBorderRadius) *
  45016. relativeR);
  45017. // From line to arc
  45018. if (fromLineToArc) {
  45019. // Update the cache
  45020. params.start = start + angleOffset;
  45021. // First move to the start position at the radial line. We want to
  45022. // start one borderRadius closer to the center.
  45023. line[1] = cx + distanceBigCenterToStartArc * Math.cos(start);
  45024. line[2] = cy + distanceBigCenterToStartArc * Math.sin(start);
  45025. // Now draw an arc towards the point where the small circle touches
  45026. // the great circle.
  45027. path.splice(i + 1, 0, [
  45028. 'A',
  45029. r,
  45030. r,
  45031. 0,
  45032. 0,
  45033. 1,
  45034. cx + bigR * Math.cos(params.start),
  45035. cy + bigR * Math.sin(params.start)
  45036. ]);
  45037. // From arc to line
  45038. }
  45039. else {
  45040. // Update the cache
  45041. params.end = end - angleOffset;
  45042. // End the big arc a bit earlier
  45043. arc[6] = cx + bigR * Math.cos(params.end);
  45044. arc[7] = cy + bigR * Math.sin(params.end);
  45045. // Draw a small arc towards a point on the end angle, but one
  45046. // borderRadius closer to the center relative to the perimeter.
  45047. path.splice(i + 1, 0, [
  45048. 'A',
  45049. r,
  45050. r,
  45051. 0,
  45052. 0,
  45053. 1,
  45054. cx + distanceBigCenterToStartArc * Math.cos(end),
  45055. cy + distanceBigCenterToStartArc * Math.sin(end)
  45056. ]);
  45057. }
  45058. // Long or short arc must be reconsidered because we have modified the
  45059. // start and end points
  45060. arc[4] = Math.abs(params.end - params.start) < Math.PI ? 0 : 1;
  45061. }
  45062. };
  45063. /* *
  45064. *
  45065. * Modifications
  45066. *
  45067. * */
  45068. // Check if the module has already been imported
  45069. // @todo implement as composition
  45070. if (SVGElement.symbolCustomAttribs.indexOf('borderRadius') === -1) {
  45071. SVGElement.symbolCustomAttribs.push('borderRadius', 'brBoxHeight', 'brBoxY');
  45072. // Extend arc with borderRadius
  45073. const arc = SVGRenderer.prototype.symbols.arc;
  45074. SVGRenderer.prototype.symbols.arc = function (x, y, w, h, options = {}) {
  45075. const path = arc(x, y, w, h, options), { innerR = 0, r = w, start = 0, end = 0 } = options;
  45076. if (options.open || !options.borderRadius) {
  45077. return path;
  45078. }
  45079. const alpha = end - start, sinHalfAlpha = Math.sin(alpha / 2), borderRadius = Math.max(Math.min(relativeLength(options.borderRadius || 0, r - innerR),
  45080. // Cap to half the sector radius
  45081. (r - innerR) / 2,
  45082. // For smaller pie slices, cap to the largest small circle that
  45083. // can be fitted within the sector
  45084. (r * sinHalfAlpha) / (1 + sinHalfAlpha)), 0),
  45085. // For the inner radius, we need an extra cap because the inner arc
  45086. // is shorter than the outer arc
  45087. innerBorderRadius = Math.min(borderRadius, 2 * (alpha / Math.PI) * innerR);
  45088. // Apply turn-by-turn border radius. Start at the end since we're
  45089. // splicing in arc segments.
  45090. let i = path.length - 1;
  45091. while (i--) {
  45092. applyBorderRadius(path, i, i > 1 ? innerBorderRadius : borderRadius);
  45093. }
  45094. return path;
  45095. };
  45096. // Extend roundedRect with individual cutting through rOffset
  45097. const roundedRect = SVGRenderer.prototype.symbols.roundedRect;
  45098. SVGRenderer.prototype.symbols.roundedRect = function (x, y, width, height, options = {}) {
  45099. const path = roundedRect(x, y, width, height, options), { r = 0, brBoxHeight = height, brBoxY = y } = options, brOffsetTop = y - brBoxY, brOffsetBtm = (brBoxY + brBoxHeight) - (y + height),
  45100. // When the distance to the border-radius box is greater than the r
  45101. // itself, it means no border radius. The -0.1 accounts for float
  45102. // rounding errors.
  45103. rTop = (brOffsetTop - r) > -0.1 ? 0 : r, rBtm = (brOffsetBtm - r) > -0.1 ? 0 : r, cutTop = Math.max(rTop && brOffsetTop, 0), cutBtm = Math.max(rBtm && brOffsetBtm, 0);
  45104. /*
  45105. The naming of control points:
  45106. / a -------- b \
  45107. / \
  45108. h c
  45109. | |
  45110. | |
  45111. | |
  45112. g d
  45113. \ /
  45114. \ f -------- e /
  45115. */
  45116. const a = [x + rTop, y], b = [x + width - rTop, y], c = [x + width, y + rTop], d = [
  45117. x + width, y + height - rBtm
  45118. ], e = [
  45119. x + width - rBtm,
  45120. y + height
  45121. ], f = [x + rBtm, y + height], g = [x, y + height - rBtm], h = [x, y + rTop];
  45122. const applyPythagoras = (r, altitude) => Math.sqrt(Math.pow(r, 2) - Math.pow(altitude, 2));
  45123. // Inside stacks, cut off part of the top
  45124. if (cutTop) {
  45125. const base = applyPythagoras(rTop, rTop - cutTop);
  45126. a[0] -= base;
  45127. b[0] += base;
  45128. c[1] = h[1] = y + rTop - cutTop;
  45129. }
  45130. // Column is lower than the radius. Cut off bottom inside the top
  45131. // radius.
  45132. if (height < rTop - cutTop) {
  45133. const base = applyPythagoras(rTop, rTop - cutTop - height);
  45134. c[0] = d[0] = x + width - rTop + base;
  45135. e[0] = Math.min(c[0], e[0]);
  45136. f[0] = Math.max(d[0], f[0]);
  45137. g[0] = h[0] = x + rTop - base;
  45138. c[1] = h[1] = y + height;
  45139. }
  45140. // Inside stacks, cut off part of the bottom
  45141. if (cutBtm) {
  45142. const base = applyPythagoras(rBtm, rBtm - cutBtm);
  45143. e[0] += base;
  45144. f[0] -= base;
  45145. d[1] = g[1] = y + height - rBtm + cutBtm;
  45146. }
  45147. // Cut off top inside the bottom radius
  45148. if (height < rBtm - cutBtm) {
  45149. const base = applyPythagoras(rBtm, rBtm - cutBtm - height);
  45150. c[0] = d[0] = x + width - rBtm + base;
  45151. b[0] = Math.min(c[0], b[0]);
  45152. a[0] = Math.max(d[0], a[0]);
  45153. g[0] = h[0] = x + rBtm - base;
  45154. d[1] = g[1] = y;
  45155. }
  45156. // Preserve the box for data labels
  45157. path.length = 0;
  45158. path.push(['M', ...a],
  45159. // top side
  45160. ['L', ...b],
  45161. // top right corner
  45162. ['A', rTop, rTop, 0, 0, 1, ...c],
  45163. // right side
  45164. ['L', ...d],
  45165. // bottom right corner
  45166. ['A', rBtm, rBtm, 0, 0, 1, ...e],
  45167. // bottom side
  45168. ['L', ...f],
  45169. // bottom left corner
  45170. ['A', rBtm, rBtm, 0, 0, 1, ...g],
  45171. // left side
  45172. ['L', ...h],
  45173. // top left corner
  45174. ['A', rTop, rTop, 0, 0, 1, ...a], ['Z']);
  45175. return path;
  45176. };
  45177. addEvent(seriesTypes.pie, 'afterTranslate', function () {
  45178. const borderRadius = optionsToObject(this.options.borderRadius);
  45179. for (const point of this.points) {
  45180. const shapeArgs = point.shapeArgs;
  45181. if (shapeArgs) {
  45182. shapeArgs.borderRadius = relativeLength(borderRadius.radius, (shapeArgs.r || 0) - ((shapeArgs.innerR) || 0));
  45183. }
  45184. }
  45185. });
  45186. addEvent(Series, 'afterColumnTranslate', function () {
  45187. var _a,
  45188. _b;
  45189. if (this.options.borderRadius &&
  45190. !(this.chart.is3d && this.chart.is3d())) {
  45191. const { options, yAxis } = this, percent = options.stacking === 'percent', seriesDefault = (_b = (_a = defaultOptions.plotOptions) === null || _a === void 0 ? void 0 : _a[this.type]) === null || _b === void 0 ? void 0 : _b.borderRadius, borderRadius = optionsToObject(options.borderRadius, isObject(seriesDefault) ? seriesDefault : {}), reversed = yAxis.options.reversed;
  45192. for (const point of this.points) {
  45193. const { shapeArgs } = point;
  45194. if (point.shapeType === 'roundedRect' && shapeArgs) {
  45195. const { width = 0, height = 0, y = 0 } = shapeArgs;
  45196. let brBoxY = y, brBoxHeight = height;
  45197. // It would be nice to refactor StackItem.getStackBox/
  45198. // setOffset so that we could get a reliable box out of
  45199. // it. Currently it is close if we remove the label
  45200. // offset, but we still need to run crispCol and also
  45201. // flip it if inverted, so atm it is simpler to do it
  45202. // like the below.
  45203. if (borderRadius.scope === 'stack' &&
  45204. point.stackTotal) {
  45205. const stackEnd = yAxis.translate(percent ? 100 : point.stackTotal, false, true, false, true), stackThreshold = yAxis.translate(options.threshold || 0, false, true, false, true), box = this.crispCol(0, Math.min(stackEnd, stackThreshold), 0, Math.abs(stackEnd - stackThreshold));
  45206. brBoxY = box.y;
  45207. brBoxHeight = box.height;
  45208. }
  45209. const flip = (point.negative ? -1 : 1) *
  45210. (reversed ? -1 : 1) === -1;
  45211. // Handle the where option
  45212. let where = borderRadius.where;
  45213. // Waterfall, hanging columns should have rounding on
  45214. // all sides
  45215. if (!where &&
  45216. this.is('waterfall') &&
  45217. Math.abs((point.yBottom || 0) -
  45218. (this.translatedThreshold || 0)) > this.borderWidth) {
  45219. where = 'all';
  45220. }
  45221. if (!where) {
  45222. where = 'end';
  45223. }
  45224. // Get the radius
  45225. const r = Math.min(relativeLength(borderRadius.radius, width), width / 2,
  45226. // Cap to the height, but not if where is `end`
  45227. where === 'all' ? height / 2 : Infinity) || 0;
  45228. // If the `where` option is 'end', cut off the
  45229. // rectangles by making the border-radius box one r
  45230. // greater, so that the imaginary radius falls outside
  45231. // the rectangle.
  45232. if (where === 'end') {
  45233. if (flip) {
  45234. brBoxY -= r;
  45235. brBoxHeight += r;
  45236. }
  45237. else {
  45238. brBoxHeight += r;
  45239. }
  45240. }
  45241. extend(shapeArgs, { brBoxHeight, brBoxY, r });
  45242. }
  45243. }
  45244. }
  45245. }, {
  45246. // After columnrange and polar column modifications
  45247. order: 9
  45248. });
  45249. }
  45250. /* *
  45251. *
  45252. * Default Export
  45253. *
  45254. * */
  45255. const BorderRadius = {
  45256. optionsToObject
  45257. };
  45258. /* *
  45259. *
  45260. * API Declarations
  45261. *
  45262. * */
  45263. /**
  45264. * Detailed options for border radius.
  45265. *
  45266. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  45267. * Rounded columns
  45268. * @sample highcharts/plotoptions/series-border-radius
  45269. * Column and pie with rounded border
  45270. *
  45271. * @interface Highcharts.BorderRadiusOptionsObject
  45272. */ /**
  45273. * The border radius. A number signifies pixels. A percentage string, like for
  45274. * example `50%`, signifies a relative size. For columns this is relative to the
  45275. * column width, for pies it is relative to the radius and the inner radius.
  45276. *
  45277. * @name Highcharts.BorderRadiusOptionsObject#radius
  45278. * @type {string|number}
  45279. */ /**
  45280. * The scope of the rounding for column charts. In a stacked column chart, the
  45281. * value `point` means each single point will get rounded corners. The value
  45282. * `stack` means the rounding will apply to the full stack, so that only points
  45283. * close to the top or bottom will receive rounding.
  45284. *
  45285. * @name Highcharts.BorderRadiusOptionsObject#scope
  45286. * @validvalue ["point", "stack"]
  45287. * @type {string}
  45288. */ /**
  45289. * For column charts, where in the point or stack to apply rounding. The `end`
  45290. * value means only those corners at the point value will be rounded, leaving
  45291. * the corners at the base or threshold unrounded. This is the most intuitive
  45292. * behaviour. The `all` value means also the base will be rounded.
  45293. *
  45294. * @name Highcharts.BorderRadiusOptionsObject#where
  45295. * @validvalue ["all", "end"]
  45296. * @type {string}
  45297. * @default end
  45298. */
  45299. (''); // keeps doclets above in JS file
  45300. return BorderRadius;
  45301. });
  45302. _registerModule(_modules, 'Core/Responsive.js', [_modules['Core/Utilities.js']], function (U) {
  45303. /* *
  45304. *
  45305. * (c) 2010-2021 Torstein Honsi
  45306. *
  45307. * License: www.highcharts.com/license
  45308. *
  45309. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45310. *
  45311. * */
  45312. const { diffObjects, extend, find, isArray, isObject, merge, objectEach, pick, splat, uniqueKey } = U;
  45313. /* *
  45314. *
  45315. * Composition
  45316. *
  45317. * */
  45318. var Responsive;
  45319. (function (Responsive) {
  45320. /* *
  45321. *
  45322. * Declarations
  45323. *
  45324. * */
  45325. /* *
  45326. *
  45327. * Constants
  45328. *
  45329. * */
  45330. const composedMembers = [];
  45331. /* *
  45332. *
  45333. * Functions
  45334. *
  45335. * */
  45336. /**
  45337. * @private
  45338. */
  45339. function compose(ChartClass) {
  45340. if (U.pushUnique(composedMembers, ChartClass)) {
  45341. extend(ChartClass.prototype, {
  45342. matchResponsiveRule,
  45343. setResponsive
  45344. });
  45345. }
  45346. return ChartClass;
  45347. }
  45348. Responsive.compose = compose;
  45349. /**
  45350. * Handle a single responsiveness rule.
  45351. *
  45352. * @private
  45353. * @function Highcharts.Chart#matchResponsiveRule
  45354. * @param {Highcharts.ResponsiveRulesOptions} rule
  45355. * @param {Array<string>} matches
  45356. */
  45357. function matchResponsiveRule(rule, matches) {
  45358. const condition = rule.condition, fn = condition.callback || function () {
  45359. return (this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
  45360. this.chartHeight <= pick(condition.maxHeight, Number.MAX_VALUE) &&
  45361. this.chartWidth >= pick(condition.minWidth, 0) &&
  45362. this.chartHeight >= pick(condition.minHeight, 0));
  45363. };
  45364. if (fn.call(this)) {
  45365. matches.push(rule._id);
  45366. }
  45367. }
  45368. /**
  45369. * Update the chart based on the current chart/document size and options
  45370. * for responsiveness.
  45371. *
  45372. * @private
  45373. * @function Highcharts.Chart#setResponsive
  45374. * @param {boolean} [redraw=true]
  45375. * @param {boolean} [reset=false]
  45376. * Reset by un-applying all rules. Chart.update resets all rules before
  45377. * applying updated options.
  45378. */
  45379. function setResponsive(redraw, reset) {
  45380. const options = this.options.responsive, currentResponsive = this.currentResponsive;
  45381. let ruleIds = [], undoOptions;
  45382. if (!reset && options && options.rules) {
  45383. options.rules.forEach((rule) => {
  45384. if (typeof rule._id === 'undefined') {
  45385. rule._id = uniqueKey();
  45386. }
  45387. this.matchResponsiveRule(rule, ruleIds /* , redraw */);
  45388. }, this);
  45389. }
  45390. // Merge matching rules
  45391. const mergedOptions = merge(...ruleIds
  45392. .map((ruleId) => find((options || {}).rules || [], (rule) => (rule._id === ruleId)))
  45393. .map((rule) => (rule && rule.chartOptions)));
  45394. mergedOptions.isResponsiveOptions = true;
  45395. // Stringified key for the rules that currently apply.
  45396. ruleIds = (ruleIds.toString() || void 0);
  45397. const currentRuleIds = (currentResponsive && currentResponsive.ruleIds);
  45398. // Changes in what rules apply
  45399. if (ruleIds !== currentRuleIds) {
  45400. // Undo previous rules. Before we apply a new set of rules, we
  45401. // need to roll back completely to base options (#6291).
  45402. if (currentResponsive) {
  45403. this.update(currentResponsive.undoOptions, redraw, true);
  45404. }
  45405. if (ruleIds) {
  45406. // Get undo-options for matching rules. The `undoOptions``
  45407. // hold the current values before they are changed by the
  45408. // `mergedOptions`.
  45409. undoOptions = diffObjects(mergedOptions, this.options, true, this.collectionsWithUpdate);
  45410. undoOptions.isResponsiveOptions = true;
  45411. this.currentResponsive = {
  45412. ruleIds: ruleIds,
  45413. mergedOptions: mergedOptions,
  45414. undoOptions: undoOptions
  45415. };
  45416. this.update(mergedOptions, redraw, true);
  45417. }
  45418. else {
  45419. this.currentResponsive = void 0;
  45420. }
  45421. }
  45422. }
  45423. })(Responsive || (Responsive = {}));
  45424. /* *
  45425. *
  45426. * Default Export
  45427. *
  45428. * */
  45429. /* *
  45430. *
  45431. * API Declarations
  45432. *
  45433. * */
  45434. /**
  45435. * A callback function to gain complete control on when the responsive rule
  45436. * applies.
  45437. *
  45438. * @callback Highcharts.ResponsiveCallbackFunction
  45439. *
  45440. * @param {Highcharts.Chart} this
  45441. * Chart context.
  45442. *
  45443. * @return {boolean}
  45444. * Return `true` if it applies.
  45445. */
  45446. (''); // keeps doclets above in JS file
  45447. /* *
  45448. *
  45449. * API Options
  45450. *
  45451. * */
  45452. /**
  45453. * Allows setting a set of rules to apply for different screen or chart
  45454. * sizes. Each rule specifies additional chart options.
  45455. *
  45456. * @sample {highstock} stock/demo/responsive/
  45457. * Stock chart
  45458. * @sample highcharts/responsive/axis/
  45459. * Axis
  45460. * @sample highcharts/responsive/legend/
  45461. * Legend
  45462. * @sample highcharts/responsive/classname/
  45463. * Class name
  45464. *
  45465. * @since 5.0.0
  45466. * @apioption responsive
  45467. */
  45468. /**
  45469. * A set of rules for responsive settings. The rules are executed from
  45470. * the top down.
  45471. *
  45472. * @sample {highcharts} highcharts/responsive/axis/
  45473. * Axis changes
  45474. * @sample {highstock} highcharts/responsive/axis/
  45475. * Axis changes
  45476. * @sample {highmaps} highcharts/responsive/axis/
  45477. * Axis changes
  45478. *
  45479. * @type {Array<*>}
  45480. * @since 5.0.0
  45481. * @apioption responsive.rules
  45482. */
  45483. /**
  45484. * A full set of chart options to apply as overrides to the general
  45485. * chart options. The chart options are applied when the given rule
  45486. * is active.
  45487. *
  45488. * A special case is configuration objects that take arrays, for example
  45489. * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
  45490. * collections, an `id` option is used to map the new option set to
  45491. * an existing object. If an existing object of the same id is not found,
  45492. * the item of the same indexupdated. So for example, setting `chartOptions`
  45493. * with two series items without an `id`, will cause the existing chart's
  45494. * two series to be updated with respective options.
  45495. *
  45496. * @sample {highstock} stock/demo/responsive/
  45497. * Stock chart
  45498. * @sample highcharts/responsive/axis/
  45499. * Axis
  45500. * @sample highcharts/responsive/legend/
  45501. * Legend
  45502. * @sample highcharts/responsive/classname/
  45503. * Class name
  45504. *
  45505. * @type {Highcharts.Options}
  45506. * @since 5.0.0
  45507. * @apioption responsive.rules.chartOptions
  45508. */
  45509. /**
  45510. * Under which conditions the rule applies.
  45511. *
  45512. * @since 5.0.0
  45513. * @apioption responsive.rules.condition
  45514. */
  45515. /**
  45516. * A callback function to gain complete control on when the responsive
  45517. * rule applies. Return `true` if it applies. This opens for checking
  45518. * against other metrics than the chart size, for example the document
  45519. * size or other elements.
  45520. *
  45521. * @type {Highcharts.ResponsiveCallbackFunction}
  45522. * @since 5.0.0
  45523. * @context Highcharts.Chart
  45524. * @apioption responsive.rules.condition.callback
  45525. */
  45526. /**
  45527. * The responsive rule applies if the chart height is less than this.
  45528. *
  45529. * @type {number}
  45530. * @since 5.0.0
  45531. * @apioption responsive.rules.condition.maxHeight
  45532. */
  45533. /**
  45534. * The responsive rule applies if the chart width is less than this.
  45535. *
  45536. * @sample highcharts/responsive/axis/
  45537. * Max width is 500
  45538. *
  45539. * @type {number}
  45540. * @since 5.0.0
  45541. * @apioption responsive.rules.condition.maxWidth
  45542. */
  45543. /**
  45544. * The responsive rule applies if the chart height is greater than this.
  45545. *
  45546. * @type {number}
  45547. * @default 0
  45548. * @since 5.0.0
  45549. * @apioption responsive.rules.condition.minHeight
  45550. */
  45551. /**
  45552. * The responsive rule applies if the chart width is greater than this.
  45553. *
  45554. * @type {number}
  45555. * @default 0
  45556. * @since 5.0.0
  45557. * @apioption responsive.rules.condition.minWidth
  45558. */
  45559. (''); // keeps doclets above in JS file
  45560. return Responsive;
  45561. });
  45562. _registerModule(_modules, 'masters/highcharts.src.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Core/Defaults.js'], _modules['Core/Animation/Fx.js'], _modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Renderer/HTML/AST.js'], _modules['Core/Templating.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Renderer/HTML/HTMLElement.js'], _modules['Core/Renderer/HTML/HTMLRenderer.js'], _modules['Core/Axis/Axis.js'], _modules['Core/Axis/DateTimeAxis.js'], _modules['Core/Axis/LogarithmicAxis.js'], _modules['Core/Axis/PlotLineOrBand/PlotLineOrBand.js'], _modules['Core/Axis/Tick.js'], _modules['Core/Tooltip.js'], _modules['Core/Series/Point.js'], _modules['Core/Pointer.js'], _modules['Core/Legend/Legend.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Axis/Stacking/StackingAxis.js'], _modules['Core/Axis/Stacking/StackItem.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Series/Column/ColumnSeries.js'], _modules['Series/Column/ColumnDataLabel.js'], _modules['Series/Pie/PieSeries.js'], _modules['Series/Pie/PieDataLabel.js'], _modules['Core/Series/DataLabel.js'], _modules['Core/Responsive.js'], _modules['Core/Color/Color.js'], _modules['Core/Time.js']], function (Highcharts, Utilities, Defaults, Fx, Animation, AST, Templating, RendererUtilities, SVGElement, SVGRenderer, HTMLElement, HTMLRenderer, Axis, DateTimeAxis, LogarithmicAxis, PlotLineOrBand, Tick, Tooltip, Point, Pointer, Legend, Chart, StackingAxis, StackItem, Series, SeriesRegistry, ColumnSeries, ColumnDataLabel, PieSeries, PieDataLabel, DataLabel, Responsive, Color, Time) {
  45563. const G = Highcharts;
  45564. // Animation
  45565. G.animate = Animation.animate;
  45566. G.animObject = Animation.animObject;
  45567. G.getDeferredAnimation = Animation.getDeferredAnimation;
  45568. G.setAnimation = Animation.setAnimation;
  45569. G.stop = Animation.stop;
  45570. G.timers = Fx.timers;
  45571. // Classes
  45572. G.AST = AST;
  45573. G.Axis = Axis;
  45574. G.Chart = Chart;
  45575. G.chart = Chart.chart;
  45576. G.Fx = Fx;
  45577. G.Legend = Legend;
  45578. G.PlotLineOrBand = PlotLineOrBand;
  45579. G.Point = Point;
  45580. G.Pointer = Pointer;
  45581. G.Series = Series;
  45582. G.StackItem = StackItem;
  45583. G.SVGElement = SVGElement;
  45584. G.SVGRenderer = SVGRenderer;
  45585. G.Templating = Templating;
  45586. G.Tick = Tick;
  45587. G.Time = Time;
  45588. G.Tooltip = Tooltip;
  45589. // Color
  45590. G.Color = Color;
  45591. G.color = Color.parse;
  45592. // Compositions
  45593. HTMLRenderer.compose(SVGRenderer);
  45594. HTMLElement.compose(SVGElement);
  45595. Pointer.compose(Chart);
  45596. Legend.compose(Chart);
  45597. // DefaultOptions
  45598. G.defaultOptions = Defaults.defaultOptions;
  45599. G.getOptions = Defaults.getOptions;
  45600. G.time = Defaults.defaultTime;
  45601. G.setOptions = Defaults.setOptions;
  45602. // Format Utilities
  45603. G.dateFormat = Templating.dateFormat;
  45604. G.format = Templating.format;
  45605. G.numberFormat = Templating.numberFormat;
  45606. // Utilities
  45607. G.addEvent = Utilities.addEvent;
  45608. G.arrayMax = Utilities.arrayMax;
  45609. G.arrayMin = Utilities.arrayMin;
  45610. G.attr = Utilities.attr;
  45611. G.clearTimeout = Utilities.clearTimeout;
  45612. G.correctFloat = Utilities.correctFloat;
  45613. G.createElement = Utilities.createElement;
  45614. G.css = Utilities.css;
  45615. G.defined = Utilities.defined;
  45616. G.destroyObjectProperties = Utilities.destroyObjectProperties;
  45617. G.discardElement = Utilities.discardElement;
  45618. G.distribute = RendererUtilities.distribute;
  45619. G.erase = Utilities.erase;
  45620. G.error = Utilities.error;
  45621. G.extend = Utilities.extend;
  45622. G.extendClass = Utilities.extendClass;
  45623. G.find = Utilities.find;
  45624. G.fireEvent = Utilities.fireEvent;
  45625. G.getMagnitude = Utilities.getMagnitude;
  45626. G.getStyle = Utilities.getStyle;
  45627. G.inArray = Utilities.inArray;
  45628. G.isArray = Utilities.isArray;
  45629. G.isClass = Utilities.isClass;
  45630. G.isDOMElement = Utilities.isDOMElement;
  45631. G.isFunction = Utilities.isFunction;
  45632. G.isNumber = Utilities.isNumber;
  45633. G.isObject = Utilities.isObject;
  45634. G.isString = Utilities.isString;
  45635. G.keys = Utilities.keys;
  45636. G.merge = Utilities.merge;
  45637. G.normalizeTickInterval = Utilities.normalizeTickInterval;
  45638. G.objectEach = Utilities.objectEach;
  45639. G.offset = Utilities.offset;
  45640. G.pad = Utilities.pad;
  45641. G.pick = Utilities.pick;
  45642. G.pInt = Utilities.pInt;
  45643. G.relativeLength = Utilities.relativeLength;
  45644. G.removeEvent = Utilities.removeEvent;
  45645. G.seriesType = SeriesRegistry.seriesType;
  45646. G.splat = Utilities.splat;
  45647. G.stableSort = Utilities.stableSort;
  45648. G.syncTimeout = Utilities.syncTimeout;
  45649. G.timeUnits = Utilities.timeUnits;
  45650. G.uniqueKey = Utilities.uniqueKey;
  45651. G.useSerialIds = Utilities.useSerialIds;
  45652. G.wrap = Utilities.wrap;
  45653. // Compositions
  45654. ColumnDataLabel.compose(ColumnSeries);
  45655. DataLabel.compose(Series);
  45656. DateTimeAxis.compose(Axis);
  45657. LogarithmicAxis.compose(Axis);
  45658. PieDataLabel.compose(PieSeries);
  45659. PlotLineOrBand.compose(Axis);
  45660. Responsive.compose(Chart);
  45661. StackingAxis.compose(Axis, Chart, Series);
  45662. Tooltip.compose(Pointer);
  45663. // Default Export
  45664. return G;
  45665. });
  45666. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Stacking/StackItem.js'], _modules['Core/Utilities.js']], function (StackItem, U) {
  45667. /* *
  45668. *
  45669. * (c) 2009-2021 Torstein Honsi
  45670. *
  45671. * License: www.highcharts.com/license
  45672. *
  45673. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45674. *
  45675. * */
  45676. const { addEvent, find, fireEvent, isArray, isNumber, pick } = U;
  45677. /* *
  45678. *
  45679. * Composition
  45680. *
  45681. * */
  45682. /**
  45683. * Axis with support of broken data rows.
  45684. * @private
  45685. */
  45686. var BrokenAxis;
  45687. (function (BrokenAxis) {
  45688. /* *
  45689. *
  45690. * Declarations
  45691. *
  45692. * */
  45693. /* *
  45694. *
  45695. * Constants
  45696. *
  45697. * */
  45698. const composedMembers = [];
  45699. /* *
  45700. *
  45701. * Functions
  45702. *
  45703. * */
  45704. /* eslint-disable valid-jsdoc */
  45705. /**
  45706. * Adds support for broken axes.
  45707. * @private
  45708. */
  45709. function compose(AxisClass, SeriesClass) {
  45710. if (U.pushUnique(composedMembers, AxisClass)) {
  45711. AxisClass.keepProps.push('brokenAxis');
  45712. addEvent(AxisClass, 'init', onAxisInit);
  45713. addEvent(AxisClass, 'afterInit', onAxisAfterInit);
  45714. addEvent(AxisClass, 'afterSetTickPositions', onAxisAfterSetTickPositions);
  45715. addEvent(AxisClass, 'afterSetOptions', onAxisAfterSetOptions);
  45716. }
  45717. if (U.pushUnique(composedMembers, SeriesClass)) {
  45718. const seriesProto = SeriesClass.prototype;
  45719. seriesProto.drawBreaks = seriesDrawBreaks;
  45720. seriesProto.gappedPath = seriesGappedPath;
  45721. addEvent(SeriesClass, 'afterGeneratePoints', onSeriesAfterGeneratePoints);
  45722. addEvent(SeriesClass, 'afterRender', onSeriesAfterRender);
  45723. }
  45724. return AxisClass;
  45725. }
  45726. BrokenAxis.compose = compose;
  45727. /**
  45728. * @private
  45729. */
  45730. function onAxisAfterInit() {
  45731. if (typeof this.brokenAxis !== 'undefined') {
  45732. this.brokenAxis.setBreaks(this.options.breaks, false);
  45733. }
  45734. }
  45735. /**
  45736. * Force Axis to be not-ordinal when breaks are defined.
  45737. * @private
  45738. */
  45739. function onAxisAfterSetOptions() {
  45740. const axis = this;
  45741. if (axis.brokenAxis && axis.brokenAxis.hasBreaks) {
  45742. axis.options.ordinal = false;
  45743. }
  45744. }
  45745. /**
  45746. * @private
  45747. */
  45748. function onAxisAfterSetTickPositions() {
  45749. const axis = this, brokenAxis = axis.brokenAxis;
  45750. if (brokenAxis &&
  45751. brokenAxis.hasBreaks) {
  45752. const tickPositions = axis.tickPositions, info = axis.tickPositions.info, newPositions = [];
  45753. for (let i = 0; i < tickPositions.length; i++) {
  45754. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  45755. newPositions.push(tickPositions[i]);
  45756. }
  45757. }
  45758. axis.tickPositions = newPositions;
  45759. axis.tickPositions.info = info;
  45760. }
  45761. }
  45762. /**
  45763. * @private
  45764. */
  45765. function onAxisInit() {
  45766. const axis = this;
  45767. if (!axis.brokenAxis) {
  45768. axis.brokenAxis = new Additions(axis);
  45769. }
  45770. }
  45771. /**
  45772. * @private
  45773. */
  45774. function onSeriesAfterGeneratePoints() {
  45775. const { isDirty, options: { connectNulls }, points, xAxis, yAxis } = this;
  45776. // Set, or reset visibility of the points. Axis.setBreaks marks
  45777. // the series as isDirty
  45778. if (isDirty) {
  45779. let i = points.length;
  45780. while (i--) {
  45781. const point = points[i];
  45782. // Respect nulls inside the break (#4275)
  45783. const nullGap = point.y === null && connectNulls === false;
  45784. const isPointInBreak = (!nullGap && ((xAxis &&
  45785. xAxis.brokenAxis &&
  45786. xAxis.brokenAxis.isInAnyBreak(point.x, true)) || (yAxis &&
  45787. yAxis.brokenAxis &&
  45788. yAxis.brokenAxis.isInAnyBreak(point.y, true))));
  45789. // Set point.visible if in any break.
  45790. // If not in break, reset visible to original value.
  45791. point.visible = isPointInBreak ?
  45792. false :
  45793. point.options.visible !== false;
  45794. }
  45795. }
  45796. }
  45797. /**
  45798. * @private
  45799. */
  45800. function onSeriesAfterRender() {
  45801. this.drawBreaks(this.xAxis, ['x']);
  45802. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  45803. }
  45804. /**
  45805. * @private
  45806. */
  45807. function seriesDrawBreaks(axis, keys) {
  45808. const series = this, points = series.points;
  45809. let breaks, threshold, eventName, y;
  45810. if (axis && // #5950
  45811. axis.brokenAxis &&
  45812. axis.brokenAxis.hasBreaks) {
  45813. const brokenAxis = axis.brokenAxis;
  45814. keys.forEach(function (key) {
  45815. breaks = brokenAxis && brokenAxis.breakArray || [];
  45816. threshold = axis.isXAxis ?
  45817. axis.min :
  45818. pick(series.options.threshold, axis.min);
  45819. points.forEach(function (point) {
  45820. y = pick(point['stack' + key.toUpperCase()], point[key]);
  45821. breaks.forEach(function (brk) {
  45822. if (isNumber(threshold) && isNumber(y)) {
  45823. eventName = false;
  45824. if ((threshold < brk.from && y > brk.to) ||
  45825. (threshold > brk.from && y < brk.from)) {
  45826. eventName = 'pointBreak';
  45827. }
  45828. else if ((threshold < brk.from &&
  45829. y > brk.from &&
  45830. y < brk.to) || (threshold > brk.from &&
  45831. y > brk.to &&
  45832. y < brk.from)) {
  45833. eventName = 'pointInBreak';
  45834. }
  45835. if (eventName) {
  45836. fireEvent(axis, eventName, { point, brk });
  45837. }
  45838. }
  45839. });
  45840. });
  45841. });
  45842. }
  45843. }
  45844. /**
  45845. * Extend getGraphPath by identifying gaps in the data so that we
  45846. * can draw a gap in the line or area. This was moved from ordinal
  45847. * axis module to broken axis module as of #5045.
  45848. *
  45849. * @private
  45850. * @function Highcharts.Series#gappedPath
  45851. *
  45852. * @return {Highcharts.SVGPathArray}
  45853. * Gapped path
  45854. */
  45855. function seriesGappedPath() {
  45856. const currentDataGrouping = this.currentDataGrouping, groupingSize = currentDataGrouping && currentDataGrouping.gapSize, points = this.points.slice(), yAxis = this.yAxis;
  45857. let gapSize = this.options.gapSize, i = points.length - 1, stack;
  45858. /**
  45859. * Defines when to display a gap in the graph, together with the
  45860. * [gapUnit](plotOptions.series.gapUnit) option.
  45861. *
  45862. * In case when `dataGrouping` is enabled, points can be grouped
  45863. * into a larger time span. This can make the grouped points to
  45864. * have a greater distance than the absolute value of `gapSize`
  45865. * property, which will result in disappearing graph completely.
  45866. * To prevent this situation the mentioned distance between
  45867. * grouped points is used instead of previously defined
  45868. * `gapSize`.
  45869. *
  45870. * In practice, this option is most often used to visualize gaps
  45871. * in time series. In a stock chart, intraday data is available
  45872. * for daytime hours, while gaps will appear in nights and
  45873. * weekends.
  45874. *
  45875. * @see [gapUnit](plotOptions.series.gapUnit)
  45876. * @see [xAxis.breaks](#xAxis.breaks)
  45877. *
  45878. * @sample {highstock} stock/plotoptions/series-gapsize/
  45879. * Setting the gap size to 2 introduces gaps for weekends in
  45880. * daily datasets.
  45881. *
  45882. * @type {number}
  45883. * @default 0
  45884. * @product highstock
  45885. * @requires modules/broken-axis
  45886. * @apioption plotOptions.series.gapSize
  45887. */
  45888. /**
  45889. * Together with [gapSize](plotOptions.series.gapSize), this
  45890. * option defines where to draw gaps in the graph.
  45891. *
  45892. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  45893. * means that if the distance between two points is greater than
  45894. * 5 times that of the two closest points, the graph will be
  45895. * broken.
  45896. *
  45897. * When the `gapUnit` is `"value"`, the gap is based on absolute
  45898. * axis values, which on a datetime axis is milliseconds. This
  45899. * also applies to the navigator series that inherits gap
  45900. * options from the base series.
  45901. *
  45902. * @see [gapSize](plotOptions.series.gapSize)
  45903. *
  45904. * @type {string}
  45905. * @default relative
  45906. * @since 5.0.13
  45907. * @product highstock
  45908. * @validvalue ["relative", "value"]
  45909. * @requires modules/broken-axis
  45910. * @apioption plotOptions.series.gapUnit
  45911. */
  45912. if (gapSize && i > 0) { // #5008
  45913. // Gap unit is relative
  45914. if (this.options.gapUnit !== 'value') {
  45915. gapSize *= this.basePointRange;
  45916. }
  45917. // Setting a new gapSize in case dataGrouping is enabled
  45918. // (#7686)
  45919. if (groupingSize &&
  45920. groupingSize > gapSize &&
  45921. // Except when DG is forced (e.g. from other series)
  45922. // and has lower granularity than actual points (#11351)
  45923. groupingSize >= this.basePointRange) {
  45924. gapSize = groupingSize;
  45925. }
  45926. // extension for ordinal breaks
  45927. let current, next;
  45928. while (i--) {
  45929. // Reassign next if it is not visible
  45930. if (!(next && next.visible !== false)) {
  45931. next = points[i + 1];
  45932. }
  45933. current = points[i];
  45934. // Skip iteration if one of the points is not visible
  45935. if (next.visible === false || current.visible === false) {
  45936. continue;
  45937. }
  45938. if (next.x - current.x > gapSize) {
  45939. const xRange = (current.x + next.x) / 2;
  45940. points.splice(// insert after this one
  45941. i + 1, 0, {
  45942. isNull: true,
  45943. x: xRange
  45944. });
  45945. // For stacked chart generate empty stack items, #6546
  45946. if (yAxis.stacking && this.options.stacking) {
  45947. stack = yAxis.stacking.stacks[this.stackKey][xRange] = new StackItem(yAxis, yAxis.options.stackLabels, false, xRange, this.stack);
  45948. stack.total = 0;
  45949. }
  45950. }
  45951. // Assign current to next for the upcoming iteration
  45952. next = current;
  45953. }
  45954. }
  45955. // Call base method
  45956. return this.getGraphPath(points);
  45957. }
  45958. /* *
  45959. *
  45960. * Class
  45961. *
  45962. * */
  45963. /**
  45964. * Provides support for broken axes.
  45965. * @private
  45966. * @class
  45967. */
  45968. class Additions {
  45969. /* *
  45970. *
  45971. * Static Functions
  45972. *
  45973. * */
  45974. /**
  45975. * @private
  45976. */
  45977. static isInBreak(brk, val) {
  45978. const repeat = brk.repeat || Infinity, from = brk.from, length = brk.to - brk.from, test = (val >= from ?
  45979. (val - from) % repeat :
  45980. repeat - ((from - val) % repeat));
  45981. let ret;
  45982. if (!brk.inclusive) {
  45983. ret = test < length && test !== 0;
  45984. }
  45985. else {
  45986. ret = test <= length;
  45987. }
  45988. return ret;
  45989. }
  45990. /**
  45991. * @private
  45992. */
  45993. static lin2Val(val) {
  45994. const axis = this;
  45995. const brokenAxis = axis.brokenAxis;
  45996. const breakArray = brokenAxis && brokenAxis.breakArray;
  45997. if (!breakArray || !isNumber(val)) {
  45998. return val;
  45999. }
  46000. let nval = val, brk, i;
  46001. for (i = 0; i < breakArray.length; i++) {
  46002. brk = breakArray[i];
  46003. if (brk.from >= nval) {
  46004. break;
  46005. }
  46006. else if (brk.to < nval) {
  46007. nval += brk.len;
  46008. }
  46009. else if (Additions.isInBreak(brk, nval)) {
  46010. nval += brk.len;
  46011. }
  46012. }
  46013. return nval;
  46014. }
  46015. /**
  46016. * @private
  46017. */
  46018. static val2Lin(val) {
  46019. const axis = this;
  46020. const brokenAxis = axis.brokenAxis;
  46021. const breakArray = brokenAxis && brokenAxis.breakArray;
  46022. if (!breakArray || !isNumber(val)) {
  46023. return val;
  46024. }
  46025. let nval = val, brk, i;
  46026. for (i = 0; i < breakArray.length; i++) {
  46027. brk = breakArray[i];
  46028. if (brk.to <= val) {
  46029. nval -= brk.len;
  46030. }
  46031. else if (brk.from >= val) {
  46032. break;
  46033. }
  46034. else if (Additions.isInBreak(brk, val)) {
  46035. nval -= (val - brk.from);
  46036. break;
  46037. }
  46038. }
  46039. return nval;
  46040. }
  46041. /* *
  46042. *
  46043. * Constructors
  46044. *
  46045. * */
  46046. constructor(axis) {
  46047. this.hasBreaks = false;
  46048. this.axis = axis;
  46049. }
  46050. /* *
  46051. *
  46052. * Functions
  46053. *
  46054. * */
  46055. /**
  46056. * Returns the first break found where the x is larger then break.from
  46057. * and smaller then break.to.
  46058. *
  46059. * @param {number} x
  46060. * The number which should be within a break.
  46061. *
  46062. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  46063. * The array of breaks to search within.
  46064. *
  46065. * @return {Highcharts.XAxisBreaksOptions|undefined}
  46066. * Returns the first break found that matches, returns false if no break
  46067. * is found.
  46068. */
  46069. findBreakAt(x, breaks) {
  46070. return find(breaks, function (b) {
  46071. return b.from < x && x < b.to;
  46072. });
  46073. }
  46074. /**
  46075. * @private
  46076. */
  46077. isInAnyBreak(val, testKeep) {
  46078. const brokenAxis = this, axis = brokenAxis.axis, breaks = axis.options.breaks || [];
  46079. let i = breaks.length, inbrk, keep, ret;
  46080. if (i && isNumber(val)) {
  46081. while (i--) {
  46082. if (Additions.isInBreak(breaks[i], val)) {
  46083. inbrk = true;
  46084. if (!keep) {
  46085. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  46086. }
  46087. }
  46088. }
  46089. if (inbrk && testKeep) {
  46090. ret = inbrk && !keep;
  46091. }
  46092. else {
  46093. ret = inbrk;
  46094. }
  46095. }
  46096. return ret;
  46097. }
  46098. /**
  46099. * Dynamically set or unset breaks in an axis. This function in lighter
  46100. * than usin Axis.update, and it also preserves animation.
  46101. *
  46102. * @private
  46103. * @function Highcharts.Axis#setBreaks
  46104. *
  46105. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  46106. * The breaks to add. When `undefined` it removes existing breaks.
  46107. *
  46108. * @param {boolean} [redraw=true]
  46109. * Whether to redraw the chart immediately.
  46110. */
  46111. setBreaks(breaks, redraw) {
  46112. const brokenAxis = this;
  46113. const axis = brokenAxis.axis;
  46114. const hasBreaks = (isArray(breaks) && !!breaks.length);
  46115. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  46116. brokenAxis.hasBreaks = hasBreaks;
  46117. if (breaks !== axis.options.breaks) {
  46118. axis.options.breaks = axis.userOptions.breaks = breaks;
  46119. }
  46120. axis.forceRedraw = true; // Force recalculation in setScale
  46121. // Recalculate series related to the axis.
  46122. axis.series.forEach(function (series) {
  46123. series.isDirty = true;
  46124. });
  46125. if (!hasBreaks && axis.val2lin === Additions.val2Lin) {
  46126. // Revert to prototype functions
  46127. delete axis.val2lin;
  46128. delete axis.lin2val;
  46129. }
  46130. if (hasBreaks) {
  46131. axis.userOptions.ordinal = false;
  46132. axis.lin2val = Additions.lin2Val;
  46133. axis.val2lin = Additions.val2Lin;
  46134. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  46135. // If trying to set extremes inside a break, extend min to
  46136. // after, and max to before the break ( #3857 )
  46137. if (brokenAxis.hasBreaks) {
  46138. const breaks = (this.options.breaks || []);
  46139. let axisBreak;
  46140. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  46141. newMin = axisBreak.to;
  46142. }
  46143. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  46144. newMax = axisBreak.from;
  46145. }
  46146. // If both min and max is within the same break.
  46147. if (newMax < newMin) {
  46148. newMax = newMin;
  46149. }
  46150. }
  46151. axis.constructor.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  46152. };
  46153. axis.setAxisTranslation = function () {
  46154. axis.constructor.prototype.setAxisTranslation.call(this);
  46155. brokenAxis.unitLength = void 0;
  46156. if (brokenAxis.hasBreaks) {
  46157. const breaks = axis.options.breaks || [],
  46158. // Temporary one:
  46159. breakArrayT = [], breakArray = [], pointRangePadding = pick(axis.pointRangePadding, 0);
  46160. let length = 0, inBrk, repeat, min = axis.userMin || axis.min, max = axis.userMax || axis.max, start, i;
  46161. // Min & max check (#4247)
  46162. breaks.forEach(function (brk) {
  46163. repeat = brk.repeat || Infinity;
  46164. if (isNumber(min) && isNumber(max)) {
  46165. if (Additions.isInBreak(brk, min)) {
  46166. min += ((brk.to % repeat) -
  46167. (min % repeat));
  46168. }
  46169. if (Additions.isInBreak(brk, max)) {
  46170. max -= ((max % repeat) -
  46171. (brk.from % repeat));
  46172. }
  46173. }
  46174. });
  46175. // Construct an array holding all breaks in the axis
  46176. breaks.forEach(function (brk) {
  46177. start = brk.from;
  46178. repeat = brk.repeat || Infinity;
  46179. if (isNumber(min) && isNumber(max)) {
  46180. while (start - repeat > min) {
  46181. start -= repeat;
  46182. }
  46183. while (start < min) {
  46184. start += repeat;
  46185. }
  46186. for (i = start; i < max; i += repeat) {
  46187. breakArrayT.push({
  46188. value: i,
  46189. move: 'in'
  46190. });
  46191. breakArrayT.push({
  46192. value: i + brk.to - brk.from,
  46193. move: 'out',
  46194. size: brk.breakSize
  46195. });
  46196. }
  46197. }
  46198. });
  46199. breakArrayT.sort(function (a, b) {
  46200. return ((a.value === b.value) ?
  46201. ((a.move === 'in' ? 0 : 1) -
  46202. (b.move === 'in' ? 0 : 1)) :
  46203. a.value - b.value);
  46204. });
  46205. // Simplify the breaks
  46206. inBrk = 0;
  46207. start = min;
  46208. breakArrayT.forEach(function (brk) {
  46209. inBrk += (brk.move === 'in' ? 1 : -1);
  46210. if (inBrk === 1 && brk.move === 'in') {
  46211. start = brk.value;
  46212. }
  46213. if (inBrk === 0 && isNumber(start)) {
  46214. breakArray.push({
  46215. from: start,
  46216. to: brk.value,
  46217. len: brk.value - start - (brk.size || 0)
  46218. });
  46219. length += (brk.value -
  46220. start -
  46221. (brk.size || 0));
  46222. }
  46223. });
  46224. brokenAxis.breakArray = breakArray;
  46225. // Used with staticScale, and below the actual axis
  46226. // length, when breaks are substracted.
  46227. if (isNumber(min) &&
  46228. isNumber(max) &&
  46229. isNumber(axis.min)) {
  46230. brokenAxis.unitLength = max - min - length +
  46231. pointRangePadding;
  46232. fireEvent(axis, 'afterBreaks');
  46233. if (axis.staticScale) {
  46234. axis.transA = axis.staticScale;
  46235. }
  46236. else if (brokenAxis.unitLength) {
  46237. axis.transA *=
  46238. (max - axis.min + pointRangePadding) /
  46239. brokenAxis.unitLength;
  46240. }
  46241. if (pointRangePadding) {
  46242. axis.minPixelPadding =
  46243. axis.transA * (axis.minPointOffset || 0);
  46244. }
  46245. axis.min = min;
  46246. axis.max = max;
  46247. }
  46248. }
  46249. };
  46250. }
  46251. if (pick(redraw, true)) {
  46252. axis.chart.redraw();
  46253. }
  46254. }
  46255. }
  46256. BrokenAxis.Additions = Additions;
  46257. })(BrokenAxis || (BrokenAxis = {}));
  46258. /* *
  46259. *
  46260. * Default Export
  46261. *
  46262. * */
  46263. return BrokenAxis;
  46264. });
  46265. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [_modules['Core/Globals.js'], _modules['Core/Axis/BrokenAxis.js']], function (Highcharts, BrokenAxis) {
  46266. const G = Highcharts;
  46267. // Compositions
  46268. BrokenAxis.compose(G.Axis, G.Series);
  46269. });
  46270. _registerModule(_modules, 'Extensions/DataGrouping/ApproximationRegistry.js', [], function () {
  46271. /* *
  46272. *
  46273. * (c) 2010-2021 Torstein Honsi
  46274. *
  46275. * License: www.highcharts.com/license
  46276. *
  46277. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46278. *
  46279. * */
  46280. /* *
  46281. *
  46282. * Constants
  46283. *
  46284. * */
  46285. /**
  46286. * Define the available approximation types. The data grouping
  46287. * approximations takes an array or numbers as the first parameter. In case
  46288. * of ohlc, four arrays are sent in as four parameters. Each array consists
  46289. * only of numbers. In case null values belong to the group, the property
  46290. * .hasNulls will be set to true on the array.
  46291. *
  46292. * @product highstock
  46293. *
  46294. * @private
  46295. */
  46296. const ApproximationRegistry = {
  46297. // approximations added programmatically
  46298. };
  46299. /* *
  46300. *
  46301. * Default Export
  46302. *
  46303. * */
  46304. return ApproximationRegistry;
  46305. });
  46306. _registerModule(_modules, 'Extensions/DataGrouping/ApproximationDefaults.js', [_modules['Extensions/DataGrouping/ApproximationRegistry.js'], _modules['Core/Utilities.js']], function (ApproximationRegistry, U) {
  46307. /* *
  46308. *
  46309. * (c) 2010-2021 Torstein Honsi
  46310. *
  46311. * License: www.highcharts.com/license
  46312. *
  46313. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46314. *
  46315. * */
  46316. const { arrayMax, arrayMin, correctFloat, extend, isNumber } = U;
  46317. /* *
  46318. *
  46319. * Functions
  46320. *
  46321. * */
  46322. /**
  46323. * @private
  46324. */
  46325. function average(arr) {
  46326. const len = arr.length;
  46327. let ret = sum(arr);
  46328. // If we have a number, return it divided by the length. If not,
  46329. // return null or undefined based on what the sum method finds.
  46330. if (isNumber(ret) && len) {
  46331. ret = correctFloat(ret / len);
  46332. }
  46333. return ret;
  46334. }
  46335. /**
  46336. * The same as average, but for series with multiple values, like area ranges.
  46337. * @private
  46338. */
  46339. function averages() {
  46340. const ret = [];
  46341. [].forEach.call(arguments, function (arr) {
  46342. ret.push(average(arr));
  46343. });
  46344. // Return undefined when first elem. is undefined and let
  46345. // sum method handle null (#7377)
  46346. return typeof ret[0] === 'undefined' ? void 0 : ret;
  46347. }
  46348. /**
  46349. * @private
  46350. */
  46351. function close(arr) {
  46352. return arr.length ?
  46353. arr[arr.length - 1] :
  46354. (arr.hasNulls ? null : void 0);
  46355. }
  46356. /**
  46357. * @private
  46358. */
  46359. function high(arr) {
  46360. return arr.length ?
  46361. arrayMax(arr) :
  46362. (arr.hasNulls ? null : void 0);
  46363. }
  46364. /**
  46365. * HLC, OHLC and range are special cases where a multidimensional array is input
  46366. * and an array is output.
  46367. * @private
  46368. */
  46369. function hlc(high, low, close) {
  46370. high = ApproximationRegistry.high(high);
  46371. low = ApproximationRegistry.low(low);
  46372. close = ApproximationRegistry.close(close);
  46373. if (isNumber(high) ||
  46374. isNumber(low) ||
  46375. isNumber(close)) {
  46376. return [high, low, close];
  46377. }
  46378. }
  46379. /**
  46380. * @private
  46381. */
  46382. function low(arr) {
  46383. return arr.length ?
  46384. arrayMin(arr) :
  46385. (arr.hasNulls ? null : void 0);
  46386. }
  46387. /**
  46388. * @private
  46389. */
  46390. function ohlc(open, high, low, close) {
  46391. open = ApproximationRegistry.open(open);
  46392. high = ApproximationRegistry.high(high);
  46393. low = ApproximationRegistry.low(low);
  46394. close = ApproximationRegistry.close(close);
  46395. if (isNumber(open) ||
  46396. isNumber(high) ||
  46397. isNumber(low) ||
  46398. isNumber(close)) {
  46399. return [open, high, low, close];
  46400. }
  46401. }
  46402. /**
  46403. * @private
  46404. */
  46405. function open(arr) {
  46406. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  46407. }
  46408. /**
  46409. * @private
  46410. */
  46411. function range(low, high) {
  46412. low = ApproximationRegistry.low(low);
  46413. high = ApproximationRegistry.high(high);
  46414. if (isNumber(low) || isNumber(high)) {
  46415. return [low, high];
  46416. }
  46417. if (low === null && high === null) {
  46418. return null;
  46419. }
  46420. // else, return is undefined
  46421. }
  46422. /**
  46423. * @private
  46424. */
  46425. function sum(arr) {
  46426. let len = arr.length, ret;
  46427. // 1. it consists of nulls exclusive
  46428. if (!len && arr.hasNulls) {
  46429. ret = null;
  46430. // 2. it has a length and real values
  46431. }
  46432. else if (len) {
  46433. ret = 0;
  46434. while (len--) {
  46435. ret += arr[len];
  46436. }
  46437. }
  46438. // 3. it has zero length, so just return undefined
  46439. // => doNothing()
  46440. return ret;
  46441. }
  46442. /* *
  46443. *
  46444. * Default Export
  46445. *
  46446. * */
  46447. const ApproximationDefaults = {
  46448. average,
  46449. averages,
  46450. close,
  46451. high,
  46452. hlc,
  46453. low,
  46454. ohlc,
  46455. open,
  46456. range,
  46457. sum
  46458. };
  46459. extend(ApproximationRegistry, ApproximationDefaults);
  46460. return ApproximationDefaults;
  46461. });
  46462. _registerModule(_modules, 'Extensions/DataGrouping/DataGroupingDefaults.js', [], function () {
  46463. /* *
  46464. *
  46465. * (c) 2010-2021 Torstein Honsi
  46466. *
  46467. * License: www.highcharts.com/license
  46468. *
  46469. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46470. *
  46471. * */
  46472. /* *
  46473. *
  46474. * Constants
  46475. *
  46476. * */
  46477. /**
  46478. * Common options
  46479. * @private
  46480. */
  46481. const common = {
  46482. // enabled: null, // (true for stock charts, false for basic),
  46483. // forced: undefined,
  46484. groupPixelWidth: 2,
  46485. // the first one is the point or start value, the second is the start
  46486. // value if we're dealing with range, the third one is the end value if
  46487. // dealing with a range
  46488. dateTimeLabelFormats: {
  46489. millisecond: [
  46490. '%A, %e %b, %H:%M:%S.%L',
  46491. '%A, %e %b, %H:%M:%S.%L',
  46492. '-%H:%M:%S.%L'
  46493. ],
  46494. second: [
  46495. '%A, %e %b, %H:%M:%S',
  46496. '%A, %e %b, %H:%M:%S',
  46497. '-%H:%M:%S'
  46498. ],
  46499. minute: [
  46500. '%A, %e %b, %H:%M',
  46501. '%A, %e %b, %H:%M',
  46502. '-%H:%M'
  46503. ],
  46504. hour: [
  46505. '%A, %e %b, %H:%M',
  46506. '%A, %e %b, %H:%M',
  46507. '-%H:%M'
  46508. ],
  46509. day: [
  46510. '%A, %e %b %Y',
  46511. '%A, %e %b',
  46512. '-%A, %e %b %Y'
  46513. ],
  46514. week: [
  46515. 'Week from %A, %e %b %Y',
  46516. '%A, %e %b',
  46517. '-%A, %e %b %Y'
  46518. ],
  46519. month: [
  46520. '%B %Y',
  46521. '%B',
  46522. '-%B %Y'
  46523. ],
  46524. year: [
  46525. '%Y',
  46526. '%Y',
  46527. '-%Y'
  46528. ]
  46529. }
  46530. // smoothed = false, // enable this for navigator series only
  46531. };
  46532. /**
  46533. * Extends common options
  46534. * @private
  46535. */
  46536. const seriesSpecific = {
  46537. line: {},
  46538. spline: {},
  46539. area: {},
  46540. areaspline: {},
  46541. arearange: {},
  46542. column: {
  46543. groupPixelWidth: 10
  46544. },
  46545. columnrange: {
  46546. groupPixelWidth: 10
  46547. },
  46548. candlestick: {
  46549. groupPixelWidth: 10
  46550. },
  46551. ohlc: {
  46552. groupPixelWidth: 5
  46553. },
  46554. hlc: {
  46555. groupPixelWidth: 5
  46556. // Move to HeikinAshiSeries.ts aftre refactoring data grouping.
  46557. },
  46558. heikinashi: {
  46559. groupPixelWidth: 10
  46560. }
  46561. };
  46562. /**
  46563. * Units are defined in a separate array to allow complete overriding in
  46564. * case of a user option.
  46565. * @private
  46566. */
  46567. const units = [
  46568. [
  46569. 'millisecond',
  46570. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  46571. ], [
  46572. 'second',
  46573. [1, 2, 5, 10, 15, 30]
  46574. ], [
  46575. 'minute',
  46576. [1, 2, 5, 10, 15, 30]
  46577. ], [
  46578. 'hour',
  46579. [1, 2, 3, 4, 6, 8, 12]
  46580. ], [
  46581. 'day',
  46582. [1]
  46583. ], [
  46584. 'week',
  46585. [1]
  46586. ], [
  46587. 'month',
  46588. [1, 3, 6]
  46589. ], [
  46590. 'year',
  46591. null
  46592. ]
  46593. ];
  46594. /* *
  46595. *
  46596. * Default Export
  46597. *
  46598. * */
  46599. const DataGroupingDefaults = {
  46600. common,
  46601. seriesSpecific,
  46602. units
  46603. };
  46604. return DataGroupingDefaults;
  46605. });
  46606. _registerModule(_modules, 'Extensions/DataGrouping/DataGroupingAxisComposition.js', [_modules['Extensions/DataGrouping/DataGroupingDefaults.js'], _modules['Core/Utilities.js']], function (DataGroupingDefaults, U) {
  46607. /* *
  46608. *
  46609. * (c) 2010-2021 Torstein Honsi
  46610. *
  46611. * License: www.highcharts.com/license
  46612. *
  46613. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46614. *
  46615. * */
  46616. const { addEvent, extend, merge, pick } = U;
  46617. /* *
  46618. *
  46619. * Constants
  46620. *
  46621. * */
  46622. const composedMembers = [];
  46623. /* *
  46624. *
  46625. * Variables
  46626. *
  46627. * */
  46628. let AxisConstructor;
  46629. /* *
  46630. *
  46631. * Functions
  46632. *
  46633. * */
  46634. /**
  46635. * Check the groupPixelWidth and apply the grouping if needed.
  46636. * Fired only after processing the data.
  46637. *
  46638. * @product highstock
  46639. *
  46640. * @function Highcharts.Axis#applyGrouping
  46641. */
  46642. function applyGrouping(e) {
  46643. const axis = this, series = axis.series;
  46644. // Reset the groupPixelWidth for all series, #17141.
  46645. series.forEach(function (series) {
  46646. series.groupPixelWidth = void 0; // #2110
  46647. });
  46648. series.forEach(function (series) {
  46649. series.groupPixelWidth = (axis.getGroupPixelWidth &&
  46650. axis.getGroupPixelWidth());
  46651. if (series.groupPixelWidth) {
  46652. series.hasProcessed = true; // #2692
  46653. }
  46654. // Fire independing on series.groupPixelWidth to always set a proper
  46655. // dataGrouping state, (#16238)
  46656. series.applyGrouping(!!e.hasExtremesChanged);
  46657. });
  46658. }
  46659. /**
  46660. * @private
  46661. */
  46662. function compose(AxisClass) {
  46663. AxisConstructor = AxisClass;
  46664. if (U.pushUnique(composedMembers, AxisClass)) {
  46665. addEvent(AxisClass, 'afterSetScale', onAfterSetScale);
  46666. // When all series are processed, calculate the group pixel width and
  46667. // then if this value is different than zero apply groupings.
  46668. addEvent(AxisClass, 'postProcessData', applyGrouping);
  46669. extend(AxisClass.prototype, {
  46670. applyGrouping,
  46671. getGroupPixelWidth,
  46672. setDataGrouping
  46673. });
  46674. }
  46675. }
  46676. /**
  46677. * Get the data grouping pixel width based on the greatest defined individual
  46678. * width of the axis' series, and if whether one of the axes need grouping.
  46679. * @private
  46680. */
  46681. function getGroupPixelWidth() {
  46682. const series = this.series;
  46683. let i = series.length, groupPixelWidth = 0, doGrouping = false, dataLength, dgOptions;
  46684. // If one of the series needs grouping, apply it to all (#1634)
  46685. while (i--) {
  46686. dgOptions = series[i].options.dataGrouping;
  46687. if (dgOptions) { // #2692
  46688. // If multiple series are compared on the same x axis, give them the
  46689. // same group pixel width (#334)
  46690. groupPixelWidth = Math.max(groupPixelWidth,
  46691. // Fallback to commonOptions (#9693)
  46692. pick(dgOptions.groupPixelWidth, DataGroupingDefaults.common.groupPixelWidth));
  46693. dataLength = (series[i].processedXData || series[i].data).length;
  46694. // Execute grouping if the amount of points is greater than the
  46695. // limit defined in groupPixelWidth
  46696. if (series[i].groupPixelWidth ||
  46697. (dataLength >
  46698. (this.chart.plotSizeX / groupPixelWidth)) ||
  46699. (dataLength && dgOptions.forced)) {
  46700. doGrouping = true;
  46701. }
  46702. }
  46703. }
  46704. return doGrouping ? groupPixelWidth : 0;
  46705. }
  46706. /**
  46707. * When resetting the scale reset the hasProccessed flag to avoid taking
  46708. * previous data grouping of neighbour series into accound when determining
  46709. * group pixel width (#2692).
  46710. * @private
  46711. */
  46712. function onAfterSetScale() {
  46713. this.series.forEach(function (series) {
  46714. series.hasProcessed = false;
  46715. });
  46716. }
  46717. /**
  46718. * Highcharts Stock only. Force data grouping on all the axis' series.
  46719. *
  46720. * @product highstock
  46721. *
  46722. * @function Highcharts.Axis#setDataGrouping
  46723. *
  46724. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  46725. * A `dataGrouping` configuration. Use `false` to disable data grouping
  46726. * dynamically.
  46727. *
  46728. * @param {boolean} [redraw=true]
  46729. * Whether to redraw the chart or wait for a later call to
  46730. * {@link Chart#redraw}.
  46731. */
  46732. function setDataGrouping(dataGrouping, redraw) {
  46733. const axis = this;
  46734. let i;
  46735. redraw = pick(redraw, true);
  46736. if (!dataGrouping) {
  46737. dataGrouping = {
  46738. forced: false,
  46739. units: null
  46740. };
  46741. }
  46742. // Axis is instantiated, update all series
  46743. if (this instanceof AxisConstructor) {
  46744. i = this.series.length;
  46745. while (i--) {
  46746. this.series[i].update({
  46747. dataGrouping: dataGrouping
  46748. }, false);
  46749. }
  46750. // Axis not yet instanciated, alter series options
  46751. }
  46752. else {
  46753. this.chart.options.series.forEach(function (seriesOptions) {
  46754. // Merging dataGrouping options with already defined options #16759
  46755. seriesOptions.dataGrouping = typeof dataGrouping === 'boolean' ?
  46756. dataGrouping :
  46757. merge(dataGrouping, seriesOptions.dataGrouping);
  46758. });
  46759. }
  46760. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  46761. if (axis.ordinal) {
  46762. axis.ordinal.slope = void 0;
  46763. }
  46764. if (redraw) {
  46765. this.chart.redraw();
  46766. }
  46767. }
  46768. /* *
  46769. *
  46770. * Default Export
  46771. *
  46772. * */
  46773. const DataGroupingAxisComposition = {
  46774. compose
  46775. };
  46776. return DataGroupingAxisComposition;
  46777. });
  46778. _registerModule(_modules, 'Extensions/DataGrouping/DataGroupingSeriesComposition.js', [_modules['Extensions/DataGrouping/ApproximationRegistry.js'], _modules['Extensions/DataGrouping/DataGroupingDefaults.js'], _modules['Core/Axis/DateTimeAxis.js'], _modules['Core/Defaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (ApproximationRegistry, DataGroupingDefaults, DateTimeAxis, D, SeriesRegistry, U) {
  46779. /* *
  46780. *
  46781. * (c) 2010-2021 Torstein Honsi
  46782. *
  46783. * License: www.highcharts.com/license
  46784. *
  46785. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46786. *
  46787. * */
  46788. const { series: { prototype: seriesProto } } = SeriesRegistry;
  46789. const { addEvent, defined, error, extend, isNumber, merge, pick } = U;
  46790. /* *
  46791. *
  46792. * Constants
  46793. *
  46794. * */
  46795. const baseGeneratePoints = seriesProto.generatePoints;
  46796. const composedMembers = [];
  46797. /* *
  46798. *
  46799. * Functions
  46800. *
  46801. * */
  46802. /**
  46803. * @private
  46804. */
  46805. function adjustExtremes(xAxis, groupedXData) {
  46806. // Make sure the X axis extends to show the first group (#2533)
  46807. // But only for visible series (#5493, #6393)
  46808. if (defined(groupedXData[0]) &&
  46809. isNumber(xAxis.min) &&
  46810. isNumber(xAxis.dataMin) &&
  46811. groupedXData[0] < xAxis.min) {
  46812. if ((!defined(xAxis.options.min) &&
  46813. xAxis.min <= xAxis.dataMin) ||
  46814. xAxis.min === xAxis.dataMin) {
  46815. xAxis.min = Math.min(groupedXData[0], xAxis.min);
  46816. }
  46817. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  46818. }
  46819. // When the last anchor set, change the extremes that
  46820. // the last point is visible (#12455).
  46821. if (defined(groupedXData[groupedXData.length - 1]) &&
  46822. isNumber(xAxis.max) &&
  46823. isNumber(xAxis.dataMax) &&
  46824. groupedXData[groupedXData.length - 1] > xAxis.max) {
  46825. if ((!defined(xAxis.options.max) &&
  46826. isNumber(xAxis.dataMax) &&
  46827. xAxis.max >= xAxis.dataMax) || xAxis.max === xAxis.dataMax) {
  46828. xAxis.max = Math.max(groupedXData[groupedXData.length - 1], xAxis.max);
  46829. }
  46830. xAxis.dataMax = Math.max(groupedXData[groupedXData.length - 1], xAxis.dataMax);
  46831. }
  46832. }
  46833. /**
  46834. * @private
  46835. */
  46836. function anchorPoints(series, groupedXData, xMax) {
  46837. const options = series.options, dataGroupingOptions = options.dataGrouping, totalRange = (series.currentDataGrouping && series.currentDataGrouping.gapSize);
  46838. let i;
  46839. // DataGrouping x-coordinates.
  46840. if (dataGroupingOptions && series.xData && totalRange && series.groupMap) {
  46841. const groupedDataLength = groupedXData.length - 1, anchor = dataGroupingOptions.anchor, firstAnchor = pick(dataGroupingOptions.firstAnchor, anchor), lastAnchor = pick(dataGroupingOptions.lastAnchor, anchor);
  46842. // Anchor points that are not extremes.
  46843. if (anchor && anchor !== 'start') {
  46844. const shiftInterval = (totalRange *
  46845. { middle: 0.5, end: 1 }[anchor]);
  46846. i = groupedXData.length - 1;
  46847. while (i-- && i > 0) {
  46848. groupedXData[i] += shiftInterval;
  46849. }
  46850. }
  46851. // Change the first point position, but only when it is
  46852. // the first point in the data set not in the current zoom.
  46853. if (firstAnchor &&
  46854. firstAnchor !== 'start' &&
  46855. series.xData[0] >= groupedXData[0]) {
  46856. const groupStart = series.groupMap[0].start, groupLength = series.groupMap[0].length;
  46857. let firstGroupstEnd;
  46858. if (isNumber(groupStart) && isNumber(groupLength)) {
  46859. firstGroupstEnd = groupStart + (groupLength - 1);
  46860. }
  46861. groupedXData[0] = {
  46862. middle: groupedXData[0] + 0.5 * totalRange,
  46863. end: groupedXData[0] + totalRange,
  46864. firstPoint: series.xData[0],
  46865. lastPoint: firstGroupstEnd && series.xData[firstGroupstEnd]
  46866. }[firstAnchor];
  46867. }
  46868. // Change the last point position but only when it is
  46869. // the last point in the data set not in the current zoom.
  46870. if (lastAnchor &&
  46871. lastAnchor !== 'start' &&
  46872. totalRange &&
  46873. groupedXData[groupedDataLength] >= xMax - totalRange) {
  46874. const lastGroupStart = series.groupMap[series.groupMap.length - 1].start;
  46875. groupedXData[groupedDataLength] = {
  46876. middle: groupedXData[groupedDataLength] + 0.5 * totalRange,
  46877. end: groupedXData[groupedDataLength] + totalRange,
  46878. firstPoint: lastGroupStart && series.xData[lastGroupStart],
  46879. lastPoint: series.xData[series.xData.length - 1]
  46880. }[lastAnchor];
  46881. }
  46882. }
  46883. }
  46884. /**
  46885. * For the processed data, calculate the grouped data if needed.
  46886. *
  46887. * @private
  46888. * @function Highcharts.Series#applyGrouping
  46889. */
  46890. function applyGrouping(hasExtremesChanged) {
  46891. const series = this, chart = series.chart, options = series.options, dataGroupingOptions = options.dataGrouping, groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  46892. pick(dataGroupingOptions.enabled, chart.options.isStock), visible = (series.visible || !chart.options.chart.ignoreHiddenSeries), lastDataGrouping = this.currentDataGrouping;
  46893. let currentDataGrouping, croppedData, revertRequireSorting = false;
  46894. // Data needs to be sorted for dataGrouping
  46895. if (groupingEnabled && !series.requireSorting) {
  46896. series.requireSorting = revertRequireSorting = true;
  46897. }
  46898. // Skip if skipDataGrouping method returns false or if grouping is disabled
  46899. // (in that order).
  46900. const skip = skipDataGrouping(series, hasExtremesChanged) === false || !groupingEnabled;
  46901. // Revert original requireSorting value if changed
  46902. if (revertRequireSorting) {
  46903. series.requireSorting = false;
  46904. }
  46905. if (!skip) {
  46906. series.destroyGroupedData();
  46907. const processedXData = dataGroupingOptions.groupAll ?
  46908. series.xData :
  46909. series.processedXData, processedYData = dataGroupingOptions.groupAll ?
  46910. series.yData :
  46911. series.processedYData, plotSizeX = chart.plotSizeX, xAxis = series.xAxis, ordinal = xAxis.options.ordinal, groupPixelWidth = series.groupPixelWidth;
  46912. let i, hasGroupedData;
  46913. // Execute grouping if the amount of points is greater than the limit
  46914. // defined in groupPixelWidth
  46915. if (groupPixelWidth &&
  46916. processedXData &&
  46917. processedXData.length &&
  46918. plotSizeX) {
  46919. hasGroupedData = true;
  46920. // Force recreation of point instances in series.translate, #5699
  46921. series.isDirty = true;
  46922. series.points = null; // #6709
  46923. const extremes = xAxis.getExtremes(), xMin = extremes.min, xMax = extremes.max, groupIntervalFactor = (ordinal &&
  46924. xAxis.ordinal &&
  46925. xAxis.ordinal.getGroupIntervalFactor(xMin, xMax, series)) || 1, interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  46926. groupIntervalFactor, groupPositions = xAxis.getTimeTicks(DateTimeAxis.Additions.prototype.normalizeTimeTickInterval(interval, dataGroupingOptions.units ||
  46927. DataGroupingDefaults.units),
  46928. // Processed data may extend beyond axis (#4907)
  46929. Math.min(xMin, processedXData[0]), Math.max(xMax, processedXData[processedXData.length - 1]), xAxis.options.startOfWeek, processedXData, series.closestPointRange), groupedData = seriesProto.groupData.apply(series, [
  46930. processedXData,
  46931. processedYData,
  46932. groupPositions,
  46933. dataGroupingOptions.approximation
  46934. ]);
  46935. let groupedXData = groupedData.groupedXData, groupedYData = groupedData.groupedYData, gapSize = 0;
  46936. // The smoothed option is deprecated, instead, there is a fallback
  46937. // to the new anchoring mechanism. #12455.
  46938. if (dataGroupingOptions &&
  46939. dataGroupingOptions.smoothed &&
  46940. groupedXData.length) {
  46941. dataGroupingOptions.firstAnchor = 'firstPoint';
  46942. dataGroupingOptions.anchor = 'middle';
  46943. dataGroupingOptions.lastAnchor = 'lastPoint';
  46944. error(32, false, chart, {
  46945. 'dataGrouping.smoothed': 'use dataGrouping.anchor'
  46946. });
  46947. }
  46948. anchorPoints(series, groupedXData, xMax);
  46949. // Record what data grouping values were used
  46950. for (i = 1; i < groupPositions.length; i++) {
  46951. // The grouped gapSize needs to be the largest distance between
  46952. // the group to capture varying group sizes like months or DST
  46953. // crossing (#10000). Also check that the gap is not at the
  46954. // start of a segment.
  46955. if (!groupPositions.info.segmentStarts ||
  46956. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  46957. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  46958. }
  46959. }
  46960. currentDataGrouping = groupPositions.info;
  46961. currentDataGrouping.gapSize = gapSize;
  46962. series.closestPointRange = groupPositions.info.totalRange;
  46963. series.groupMap = groupedData.groupMap;
  46964. if (visible) {
  46965. adjustExtremes(xAxis, groupedXData);
  46966. }
  46967. // We calculated all group positions but we should render
  46968. // only the ones within the visible range
  46969. if (dataGroupingOptions.groupAll) {
  46970. // Keep the reference to all grouped points
  46971. // for further calculation (eg. heikinashi).
  46972. series.allGroupedData = groupedYData;
  46973. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  46974. );
  46975. groupedXData = croppedData.xData;
  46976. groupedYData = croppedData.yData;
  46977. series.cropStart = croppedData.start; // #15005
  46978. }
  46979. // Set series props
  46980. series.processedXData = groupedXData;
  46981. series.processedYData = groupedYData;
  46982. }
  46983. else {
  46984. series.groupMap = null;
  46985. }
  46986. series.hasGroupedData = hasGroupedData;
  46987. series.currentDataGrouping = currentDataGrouping;
  46988. series.preventGraphAnimation =
  46989. (lastDataGrouping && lastDataGrouping.totalRange) !==
  46990. (currentDataGrouping && currentDataGrouping.totalRange);
  46991. }
  46992. }
  46993. /**
  46994. * @private
  46995. */
  46996. function compose(SeriesClass) {
  46997. const PointClass = SeriesClass.prototype.pointClass;
  46998. if (U.pushUnique(composedMembers, PointClass)) {
  46999. // Override point prototype to throw a warning when trying to update
  47000. // grouped points.
  47001. addEvent(PointClass, 'update', function () {
  47002. if (this.dataGroup) {
  47003. error(24, false, this.series.chart);
  47004. return false;
  47005. }
  47006. });
  47007. }
  47008. if (U.pushUnique(composedMembers, SeriesClass)) {
  47009. addEvent(SeriesClass, 'afterSetOptions', onAfterSetOptions);
  47010. addEvent(SeriesClass, 'destroy', destroyGroupedData);
  47011. extend(SeriesClass.prototype, {
  47012. applyGrouping,
  47013. destroyGroupedData,
  47014. generatePoints,
  47015. getDGApproximation,
  47016. groupData
  47017. });
  47018. }
  47019. }
  47020. /**
  47021. * Destroy the grouped data points. #622, #740
  47022. * @private
  47023. */
  47024. function destroyGroupedData() {
  47025. // Clear previous groups
  47026. if (this.groupedData) {
  47027. this.groupedData.forEach(function (point, i) {
  47028. if (point) {
  47029. this.groupedData[i] = point.destroy ?
  47030. point.destroy() : null;
  47031. }
  47032. }, this);
  47033. // Clears all:
  47034. // - `this.groupedData`
  47035. // - `this.points`
  47036. // - `preserve` object in series.update()
  47037. this.groupedData.length = 0;
  47038. }
  47039. }
  47040. /**
  47041. * Override the generatePoints method by adding a reference to grouped data
  47042. * @private
  47043. */
  47044. function generatePoints() {
  47045. baseGeneratePoints.apply(this);
  47046. // Record grouped data in order to let it be destroyed the next time
  47047. // processData runs
  47048. this.destroyGroupedData(); // #622
  47049. this.groupedData = this.hasGroupedData ? this.points : null;
  47050. }
  47051. /**
  47052. * Set default approximations to the prototypes if present. Properties are
  47053. * inherited down. Can be overridden for individual series types.
  47054. * @private
  47055. */
  47056. function getDGApproximation() {
  47057. if (this.is('arearange')) {
  47058. return 'range';
  47059. }
  47060. if (this.is('ohlc')) {
  47061. return 'ohlc';
  47062. }
  47063. if (this.is('hlc')) {
  47064. return 'hlc';
  47065. }
  47066. if (
  47067. // #18974, default approximation for cumulative
  47068. // should be `sum` when `dataGrouping` is enabled
  47069. this.is('column') ||
  47070. this.options.cumulative) {
  47071. return 'sum';
  47072. }
  47073. return 'average';
  47074. }
  47075. /**
  47076. * Highcharts Stock only. Takes parallel arrays of x and y data and groups the
  47077. * data into intervals defined by groupPositions, a collection of starting x
  47078. * values for each group.
  47079. *
  47080. * @product highstock
  47081. *
  47082. * @function Highcharts.Series#groupData
  47083. * @param {Array<number>} xData
  47084. * Parallel array of x data.
  47085. * @param {Array<(number|null|undefined)>|Array<Array<(number|null|undefined)>>} yData
  47086. * Parallel array of y data.
  47087. * @param {Array<number>} groupPositions
  47088. * Group positions.
  47089. * @param {string|Function} [approximation]
  47090. * Approximation to use.
  47091. * @return {Highcharts.DataGroupingResultObject}
  47092. * Mapped groups.
  47093. */
  47094. function groupData(xData, yData, groupPositions, approximation) {
  47095. const series = this, data = series.data, dataOptions = series.options && series.options.data, groupedXData = [], groupedYData = [], groupMap = [], dataLength = xData.length,
  47096. // when grouping the fake extended axis for panning,
  47097. // we don't need to consider y
  47098. handleYData = !!yData, values = [], pointArrayMap = series.pointArrayMap, pointArrayMapLength = pointArrayMap && pointArrayMap.length, extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']), groupAll = (this.options.dataGrouping &&
  47099. this.options.dataGrouping.groupAll);
  47100. let pointX, pointY, groupedY, pos = 0, start = 0;
  47101. const approximationFn = (typeof approximation === 'function' ?
  47102. approximation :
  47103. approximation && ApproximationRegistry[approximation] ?
  47104. ApproximationRegistry[approximation] :
  47105. ApproximationRegistry[(series.getDGApproximation && series.getDGApproximation() ||
  47106. 'average')]);
  47107. // Calculate values array size from pointArrayMap length
  47108. if (pointArrayMapLength) {
  47109. let len = pointArrayMap.length;
  47110. while (len--) {
  47111. values.push([]);
  47112. }
  47113. }
  47114. else {
  47115. values.push([]);
  47116. }
  47117. const valuesLen = pointArrayMapLength || 1;
  47118. for (let i = 0; i <= dataLength; i++) {
  47119. // Start with the first point within the X axis range (#2696)
  47120. if (xData[i] < groupPositions[0]) {
  47121. continue; // with next point
  47122. }
  47123. // when a new group is entered, summarize and initialize
  47124. // the previous group
  47125. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  47126. xData[i] >= groupPositions[pos + 1]) ||
  47127. i === dataLength) { // get the last group
  47128. // get group x and y
  47129. pointX = groupPositions[pos];
  47130. series.dataGroupInfo = {
  47131. start: groupAll ? start : (series.cropStart + start),
  47132. length: values[0].length
  47133. };
  47134. groupedY = approximationFn.apply(series, values);
  47135. // By default, let options of the first grouped point be passed over
  47136. // to the grouped point. This allows preserving properties like
  47137. // `name` and `color` or custom properties. Implementers can
  47138. // override this from the approximation function, where they can
  47139. // write custom options to `this.dataGroupInfo.options`.
  47140. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  47141. // Convert numbers and arrays into objects
  47142. series.dataGroupInfo.options = merge(series.pointClass.prototype
  47143. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  47144. // Make sure the raw data (x, y, open, high etc) is not copied
  47145. // over and overwriting approximated data.
  47146. extendedPointArrayMap.forEach(function (key) {
  47147. delete series.dataGroupInfo.options[key];
  47148. });
  47149. }
  47150. // push the grouped data
  47151. if (typeof groupedY !== 'undefined') {
  47152. groupedXData.push(pointX);
  47153. groupedYData.push(groupedY);
  47154. groupMap.push(series.dataGroupInfo);
  47155. }
  47156. // reset the aggregate arrays
  47157. start = i;
  47158. for (let j = 0; j < valuesLen; j++) {
  47159. values[j].length = 0; // faster than values[j] = []
  47160. values[j].hasNulls = false;
  47161. }
  47162. // Advance on the group positions
  47163. pos += 1;
  47164. // don't loop beyond the last group
  47165. if (i === dataLength) {
  47166. break;
  47167. }
  47168. }
  47169. // break out
  47170. if (i === dataLength) {
  47171. break;
  47172. }
  47173. // for each raw data point, push it to an array that contains all values
  47174. // for this specific group
  47175. if (pointArrayMap) {
  47176. const index = (series.options.dataGrouping &&
  47177. series.options.dataGrouping.groupAll ?
  47178. i : series.cropStart + i), point = (data && data[index]) ||
  47179. series.pointClass.prototype.applyOptions.apply({
  47180. series: series
  47181. }, [dataOptions[index]]);
  47182. let val;
  47183. for (let j = 0; j < pointArrayMapLength; j++) {
  47184. val = point[pointArrayMap[j]];
  47185. if (isNumber(val)) {
  47186. values[j].push(val);
  47187. }
  47188. else if (val === null) {
  47189. values[j].hasNulls = true;
  47190. }
  47191. }
  47192. }
  47193. else {
  47194. pointY = handleYData ? yData[i] : null;
  47195. if (isNumber(pointY)) {
  47196. values[0].push(pointY);
  47197. }
  47198. else if (pointY === null) {
  47199. values[0].hasNulls = true;
  47200. }
  47201. }
  47202. }
  47203. return {
  47204. groupedXData,
  47205. groupedYData,
  47206. groupMap
  47207. };
  47208. }
  47209. /**
  47210. * Handle default options for data grouping. This must be set at runtime because
  47211. * some series types are defined after this.
  47212. * @private
  47213. */
  47214. function onAfterSetOptions(e) {
  47215. const options = e.options, type = this.type, plotOptions = this.chart.options.plotOptions,
  47216. // External series, for example technical indicators should also inherit
  47217. // commonOptions which are not available outside this module
  47218. baseOptions = (this.useCommonDataGrouping &&
  47219. DataGroupingDefaults.common), seriesSpecific = DataGroupingDefaults.seriesSpecific;
  47220. let defaultOptions = D.defaultOptions.plotOptions[type].dataGrouping;
  47221. if (plotOptions && (seriesSpecific[type] || baseOptions)) { // #1284
  47222. const rangeSelector = this.chart.rangeSelector;
  47223. if (!defaultOptions) {
  47224. defaultOptions = merge(DataGroupingDefaults.common, seriesSpecific[type]);
  47225. }
  47226. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  47227. // Set by the StockChart constructor:
  47228. plotOptions[type].dataGrouping, this.userOptions.dataGrouping, !options.isInternal &&
  47229. rangeSelector &&
  47230. isNumber(rangeSelector.selected) &&
  47231. rangeSelector.buttonOptions[rangeSelector.selected].dataGrouping);
  47232. }
  47233. }
  47234. /**
  47235. * @private
  47236. */
  47237. function skipDataGrouping(series, force) {
  47238. return !(series.isCartesian &&
  47239. !series.isDirty &&
  47240. !series.xAxis.isDirty &&
  47241. !series.yAxis.isDirty &&
  47242. !force);
  47243. }
  47244. /* *
  47245. *
  47246. * Default Export
  47247. *
  47248. * */
  47249. const DataGroupingSeriesComposition = {
  47250. compose,
  47251. groupData
  47252. };
  47253. return DataGroupingSeriesComposition;
  47254. });
  47255. _registerModule(_modules, 'Extensions/DataGrouping/DataGrouping.js', [_modules['Extensions/DataGrouping/DataGroupingAxisComposition.js'], _modules['Extensions/DataGrouping/DataGroupingDefaults.js'], _modules['Extensions/DataGrouping/DataGroupingSeriesComposition.js'], _modules['Core/Templating.js'], _modules['Core/Utilities.js']], function (DataGroupingAxisComposition, DataGroupingDefaults, DataGroupingSeriesComposition, F, U) {
  47256. /* *
  47257. *
  47258. * (c) 2010-2021 Torstein Honsi
  47259. *
  47260. * License: www.highcharts.com/license
  47261. *
  47262. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47263. *
  47264. * */
  47265. const { format } = F;
  47266. const { addEvent, extend, isNumber } = U;
  47267. /* *
  47268. *
  47269. * Constants
  47270. *
  47271. * */
  47272. const composedMembers = [];
  47273. /* *
  47274. *
  47275. * Functions
  47276. *
  47277. * */
  47278. /**
  47279. * @private
  47280. */
  47281. function compose(AxisClass, SeriesClass, TooltipClass) {
  47282. DataGroupingAxisComposition.compose(AxisClass);
  47283. DataGroupingSeriesComposition.compose(SeriesClass);
  47284. if (TooltipClass &&
  47285. U.pushUnique(composedMembers, TooltipClass)) {
  47286. addEvent(TooltipClass, 'headerFormatter', onTooltipHeaderFormatter);
  47287. }
  47288. }
  47289. /**
  47290. * Extend the original method, make the tooltip's header reflect the grouped
  47291. * range.
  47292. * @private
  47293. */
  47294. function onTooltipHeaderFormatter(e) {
  47295. const chart = this.chart, time = chart.time, labelConfig = e.labelConfig, series = labelConfig.series, options = series.options, tooltipOptions = series.tooltipOptions, dataGroupingOptions = options.dataGrouping, xAxis = series.xAxis;
  47296. let xDateFormat = tooltipOptions.xDateFormat, xDateFormatEnd, currentDataGrouping, dateTimeLabelFormats, labelFormats, formattedKey, formatString = tooltipOptions[e.isFooter ? 'footerFormat' : 'headerFormat'];
  47297. // apply only to grouped series
  47298. if (xAxis &&
  47299. xAxis.options.type === 'datetime' &&
  47300. dataGroupingOptions &&
  47301. isNumber(labelConfig.key)) {
  47302. // set variables
  47303. currentDataGrouping = series.currentDataGrouping;
  47304. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  47305. // Fallback to commonOptions (#9693)
  47306. DataGroupingDefaults.common.dateTimeLabelFormats;
  47307. // if we have grouped data, use the grouping information to get the
  47308. // right format
  47309. if (currentDataGrouping) {
  47310. labelFormats =
  47311. dateTimeLabelFormats[currentDataGrouping.unitName];
  47312. if (currentDataGrouping.count === 1) {
  47313. xDateFormat = labelFormats[0];
  47314. }
  47315. else {
  47316. xDateFormat = labelFormats[1];
  47317. xDateFormatEnd = labelFormats[2];
  47318. }
  47319. // if not grouped, and we don't have set the xDateFormat option, get the
  47320. // best fit, so if the least distance between points is one minute, show
  47321. // it, but if the least distance is one day, skip hours and minutes etc.
  47322. }
  47323. else if (!xDateFormat && dateTimeLabelFormats && xAxis.dateTime) {
  47324. xDateFormat = xAxis.dateTime.getXDateFormat(labelConfig.x, tooltipOptions.dateTimeLabelFormats);
  47325. }
  47326. // now format the key
  47327. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  47328. if (xDateFormatEnd) {
  47329. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  47330. }
  47331. // Replace default header style with class name
  47332. if (series.chart.styledMode) {
  47333. formatString = this.styledModeFormat(formatString);
  47334. }
  47335. // return the replaced format
  47336. e.text = format(formatString, {
  47337. point: extend(labelConfig.point, { key: formattedKey }),
  47338. series: series
  47339. }, chart);
  47340. e.preventDefault();
  47341. }
  47342. }
  47343. /* *
  47344. *
  47345. * Default Export
  47346. *
  47347. * */
  47348. const DataGroupingComposition = {
  47349. compose,
  47350. groupData: DataGroupingSeriesComposition.groupData
  47351. };
  47352. /* *
  47353. *
  47354. * API Declarations
  47355. *
  47356. * */
  47357. /**
  47358. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  47359. */
  47360. /**
  47361. * The position of the point inside the group.
  47362. *
  47363. * @typedef {"start"|"middle"|"end"} Highcharts.DataGroupingAnchor
  47364. */
  47365. /**
  47366. * The position of the first or last point in the series inside the group.
  47367. *
  47368. * @typedef {"start"|"middle"|"end"|"firstPoint"|"lastPoint"} Highcharts.DataGroupingAnchorExtremes
  47369. */
  47370. /**
  47371. * Highcharts Stock only.
  47372. *
  47373. * @product highstock
  47374. * @interface Highcharts.DataGroupingInfoObject
  47375. */ /**
  47376. * @name Highcharts.DataGroupingInfoObject#length
  47377. * @type {number}
  47378. */ /**
  47379. * @name Highcharts.DataGroupingInfoObject#options
  47380. * @type {Highcharts.SeriesOptionsType|undefined}
  47381. */ /**
  47382. * @name Highcharts.DataGroupingInfoObject#start
  47383. * @type {number}
  47384. */
  47385. /**
  47386. * Highcharts Stock only.
  47387. *
  47388. * @product highstock
  47389. * @interface Highcharts.DataGroupingResultObject
  47390. */ /**
  47391. * @name Highcharts.DataGroupingResultObject#groupedXData
  47392. * @type {Array<number>}
  47393. */ /**
  47394. * @name Highcharts.DataGroupingResultObject#groupedYData
  47395. * @type {Array<(number|null|undefined)>|Array<Array<(number|null|undefined)>>}
  47396. */ /**
  47397. * @name Highcharts.DataGroupingResultObject#groupMap
  47398. * @type {Array<DataGroupingInfoObject>}
  47399. */
  47400. /**
  47401. * Highcharts Stock only. If a point object is created by data
  47402. * grouping, it doesn't reflect actual points in the raw
  47403. * data. In this case, the `dataGroup` property holds
  47404. * information that points back to the raw data.
  47405. *
  47406. * - `dataGroup.start` is the index of the first raw data
  47407. * point in the group.
  47408. *
  47409. * - `dataGroup.length` is the amount of points in the
  47410. * group.
  47411. *
  47412. * @sample stock/members/point-datagroup
  47413. * Click to inspect raw data points
  47414. *
  47415. * @product highstock
  47416. *
  47417. * @name Highcharts.Point#dataGroup
  47418. * @type {Highcharts.DataGroupingInfoObject|undefined}
  47419. */
  47420. (''); // detach doclets above
  47421. /* *
  47422. *
  47423. * API Options
  47424. *
  47425. * */
  47426. /**
  47427. * Data grouping is the concept of sampling the data values into larger
  47428. * blocks in order to ease readability and increase performance of the
  47429. * JavaScript charts. Highcharts Stock by default applies data grouping when
  47430. * the points become closer than a certain pixel value, determined by
  47431. * the `groupPixelWidth` option.
  47432. *
  47433. * If data grouping is applied, the grouping information of grouped
  47434. * points can be read from the [Point.dataGroup](
  47435. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  47436. * the data itself are set, for example `name` or `color` or custom properties,
  47437. * the grouping logic doesn't know how to group it. In this case the options of
  47438. * the first point instance are copied over to the group point. This can be
  47439. * altered through a custom `approximation` callback function.
  47440. *
  47441. * @declare Highcharts.DataGroupingOptionsObject
  47442. * @product highstock
  47443. * @requires product:highstock
  47444. * @requires module:modules/datagrouping
  47445. * @apioption plotOptions.series.dataGrouping
  47446. */
  47447. /**
  47448. * Specifies how the points should be located on the X axis inside the group.
  47449. * Points that are extremes can be set separately. Available options:
  47450. *
  47451. * - `start` places the point at the beginning of the group
  47452. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  47453. *
  47454. * - `middle` places the point in the middle of the group
  47455. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  47456. *
  47457. * - `end` places the point at the end of the group
  47458. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  47459. *
  47460. * @sample {highstock} stock/plotoptions/series-datagrouping-anchor
  47461. * Changing the point x-coordinate inside the group.
  47462. *
  47463. * @see [dataGrouping.firstAnchor](#plotOptions.series.dataGrouping.firstAnchor)
  47464. * @see [dataGrouping.lastAnchor](#plotOptions.series.dataGrouping.lastAnchor)
  47465. *
  47466. * @type {Highcharts.DataGroupingAnchor}
  47467. * @since 9.1.0
  47468. * @default start
  47469. * @apioption plotOptions.series.dataGrouping.anchor
  47470. */
  47471. /**
  47472. * The method of approximation inside a group. When for example 30 days
  47473. * are grouped into one month, this determines what value should represent
  47474. * the group. Possible values are "average", "averages", "open", "high",
  47475. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  47476. * is "ohlc" by default, which finds the open, high, low and close values
  47477. * within all the grouped data. For ranges, the approximation is "range",
  47478. * which finds the low and high values. For multi-dimensional data,
  47479. * like ranges and OHLC, "averages" will compute the average for each
  47480. * dimension.
  47481. *
  47482. * Custom aggregate methods can be added by assigning a callback function
  47483. * as the approximation. This function takes a numeric array as the
  47484. * argument and should return a single numeric value or `null`. Note
  47485. * that the numeric array will never contain null values, only true
  47486. * numbers. Instead, if null values are present in the raw data, the
  47487. * numeric array will have an `.hasNulls` property set to `true`. For
  47488. * single-value data sets the data is available in the first argument
  47489. * of the callback function. For OHLC data sets, all the open values
  47490. * are in the first argument, all high values in the second etc.
  47491. *
  47492. * Since v4.2.7, grouping meta data is available in the approximation
  47493. * callback from `this.dataGroupInfo`. It can be used to extract information
  47494. * from the raw data.
  47495. *
  47496. * Defaults to `average` for line-type series, `sum` for columns, `range`
  47497. * for range series, `hlc` for HLC, and `ohlc` for OHLC and candlestick.
  47498. *
  47499. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  47500. * Approximation callback with custom data
  47501. * @sample {highstock} stock/plotoptions/series-datagrouping-simple-approximation
  47502. * Simple approximation demo
  47503. *
  47504. * @type {Highcharts.DataGroupingApproximationValue|Function}
  47505. * @apioption plotOptions.series.dataGrouping.approximation
  47506. */
  47507. /**
  47508. * Datetime formats for the header of the tooltip in a stock chart.
  47509. * The format can vary within a chart depending on the currently selected
  47510. * time range and the current data grouping.
  47511. *
  47512. * The default formats are:
  47513. * ```js
  47514. * {
  47515. * millisecond: [
  47516. * '%A, %e %b, %H:%M:%S.%L', '%A, %e %b, %H:%M:%S.%L', '-%H:%M:%S.%L'
  47517. * ],
  47518. * second: ['%A, %e %b, %H:%M:%S', '%A, %e %b, %H:%M:%S', '-%H:%M:%S'],
  47519. * minute: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
  47520. * hour: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
  47521. * day: ['%A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
  47522. * week: ['Week from %A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
  47523. * month: ['%B %Y', '%B', '-%B %Y'],
  47524. * year: ['%Y', '%Y', '-%Y']
  47525. * }
  47526. * ```
  47527. *
  47528. * For each of these array definitions, the first item is the format
  47529. * used when the active time span is one unit. For instance, if the
  47530. * current data applies to one week, the first item of the week array
  47531. * is used. The second and third items are used when the active time
  47532. * span is more than two units. For instance, if the current data applies
  47533. * to two weeks, the second and third item of the week array are used,
  47534. * and applied to the start and end date of the time span.
  47535. *
  47536. * @type {Object}
  47537. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  47538. */
  47539. /**
  47540. * Enable or disable data grouping.
  47541. *
  47542. * @type {boolean}
  47543. * @default true
  47544. * @apioption plotOptions.series.dataGrouping.enabled
  47545. */
  47546. /**
  47547. * Specifies how the first grouped point is positioned on the xAxis.
  47548. * If firstAnchor and/or lastAnchor are defined, then those options take
  47549. * precedence over anchor for the first and/or last grouped points.
  47550. * Available options:
  47551. *
  47552. * -`start` places the point at the beginning of the group
  47553. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  47554. *
  47555. * -`middle` places the point in the middle of the group
  47556. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  47557. *
  47558. * -`end` places the point at the end of the group
  47559. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  47560. *
  47561. * -`firstPoint` the first point in the group
  47562. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  47563. *
  47564. * -`lastPoint` the last point in the group
  47565. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  47566. *
  47567. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  47568. * Applying first and last anchor.
  47569. *
  47570. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  47571. *
  47572. * @type {Highcharts.DataGroupingAnchorExtremes}
  47573. * @since 9.1.0
  47574. * @default start
  47575. * @apioption plotOptions.series.dataGrouping.firstAnchor
  47576. */
  47577. /**
  47578. * When data grouping is forced, it runs no matter how small the intervals
  47579. * are. This can be handy for example when the sum should be calculated
  47580. * for values appearing at random times within each hour.
  47581. *
  47582. * @type {boolean}
  47583. * @default false
  47584. * @apioption plotOptions.series.dataGrouping.forced
  47585. */
  47586. /**
  47587. * The approximate pixel width of each group. If for example a series
  47588. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  47589. * is performed. If however the series contains so many points that
  47590. * the spacing is less than the groupPixelWidth, Highcharts will try
  47591. * to group it into appropriate groups so that each is more or less
  47592. * two pixels wide. If multiple series with different group pixel widths
  47593. * are drawn on the same x axis, all series will take the greatest width.
  47594. * For example, line series have 2px default group width, while column
  47595. * series have 10px. If combined, both the line and the column will
  47596. * have 10px by default.
  47597. *
  47598. * @type {number}
  47599. * @default 2
  47600. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  47601. */
  47602. /**
  47603. * By default only points within the visible range are grouped. Enabling this
  47604. * option will force data grouping to calculate all grouped points for a given
  47605. * dataset. That option prevents for example a column series from calculating
  47606. * a grouped point partially. The effect is similar to
  47607. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  47608. * not affect yAxis extremes.
  47609. *
  47610. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  47611. * Two series with the same data but different groupAll setting
  47612. *
  47613. * @type {boolean}
  47614. * @default false
  47615. * @since 6.1.0
  47616. * @apioption plotOptions.series.dataGrouping.groupAll
  47617. */
  47618. /**
  47619. * Specifies how the last grouped point is positioned on the xAxis.
  47620. * If firstAnchor and/or lastAnchor are defined, then those options take
  47621. * precedence over anchor for the first and/or last grouped points.
  47622. * Available options:
  47623. *
  47624. * -`start` places the point at the beginning of the group
  47625. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  47626. *
  47627. * -`middle` places the point in the middle of the group
  47628. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  47629. *
  47630. * -`end` places the point at the end of the group
  47631. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  47632. *
  47633. * -`firstPoint` the first point in the group
  47634. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  47635. *
  47636. * -`lastPoint` the last point in the group
  47637. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  47638. *
  47639. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  47640. * Applying first and last anchor.
  47641. *
  47642. * @sample {highstock} stock/plotoptions/series-datagrouping-last-anchor
  47643. * Applying the last anchor in the chart with live data.
  47644. *
  47645. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  47646. *
  47647. * @type {Highcharts.DataGroupingAnchorExtremes}
  47648. * @since 9.1.0
  47649. * @default start
  47650. * @apioption plotOptions.series.dataGrouping.lastAnchor
  47651. */
  47652. /**
  47653. * Normally, a group is indexed by the start of that group, so for example
  47654. * when 30 daily values are grouped into one month, that month's x value
  47655. * will be the 1st of the month. This apparently shifts the data to
  47656. * the left. When the smoothed option is true, this is compensated for.
  47657. * The data is shifted to the middle of the group, and min and max
  47658. * values are preserved. Internally, this is used in the Navigator series.
  47659. *
  47660. * @type {boolean}
  47661. * @default false
  47662. * @deprecated
  47663. * @apioption plotOptions.series.dataGrouping.smoothed
  47664. */
  47665. /**
  47666. * An array determining what time intervals the data is allowed to be
  47667. * grouped to. Each array item is an array where the first value is
  47668. * the time unit and the second value another array of allowed multiples.
  47669. *
  47670. * Defaults to:
  47671. * ```js
  47672. * units: [[
  47673. * 'millisecond', // unit name
  47674. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  47675. * ], [
  47676. * 'second',
  47677. * [1, 2, 5, 10, 15, 30]
  47678. * ], [
  47679. * 'minute',
  47680. * [1, 2, 5, 10, 15, 30]
  47681. * ], [
  47682. * 'hour',
  47683. * [1, 2, 3, 4, 6, 8, 12]
  47684. * ], [
  47685. * 'day',
  47686. * [1]
  47687. * ], [
  47688. * 'week',
  47689. * [1]
  47690. * ], [
  47691. * 'month',
  47692. * [1, 3, 6]
  47693. * ], [
  47694. * 'year',
  47695. * null
  47696. * ]]
  47697. * ```
  47698. *
  47699. * @type {Array<Array<string,(Array<number>|null)>>}
  47700. * @apioption plotOptions.series.dataGrouping.units
  47701. */
  47702. /**
  47703. * The approximate pixel width of each group. If for example a series
  47704. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  47705. * is performed. If however the series contains so many points that
  47706. * the spacing is less than the groupPixelWidth, Highcharts will try
  47707. * to group it into appropriate groups so that each is more or less
  47708. * two pixels wide. Defaults to `10`.
  47709. *
  47710. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  47711. * Two series with the same data density but different groupPixelWidth
  47712. *
  47713. * @type {number}
  47714. * @default 10
  47715. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  47716. */
  47717. ''; // required by JSDoc parsing
  47718. return DataGroupingComposition;
  47719. });
  47720. _registerModule(_modules, 'masters/modules/datagrouping.src.js', [_modules['Core/Globals.js'], _modules['Extensions/DataGrouping/ApproximationDefaults.js'], _modules['Extensions/DataGrouping/ApproximationRegistry.js'], _modules['Extensions/DataGrouping/DataGrouping.js']], function (Highcharts, ApproximationDefaults, ApproximationRegistry, DataGrouping) {
  47721. const G = Highcharts;
  47722. G.dataGrouping = {
  47723. approximationDefaults: ApproximationDefaults,
  47724. approximations: ApproximationRegistry
  47725. };
  47726. DataGrouping.compose(G.Axis, G.Series, G.Tooltip);
  47727. });
  47728. _registerModule(_modules, 'Extensions/MouseWheelZoom/MouseWheelZoom.js', [_modules['Core/Utilities.js']], function (U) {
  47729. /* *
  47730. *
  47731. * (c) 2023 Torstein Honsi, Askel Eirik Johansson
  47732. *
  47733. * License: www.highcharts.com/license
  47734. *
  47735. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47736. *
  47737. * */
  47738. const { addEvent, isObject, pick, defined, merge } = U;
  47739. /* *
  47740. *
  47741. * Constants
  47742. *
  47743. * */
  47744. const composedClasses = [], defaultOptions = {
  47745. enabled: true,
  47746. sensitivity: 1.1
  47747. };
  47748. /* *
  47749. *
  47750. * Functions
  47751. *
  47752. * */
  47753. /**
  47754. * @private
  47755. */
  47756. const optionsToObject = (options) => {
  47757. if (!isObject(options)) {
  47758. return merge(defaultOptions, { enabled: defined(options) ? options : true });
  47759. }
  47760. return merge(defaultOptions, options);
  47761. };
  47762. /**
  47763. * @private
  47764. */
  47765. const fitToBox = function (inner, outer) {
  47766. if (inner.x + inner.width > outer.x + outer.width) {
  47767. if (inner.width > outer.width) {
  47768. inner.width = outer.width;
  47769. inner.x = outer.x;
  47770. }
  47771. else {
  47772. inner.x = outer.x + outer.width - inner.width;
  47773. }
  47774. }
  47775. if (inner.width > outer.width) {
  47776. inner.width = outer.width;
  47777. }
  47778. if (inner.x < outer.x) {
  47779. inner.x = outer.x;
  47780. }
  47781. // y and height
  47782. if (inner.y + inner.height > outer.y + outer.height) {
  47783. if (inner.height > outer.height) {
  47784. inner.height = outer.height;
  47785. inner.y = outer.y;
  47786. }
  47787. else {
  47788. inner.y = outer.y + outer.height - inner.height;
  47789. }
  47790. }
  47791. if (inner.height > outer.height) {
  47792. inner.height = outer.height;
  47793. }
  47794. if (inner.y < outer.y) {
  47795. inner.y = outer.y;
  47796. }
  47797. return inner;
  47798. };
  47799. let wheelTimer, originalOptions;
  47800. /**
  47801. * @private
  47802. */
  47803. const zoomBy = function (chart, howMuch, centerXArg, centerYArg, mouseX, mouseY, options) {
  47804. const xAxis = chart.xAxis[0], yAxis = chart.yAxis[0], type = pick(options.type, chart.options.chart.zooming.type, 'x'), zoomX = /x/.test(type), zoomY = /y/.test(type);
  47805. if (defined(xAxis.max) && defined(xAxis.min) &&
  47806. defined(yAxis.max) && defined(yAxis.min) &&
  47807. defined(xAxis.dataMax) && defined(xAxis.dataMin) &&
  47808. defined(yAxis.dataMax) && defined(yAxis.dataMin)) {
  47809. if (zoomY) {
  47810. // Options interfering with yAxis zoom by setExtremes() returning
  47811. // integers by default.
  47812. if (defined(wheelTimer)) {
  47813. clearTimeout(wheelTimer);
  47814. }
  47815. const { startOnTick, endOnTick } = yAxis.options;
  47816. if (!originalOptions) {
  47817. originalOptions = { startOnTick, endOnTick };
  47818. }
  47819. if (startOnTick || endOnTick) {
  47820. yAxis.setOptions({ startOnTick: false, endOnTick: false });
  47821. }
  47822. wheelTimer = setTimeout(() => {
  47823. if (originalOptions) {
  47824. yAxis.setOptions(originalOptions);
  47825. // Set the extremes to the same as they already are, but now
  47826. // with the original startOnTick and endOnTick. We need
  47827. // `forceRedraw` otherwise it will detect that the values
  47828. // haven't changed. We do not use a simple yAxis.update()
  47829. // because it will destroy the ticks and prevent animation.
  47830. const { min, max } = yAxis.getExtremes();
  47831. yAxis.forceRedraw = true;
  47832. yAxis.setExtremes(min, max);
  47833. originalOptions = void 0;
  47834. }
  47835. }, 400);
  47836. }
  47837. if (chart.inverted) {
  47838. const emulateRoof = yAxis.pos + yAxis.len;
  47839. // Get the correct values
  47840. centerXArg = xAxis.toValue(mouseY);
  47841. centerYArg = yAxis.toValue(mouseX);
  47842. // Swapping x and y for simplicity when chart is inverted.
  47843. const tmp = mouseX;
  47844. mouseX = mouseY;
  47845. mouseY = emulateRoof - tmp + yAxis.pos;
  47846. }
  47847. let fixToX = mouseX ? ((mouseX - xAxis.pos) / xAxis.len) : 0.5;
  47848. if (xAxis.reversed && !chart.inverted ||
  47849. chart.inverted && !xAxis.reversed) {
  47850. // We are taking into account that xAxis automatically gets
  47851. // reversed when chart.inverted
  47852. fixToX = 1 - fixToX;
  47853. }
  47854. let fixToY = 1 - (mouseY ? ((mouseY - yAxis.pos) / yAxis.len) : 0.5);
  47855. if (yAxis.reversed) {
  47856. fixToY = 1 - fixToY;
  47857. }
  47858. const xRange = xAxis.max - xAxis.min, centerX = pick(centerXArg, xAxis.min + xRange / 2), newXRange = xRange * howMuch, yRange = yAxis.max - yAxis.min, centerY = pick(centerYArg, yAxis.min + yRange / 2), newYRange = yRange * howMuch, newXMin = centerX - newXRange * fixToX, newYMin = centerY - newYRange * fixToY, dataRangeX = xAxis.dataMax - xAxis.dataMin, dataRangeY = yAxis.dataMax - yAxis.dataMin, outerX = xAxis.dataMin - dataRangeX * xAxis.options.minPadding, outerWidth = dataRangeX + dataRangeX * xAxis.options.minPadding +
  47859. dataRangeX * xAxis.options.maxPadding, outerY = yAxis.dataMin - dataRangeY * yAxis.options.minPadding, outerHeight = dataRangeY + dataRangeY * yAxis.options.minPadding +
  47860. dataRangeY * yAxis.options.maxPadding, newExt = fitToBox({
  47861. x: newXMin,
  47862. y: newYMin,
  47863. width: newXRange,
  47864. height: newYRange
  47865. }, {
  47866. x: outerX,
  47867. y: outerY,
  47868. width: outerWidth,
  47869. height: outerHeight
  47870. }), zoomOut = (newExt.x <= outerX &&
  47871. newExt.width >=
  47872. outerWidth &&
  47873. newExt.y <= outerY &&
  47874. newExt.height >= outerHeight);
  47875. // Zoom
  47876. if (defined(howMuch) && !zoomOut) {
  47877. if (zoomX) {
  47878. xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false);
  47879. }
  47880. if (zoomY) {
  47881. yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false);
  47882. }
  47883. // Reset zoom
  47884. }
  47885. else {
  47886. if (zoomX) {
  47887. xAxis.setExtremes(void 0, void 0, false);
  47888. }
  47889. if (zoomY) {
  47890. yAxis.setExtremes(void 0, void 0, false);
  47891. }
  47892. }
  47893. chart.redraw(false);
  47894. }
  47895. };
  47896. /**
  47897. * @private
  47898. */
  47899. function onAfterGetContainer() {
  47900. const chart = this, wheelZoomOptions = optionsToObject(chart.options.chart.zooming.mouseWheel);
  47901. if (wheelZoomOptions.enabled) {
  47902. addEvent(this.container, 'wheel', (e) => {
  47903. e = this.pointer.normalize(e);
  47904. // Firefox uses e.detail, WebKit and IE uses deltaX, deltaY, deltaZ.
  47905. if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
  47906. const wheelSensitivity = pick(wheelZoomOptions.sensitivity, 1.1), delta = e.detail || ((e.deltaY || 0) / 120);
  47907. zoomBy(chart, Math.pow(wheelSensitivity, delta), chart.xAxis[0].toValue(e.chartX), chart.yAxis[0].toValue(e.chartY), e.chartX, e.chartY, wheelZoomOptions);
  47908. }
  47909. // prevent page scroll
  47910. if (e.preventDefault) {
  47911. e.preventDefault();
  47912. }
  47913. });
  47914. }
  47915. }
  47916. /**
  47917. * @private
  47918. */
  47919. function compose(ChartClass) {
  47920. if (composedClasses.indexOf(ChartClass) === -1) {
  47921. composedClasses.push(ChartClass);
  47922. addEvent(ChartClass, 'afterGetContainer', onAfterGetContainer);
  47923. }
  47924. }
  47925. /* *
  47926. *
  47927. * Default Export
  47928. *
  47929. * */
  47930. const MouseWheelZoomComposition = {
  47931. compose
  47932. };
  47933. /* *
  47934. *
  47935. * API Options
  47936. *
  47937. * */
  47938. /**
  47939. * The mouse wheel zoom is a feature included in Highcharts Stock, but is
  47940. * also available for Highcharts Core as a module. Zooming with the mouse wheel
  47941. * is enabled by default. It can be disabled by setting this option to
  47942. * `false`.
  47943. *
  47944. * @type {boolean|object}
  47945. * @since 11.1.0
  47946. * @requires modules/mouse-wheel-zoom
  47947. * @sample {highcharts} highcharts/mouse-wheel-zoom/enabled
  47948. * Enable or disable
  47949. * @sample {highstock} stock/mouse-wheel-zoom/enabled
  47950. * Enable or disable
  47951. * @apioption chart.zooming.mouseWheel
  47952. */
  47953. /**
  47954. * Zooming with the mouse wheel can be disabled by setting this option to
  47955. * `false`.
  47956. *
  47957. * @type {boolean}
  47958. * @default true
  47959. * @since 11.1.0
  47960. * @requires modules/mouse-wheel-zoom
  47961. * @apioption chart.zooming.mouseWheel.enabled
  47962. */
  47963. /**
  47964. * Adjust the sensitivity of the zoom. Sensitivity of mouse wheel or trackpad
  47965. * scrolling. `1` is no sensitivity, while with `2`, one mouse wheel delta will
  47966. * zoom in `50%`.
  47967. *
  47968. * @type {number}
  47969. * @default 1.1
  47970. * @since 11.1.0
  47971. * @requires modules/mouse-wheel-zoom
  47972. * @sample {highcharts} highcharts/mouse-wheel-zoom/sensitivity
  47973. * Change mouse wheel zoom sensitivity
  47974. * @sample {highstock} stock/mouse-wheel-zoom/sensitivity
  47975. * Change mouse wheel zoom sensitivity
  47976. * @apioption chart.zooming.mouseWheel.sensitivity
  47977. */
  47978. /**
  47979. * Decides in what dimensions the user can zoom scrolling the wheel.
  47980. * Can be one of `x`, `y` or `xy`. If not specified here, it will inherit the
  47981. * type from [chart.zooming.type](chart.zooming.type).
  47982. *
  47983. * Note that particularly with mouse wheel in the y direction, the zoom is
  47984. * affected by the default [yAxis.startOnTick](#yAxis.startOnTick) and
  47985. * [endOnTick]((#yAxis.endOnTick)) settings. In order to respect these settings,
  47986. * the zoom level will adjust after the user has stopped zooming. To prevent
  47987. * this, consider setting `startOnTick` and `endOnTick` to `false`.
  47988. *
  47989. * @type {string}
  47990. * @default x
  47991. * @validvalue ["x", "y", "xy"]
  47992. * @since 11.1.0
  47993. * @requires modules/mouse-wheel-zoom
  47994. * @apioption chart.zooming.mouseWheel.type
  47995. */
  47996. (''); // Keeps doclets above in JS file
  47997. return MouseWheelZoomComposition;
  47998. });
  47999. _registerModule(_modules, 'masters/modules/mouse-wheel-zoom.src.js', [_modules['Core/Globals.js'], _modules['Extensions/MouseWheelZoom/MouseWheelZoom.js']], function (Highcharts, MouseWheelZoom) {
  48000. const G = Highcharts;
  48001. MouseWheelZoom.compose(G.Chart);
  48002. });
  48003. _registerModule(_modules, 'Series/DataModifyComposition.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (Axis, Point, Series, U) {
  48004. /* *
  48005. *
  48006. * (c) 2010-2021 Torstein Honsi
  48007. *
  48008. * License: www.highcharts.com/license
  48009. *
  48010. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48011. *
  48012. * */
  48013. const { prototype: { tooltipFormatter: pointTooltipFormatter } } = Point;
  48014. const { addEvent, arrayMax, arrayMin, correctFloat, defined, isArray, isNumber, isString, pick } = U;
  48015. /* *
  48016. *
  48017. * Composition
  48018. *
  48019. * */
  48020. var DataModifyComposition;
  48021. (function (DataModifyComposition) {
  48022. /* *
  48023. *
  48024. * Declarations
  48025. *
  48026. * */
  48027. /* *
  48028. *
  48029. * Constants
  48030. *
  48031. * */
  48032. const composedMembers = [];
  48033. /* *
  48034. *
  48035. * Functions
  48036. *
  48037. * */
  48038. /* eslint-disable valid-jsdoc */
  48039. /**
  48040. * Extends the series, axis and point classes with
  48041. * compare and cumulative support.
  48042. *
  48043. * @private
  48044. *
  48045. * @param SeriesClass
  48046. * Series class to use.
  48047. *
  48048. * @param AxisClass
  48049. * Axis class to extend.
  48050. *
  48051. * @param PointClass
  48052. * Point class to use.
  48053. */
  48054. function compose(SeriesClass, AxisClass, PointClass) {
  48055. if (U.pushUnique(composedMembers, SeriesClass)) {
  48056. const seriesProto = SeriesClass.prototype;
  48057. seriesProto.setCompare = seriesSetCompare;
  48058. seriesProto.setCumulative = seriesSetCumulative;
  48059. addEvent(SeriesClass, 'afterInit', afterInit);
  48060. addEvent(SeriesClass, 'afterGetExtremes', afterGetExtremes);
  48061. addEvent(SeriesClass, 'afterProcessData', afterProcessData);
  48062. }
  48063. if (U.pushUnique(composedMembers, AxisClass)) {
  48064. const axisProto = AxisClass.prototype;
  48065. axisProto.setCompare = axisSetCompare;
  48066. axisProto.setModifier = setModifier;
  48067. axisProto.setCumulative = axisSetCumulative;
  48068. }
  48069. if (U.pushUnique(composedMembers, PointClass)) {
  48070. const pointProto = PointClass.prototype;
  48071. pointProto.tooltipFormatter = tooltipFormatter;
  48072. }
  48073. return SeriesClass;
  48074. }
  48075. DataModifyComposition.compose = compose;
  48076. /* ********************************************************************** *
  48077. * Start shared compare and cumulative logic *
  48078. * ********************************************************************** */
  48079. /**
  48080. * Shared code for the axis.setCompare() and the axis.setCumulative()
  48081. * methods. Inits the 'compare' or the 'cumulative' mode.
  48082. * @private
  48083. */
  48084. function setModifier(mode, modeState, redraw) {
  48085. if (!this.isXAxis) {
  48086. this.series.forEach(function (series) {
  48087. if (mode === 'compare' &&
  48088. typeof modeState !== 'boolean') {
  48089. series.setCompare(modeState, false);
  48090. }
  48091. else if (mode === 'cumulative' &&
  48092. !isString(modeState)) {
  48093. series.setCumulative(modeState, false);
  48094. }
  48095. });
  48096. if (pick(redraw, true)) {
  48097. this.chart.redraw();
  48098. }
  48099. }
  48100. }
  48101. /**
  48102. * Extend the tooltip formatter by adding support for the point.change
  48103. * variable as well as the changeDecimals option.
  48104. *
  48105. * @ignore
  48106. * @function Highcharts.Point#tooltipFormatter
  48107. *
  48108. * @param {string} pointFormat
  48109. */
  48110. function tooltipFormatter(pointFormat) {
  48111. const point = this, { numberFormatter } = point.series.chart, replace = function (value) {
  48112. pointFormat = pointFormat.replace('{point.' + value + '}', (point[value] > 0 && value === 'change' ? '+' : '') +
  48113. numberFormatter(point[value], pick(point.series.tooltipOptions.changeDecimals, 2)));
  48114. };
  48115. if (defined(point.change)) {
  48116. replace('change');
  48117. }
  48118. if (defined(point.cumulativeSum)) {
  48119. replace('cumulativeSum');
  48120. }
  48121. return pointTooltipFormatter.apply(this, [pointFormat]);
  48122. }
  48123. /**
  48124. * Extend series.init by adding a methods to modify the y values used
  48125. * for plotting on the y axis. For compare mode, this method is called both
  48126. * from the axis when finding dataMin and dataMax,
  48127. * and from the series.translate method.
  48128. *
  48129. * @ignore
  48130. * @function Highcharts.Series#init
  48131. */
  48132. function afterInit() {
  48133. const compare = this.options.compare;
  48134. let dataModify;
  48135. if (compare === 'percent' ||
  48136. compare === 'value' ||
  48137. this.options.cumulative) {
  48138. dataModify = new Additions(this);
  48139. if (compare === 'percent' || compare === 'value') {
  48140. // Set comparison mode
  48141. dataModify.initCompare(compare);
  48142. }
  48143. else {
  48144. // Set Cumulative Sum mode
  48145. dataModify.initCumulative();
  48146. }
  48147. }
  48148. this.dataModify = dataModify;
  48149. }
  48150. /**
  48151. * Adjust the extremes (compare and cumulative modify the data).
  48152. * @private
  48153. */
  48154. function afterGetExtremes(e) {
  48155. const dataExtremes = e.dataExtremes, activeYData = dataExtremes.activeYData;
  48156. if (this.dataModify && dataExtremes) {
  48157. let extremes;
  48158. if (this.options.compare) {
  48159. extremes = [
  48160. this.dataModify.modifyValue(dataExtremes.dataMin),
  48161. this.dataModify.modifyValue(dataExtremes.dataMax)
  48162. ];
  48163. }
  48164. else if (this.options.cumulative &&
  48165. isArray(activeYData) &&
  48166. // If only one y visible, sum doesn't change
  48167. // so no need to change extremes
  48168. activeYData.length >= 2) {
  48169. extremes = Additions.getCumulativeExtremes(activeYData);
  48170. }
  48171. if (extremes) {
  48172. dataExtremes.dataMin = arrayMin(extremes);
  48173. dataExtremes.dataMax = arrayMax(extremes);
  48174. }
  48175. }
  48176. }
  48177. /* ********************************************************************** *
  48178. * End shared compare and cumulative logic *
  48179. * ********************************************************************** */
  48180. /* ********************************************************************** *
  48181. * Start value compare logic *
  48182. * ********************************************************************** */
  48183. /**
  48184. * Highcharts Stock only. Set the
  48185. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  48186. * mode of the series after render time.
  48187. * In most cases it is more useful running
  48188. * {@link Axis#setCompare} on the X axis to update all its series.
  48189. *
  48190. * @function Highcharts.Series#setCompare
  48191. *
  48192. * @param {string|null} [compare]
  48193. * Can be one of `undefined` (default), `null`, `"percent"`
  48194. * or `"value"`.
  48195. *
  48196. * @param {boolean} [redraw=true]
  48197. * Whether to redraw the chart or to wait for a later call to
  48198. * {@link Chart#redraw}.
  48199. */
  48200. function seriesSetCompare(compare, redraw) {
  48201. // Survive to export, #5485 (and for options generally)
  48202. this.options.compare = this.userOptions.compare = compare;
  48203. // Fire series.init() that will set or delete series.dataModify
  48204. this.update({}, pick(redraw, true));
  48205. if (this.dataModify && (compare === 'value' || compare === 'percent')) {
  48206. this.dataModify.initCompare(compare);
  48207. }
  48208. else {
  48209. // When disabling, clear the points
  48210. this.points.forEach((point) => {
  48211. delete point.change;
  48212. });
  48213. }
  48214. }
  48215. /**
  48216. * Extend series.processData by finding the first y value in the plot area,
  48217. * used for comparing the following values
  48218. *
  48219. * @ignore
  48220. * @function Highcharts.Series#processData
  48221. */
  48222. function afterProcessData() {
  48223. const series = this;
  48224. if (series.xAxis && // not pies
  48225. series.processedYData &&
  48226. series.dataModify) {
  48227. const processedXData = series.processedXData, processedYData = series.processedYData, length = processedYData.length, compareStart = series.options.compareStart === true ? 0 : 1;
  48228. let keyIndex = -1, i;
  48229. // For series with more than one value (range, OHLC etc), compare
  48230. // against close or the pointValKey (#4922, #3112, #9854)
  48231. if (series.pointArrayMap) {
  48232. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  48233. }
  48234. // find the first value for comparison
  48235. for (i = 0; i < length - compareStart; i++) {
  48236. const compareValue = processedYData[i] && keyIndex > -1 ?
  48237. processedYData[i][keyIndex] : processedYData[i];
  48238. if (isNumber(compareValue) &&
  48239. compareValue !== 0 &&
  48240. processedXData[i + compareStart] >= (series.xAxis.min || 0)) {
  48241. series.dataModify.compareValue = compareValue;
  48242. break;
  48243. }
  48244. }
  48245. }
  48246. }
  48247. /**
  48248. * Highcharts Stock only. Set the compare mode on all series
  48249. * belonging to a Y axis.
  48250. *
  48251. * @see [plotOptions.series.compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  48252. *
  48253. * @sample stock/members/axis-setcompare/
  48254. * Set compare
  48255. *
  48256. * @function Highcharts.Axis#setCompare
  48257. *
  48258. * @param {string|null} [compare]
  48259. * The compare mode. Can be one of `undefined` (default), `null`,
  48260. * `"value"` or `"percent"`.
  48261. *
  48262. * @param {boolean} [redraw=true]
  48263. * Whether to redraw the chart or to wait for a later call to
  48264. * {@link Chart#redraw}.
  48265. */
  48266. function axisSetCompare(compare, redraw) {
  48267. this.setModifier('compare', compare, redraw);
  48268. }
  48269. /* ********************************************************************** *
  48270. * End value compare logic *
  48271. * ********************************************************************** */
  48272. /* ********************************************************************** *
  48273. * Start Cumulative Sum logic, author: Rafal Sebestjanski *
  48274. * ********************************************************************** */
  48275. /**
  48276. * Highcharts Stock only. Set the
  48277. * [cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative)
  48278. * mode of the series after render time.
  48279. * In most cases it is more useful running
  48280. * {@link Axis#setCumulative} on the Y axis to update all its series.
  48281. *
  48282. * @function Highcharts.Series#setCumulative
  48283. *
  48284. * @param {boolean} [cumulative=false]
  48285. * Either enable or disable Cumulative Sum mode.
  48286. * Can be one of `false` (default) or `true`.
  48287. *
  48288. * @param {boolean} [redraw=true]
  48289. * Whether to redraw the chart or to wait for a later call to
  48290. * {@link Chart#redraw}.
  48291. */
  48292. function seriesSetCumulative(cumulative, redraw) {
  48293. // Set default value to false
  48294. cumulative = pick(cumulative, false);
  48295. // Survive to export, #5485 (and for options generally)
  48296. this.options.cumulative = this.userOptions.cumulative = cumulative;
  48297. // Fire series.init() that will set or delete series.dataModify
  48298. this.update({}, pick(redraw, true));
  48299. // If should, turn on the Cumulative Sum mode
  48300. if (this.dataModify) {
  48301. this.dataModify.initCumulative();
  48302. }
  48303. else {
  48304. // When disabling, clear the points
  48305. this.points.forEach((point) => {
  48306. delete point.cumulativeSum;
  48307. });
  48308. }
  48309. }
  48310. /**
  48311. * Highcharts Stock only. Set the cumulative mode on all series
  48312. * belonging to a Y axis.
  48313. *
  48314. * @see [plotOptions.series.cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative)
  48315. *
  48316. * @sample stock/members/axis-setcumulative/
  48317. * Set cumulative
  48318. *
  48319. * @function Highcharts.Axis#setCumulative
  48320. *
  48321. * @param {boolean} [cumulative]
  48322. * Whether to disable or enable the cumulative mode.
  48323. * Can be one of `undefined` (default, treated as `false`),
  48324. * `false` or `true`.
  48325. *
  48326. * @param {boolean} [redraw=true]
  48327. * Whether to redraw the chart or to wait for a later call to
  48328. * {@link Chart#redraw}.
  48329. */
  48330. function axisSetCumulative(cumulative, redraw) {
  48331. this.setModifier('cumulative', cumulative, redraw);
  48332. }
  48333. /* *
  48334. *
  48335. * Classes
  48336. *
  48337. * */
  48338. /**
  48339. * @private
  48340. */
  48341. class Additions {
  48342. /* *
  48343. *
  48344. * Constructors
  48345. *
  48346. * */
  48347. /**
  48348. * @private
  48349. */
  48350. constructor(series) {
  48351. this.series = series;
  48352. }
  48353. /* *
  48354. *
  48355. * Functions
  48356. *
  48357. * */
  48358. /**
  48359. * @private
  48360. */
  48361. modifyValue() {
  48362. return 0;
  48363. }
  48364. /**
  48365. * @ignore
  48366. * @function Highcharts.Series#getCumulativeExtremes
  48367. *
  48368. * @param {Array} [activeYData]
  48369. * An array cointaining all the points' y values
  48370. * in a visible range.
  48371. */
  48372. static getCumulativeExtremes(activeYData) {
  48373. let cumulativeDataMin = Infinity, cumulativeDataMax = -Infinity;
  48374. activeYData.reduce((prev, cur) => {
  48375. const sum = prev + cur;
  48376. cumulativeDataMin = Math.min(cumulativeDataMin, sum, prev);
  48377. cumulativeDataMax = Math.max(cumulativeDataMax, sum, prev);
  48378. return sum;
  48379. });
  48380. return [cumulativeDataMin, cumulativeDataMax];
  48381. }
  48382. /**
  48383. * @ignore
  48384. * @function Highcharts.Series#initCompare
  48385. *
  48386. * @param {string} [compare]
  48387. * Can be one of `"percent"` or `"value"`.
  48388. */
  48389. initCompare(compare) {
  48390. // Set the modifyValue method
  48391. this.modifyValue = function (value, index) {
  48392. if (value === null) {
  48393. value = 0;
  48394. }
  48395. const compareValue = this.compareValue;
  48396. if (typeof value !== 'undefined' &&
  48397. typeof compareValue !== 'undefined') { // #2601, #5814
  48398. // Get the modified value
  48399. if (compare === 'value') {
  48400. value -= compareValue;
  48401. // Compare percent
  48402. }
  48403. else {
  48404. const compareBase = this.series.options.compareBase;
  48405. value = 100 * (value / compareValue) -
  48406. (compareBase === 100 ? 0 : 100);
  48407. }
  48408. // record for tooltip etc.
  48409. if (typeof index !== 'undefined') {
  48410. const point = this.series.points[index];
  48411. if (point) {
  48412. point.change = value;
  48413. }
  48414. }
  48415. return value;
  48416. }
  48417. return 0;
  48418. };
  48419. }
  48420. /**
  48421. * @ignore
  48422. * @function Highcharts.Series#initCumulative
  48423. */
  48424. initCumulative() {
  48425. // Set the modifyValue method
  48426. this.modifyValue = function (value, index) {
  48427. if (value === null) {
  48428. value = 0;
  48429. }
  48430. if (value !== void 0 && index !== void 0) {
  48431. const prevPoint = index > 0 ?
  48432. this.series.points[index - 1] : null;
  48433. // Get the modified value
  48434. if (prevPoint && prevPoint.cumulativeSum) {
  48435. value = correctFloat(prevPoint.cumulativeSum + value);
  48436. }
  48437. // Record for tooltip etc.
  48438. const point = this.series.points[index];
  48439. if (point) {
  48440. point.cumulativeSum = value;
  48441. }
  48442. return value;
  48443. }
  48444. return 0;
  48445. };
  48446. }
  48447. }
  48448. DataModifyComposition.Additions = Additions;
  48449. })(DataModifyComposition || (DataModifyComposition = {}));
  48450. /* *
  48451. *
  48452. * Default Export
  48453. *
  48454. * */
  48455. /* *
  48456. *
  48457. * API Options
  48458. *
  48459. * */
  48460. /**
  48461. * Compare the values of the series against the first non-null, non-
  48462. * zero value in the visible range. The y axis will show percentage
  48463. * or absolute change depending on whether `compare` is set to `"percent"`
  48464. * or `"value"`. When this is applied to multiple series, it allows
  48465. * comparing the development of the series against each other. Adds
  48466. * a `change` field to every point object.
  48467. *
  48468. * @see [compareBase](#plotOptions.series.compareBase)
  48469. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  48470. * @see [Series.setCompare()](/class-reference/Highcharts.Series#setCompare)
  48471. *
  48472. * @sample {highstock} stock/plotoptions/series-compare-percent/
  48473. * Percent
  48474. * @sample {highstock} stock/plotoptions/series-compare-value/
  48475. * Value
  48476. *
  48477. * @type {string}
  48478. * @since 1.0.1
  48479. * @product highstock
  48480. * @validvalue ["percent", "value"]
  48481. * @apioption plotOptions.series.compare
  48482. */
  48483. /**
  48484. * Defines if comparison should start from the first point within the visible
  48485. * range or should start from the first point **before** the range.
  48486. *
  48487. * In other words, this flag determines if first point within the visible range
  48488. * will have 0% (`compareStart=true`) or should have been already calculated
  48489. * according to the previous point (`compareStart=false`).
  48490. *
  48491. * @sample {highstock} stock/plotoptions/series-comparestart/
  48492. * Calculate compare within visible range
  48493. *
  48494. * @type {boolean}
  48495. * @default false
  48496. * @since 6.0.0
  48497. * @product highstock
  48498. * @apioption plotOptions.series.compareStart
  48499. */
  48500. /**
  48501. * When [compare](#plotOptions.series.compare) is `percent`, this option
  48502. * dictates whether to use 0 or 100 as the base of comparison.
  48503. *
  48504. * @sample {highstock} stock/plotoptions/series-comparebase/
  48505. * Compare base is 100
  48506. *
  48507. * @type {number}
  48508. * @default 0
  48509. * @since 5.0.6
  48510. * @product highstock
  48511. * @validvalue [0, 100]
  48512. * @apioption plotOptions.series.compareBase
  48513. */
  48514. /**
  48515. * Cumulative Sum feature replaces points' values with the following formula:
  48516. * `sum of all previous points' values + current point's value`.
  48517. * Works only for points in a visible range.
  48518. * Adds the `cumulativeSum` field to each point object that can be accessed
  48519. * e.g. in the [tooltip.pointFormat](https://api.highcharts.com/highstock/tooltip.pointFormat).
  48520. *
  48521. * With `dataGrouping` enabled, default grouping approximation is set to `sum`.
  48522. *
  48523. * @see [Axis.setCumulative()](/class-reference/Highcharts.Axis#setCumulative)
  48524. * @see [Series.setCumulative()](/class-reference/Highcharts.Series#setCumulative)
  48525. *
  48526. * @sample {highstock} stock/plotoptions/series-cumulative-sum/
  48527. * Cumulative Sum
  48528. *
  48529. * @type {boolean}
  48530. * @default false
  48531. * @since 9.3.0
  48532. * @product highstock
  48533. * @apioption plotOptions.series.cumulative
  48534. */
  48535. ''; // keeps doclets above in transpiled file
  48536. return DataModifyComposition;
  48537. });
  48538. _registerModule(_modules, 'Core/Axis/NavigatorAxisComposition.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  48539. /* *
  48540. *
  48541. * (c) 2010-2021 Torstein Honsi
  48542. *
  48543. * License: www.highcharts.com/license
  48544. *
  48545. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48546. *
  48547. * */
  48548. const { isTouchDevice } = H;
  48549. const { addEvent, correctFloat, defined, isNumber, pick } = U;
  48550. /* *
  48551. *
  48552. * Constants
  48553. *
  48554. * */
  48555. const composedMembers = [];
  48556. /* *
  48557. *
  48558. * Functions
  48559. *
  48560. * */
  48561. /**
  48562. * @private
  48563. */
  48564. function onAxisInit() {
  48565. const axis = this;
  48566. if (!axis.navigatorAxis) {
  48567. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  48568. }
  48569. }
  48570. /**
  48571. * For Stock charts, override selection zooming with some special features
  48572. * because X axis zooming is already allowed by the Navigator and Range
  48573. * selector.
  48574. * @private
  48575. */
  48576. function onAxisZoom(e) {
  48577. console.log("onAxisZoom")
  48578. const axis = this, chart = axis.chart, chartOptions = chart.options, navigator = chartOptions.navigator, navigatorAxis = axis.navigatorAxis, pinchType = chart.zooming.pinchType, rangeSelector = chartOptions.rangeSelector, zoomType = chart.zooming.type;
  48579. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  48580. (rangeSelector && rangeSelector.enabled))) {
  48581. // For y only zooming, ignore the X axis completely
  48582. if (zoomType === 'y') {
  48583. e.zoomed = false;
  48584. // For xy zooming, record the state of the zoom before zoom
  48585. // selection, then when the reset button is pressed, revert to
  48586. // this state. This should apply only if the chart is
  48587. // initialized with a range (#6612), otherwise zoom all the way
  48588. // out.
  48589. }
  48590. else if (((!isTouchDevice && zoomType === 'xy') ||
  48591. (isTouchDevice && pinchType === 'xy')) &&
  48592. axis.options.range) {
  48593. const previousZoom = navigatorAxis.previousZoom;
  48594. if (defined(e.newMin)) {
  48595. navigatorAxis.previousZoom = [axis.min, axis.max];
  48596. }
  48597. else if (previousZoom) {
  48598. e.newMin = previousZoom[0];
  48599. e.newMax = previousZoom[1];
  48600. navigatorAxis.previousZoom = void 0;
  48601. }
  48602. }
  48603. }
  48604. if (typeof e.zoomed !== 'undefined') {
  48605. e.preventDefault();
  48606. }
  48607. }
  48608. /* *
  48609. *
  48610. * Class
  48611. *
  48612. * */
  48613. /**
  48614. * @private
  48615. * @class
  48616. */
  48617. class NavigatorAxisAdditions {
  48618. /* *
  48619. *
  48620. * Static Functions
  48621. *
  48622. * */
  48623. /**
  48624. * @private
  48625. */
  48626. static compose(AxisClass) {
  48627. if (U.pushUnique(composedMembers, AxisClass)) {
  48628. AxisClass.keepProps.push('navigatorAxis');
  48629. addEvent(AxisClass, 'init', onAxisInit);
  48630. addEvent(AxisClass, 'zoom', onAxisZoom);
  48631. }
  48632. }
  48633. /* *
  48634. *
  48635. * Constructors
  48636. *
  48637. * */
  48638. constructor(axis) {
  48639. this.axis = axis;
  48640. }
  48641. /* *
  48642. *
  48643. * Functions
  48644. *
  48645. * */
  48646. /**
  48647. * @private
  48648. */
  48649. destroy() {
  48650. this.axis = void 0;
  48651. }
  48652. /**
  48653. * Add logic to normalize the zoomed range in order to preserve the pressed
  48654. * state of range selector buttons
  48655. *
  48656. * @private
  48657. * @function Highcharts.Axis#toFixedRange
  48658. */
  48659. toFixedRange(pxMin, pxMax, fixedMin, fixedMax) {
  48660. const axis = this.axis, chart = axis.chart;
  48661. let newMin = pick(fixedMin, axis.translate(pxMin, true, !axis.horiz)), newMax = pick(fixedMax, axis.translate(pxMax, true, !axis.horiz));
  48662. const fixedRange = chart && chart.fixedRange, halfPointRange = (axis.pointRange || 0) / 2;
  48663. // Add/remove half point range to/from the extremes (#1172)
  48664. if (!defined(fixedMin)) {
  48665. newMin = correctFloat(newMin + halfPointRange);
  48666. }
  48667. if (!defined(fixedMax)) {
  48668. newMax = correctFloat(newMax - halfPointRange);
  48669. }
  48670. // Make sure panning to the edges does not decrease the zoomed range
  48671. if (fixedRange && axis.dataMin && axis.dataMax) {
  48672. if (newMax >= axis.dataMax) {
  48673. newMin = correctFloat(axis.dataMax - fixedRange);
  48674. }
  48675. if (newMin <= axis.dataMin) {
  48676. newMax = correctFloat(axis.dataMin + fixedRange);
  48677. }
  48678. }
  48679. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  48680. newMin = newMax = void 0;
  48681. }
  48682. return {
  48683. min: newMin,
  48684. max: newMax
  48685. };
  48686. }
  48687. }
  48688. /* *
  48689. *
  48690. * Default Export
  48691. *
  48692. * */
  48693. return NavigatorAxisAdditions;
  48694. });
  48695. _registerModule(_modules, 'Stock/Navigator/NavigatorDefaults.js', [_modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js']], function (Color, SeriesRegistry) {
  48696. /* *
  48697. *
  48698. * (c) 2010-2021 Torstein Honsi
  48699. *
  48700. * License: www.highcharts.com/license
  48701. *
  48702. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48703. *
  48704. * */
  48705. const { parse: color } = Color;
  48706. const { seriesTypes } = SeriesRegistry;
  48707. /* *
  48708. *
  48709. * Constants
  48710. *
  48711. * */
  48712. /**
  48713. * The navigator is a small series below the main series, displaying
  48714. * a view of the entire data set. It provides tools to zoom in and
  48715. * out on parts of the data as well as panning across the dataset.
  48716. *
  48717. * @product highstock gantt
  48718. * @optionparent navigator
  48719. */
  48720. const NavigatorDefaults = {
  48721. /**
  48722. * Whether the navigator and scrollbar should adapt to updated data
  48723. * in the base X axis. When loading data async, as in the demo below,
  48724. * this should be `false`. Otherwise new data will trigger navigator
  48725. * redraw, which will cause unwanted looping. In the demo below, the
  48726. * data in the navigator is set only once. On navigating, only the main
  48727. * chart content is updated.
  48728. *
  48729. * @sample {highstock} stock/demo/lazy-loading/
  48730. * Set to false with async data loading
  48731. *
  48732. * @type {boolean}
  48733. * @default true
  48734. * @apioption navigator.adaptToUpdatedData
  48735. */
  48736. /**
  48737. * An integer identifying the index to use for the base series, or a
  48738. * string representing the id of the series.
  48739. *
  48740. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  48741. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  48742. *
  48743. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  48744. *
  48745. * @deprecated
  48746. * @type {number|string}
  48747. * @default 0
  48748. * @apioption navigator.baseSeries
  48749. */
  48750. /**
  48751. * Enable or disable the navigator.
  48752. *
  48753. * @sample {highstock} stock/navigator/enabled/
  48754. * Disable the navigator
  48755. *
  48756. * @type {boolean}
  48757. * @default true
  48758. * @apioption navigator.enabled
  48759. */
  48760. /**
  48761. * When the chart is inverted, whether to draw the navigator on the
  48762. * opposite side.
  48763. *
  48764. * @type {boolean}
  48765. * @default false
  48766. * @since 5.0.8
  48767. * @apioption navigator.opposite
  48768. */
  48769. /**
  48770. * The height of the navigator.
  48771. *
  48772. * @sample {highstock} stock/navigator/height/
  48773. * A higher navigator
  48774. */
  48775. height: 40,
  48776. /**
  48777. * The distance from the nearest element, the X axis or X axis labels.
  48778. *
  48779. * @sample {highstock} stock/navigator/margin/
  48780. * A margin of 2 draws the navigator closer to the X axis labels
  48781. */
  48782. margin: 25,
  48783. /**
  48784. * Whether the mask should be inside the range marking the zoomed
  48785. * range, or outside. In Highcharts Stock 1.x it was always `false`.
  48786. *
  48787. * @sample {highstock} stock/navigator/maskinside-false/
  48788. * False, mask outside
  48789. *
  48790. * @since 2.0
  48791. */
  48792. maskInside: true,
  48793. /**
  48794. * Options for the handles for dragging the zoomed area.
  48795. *
  48796. * @sample {highstock} stock/navigator/handles/
  48797. * Colored handles
  48798. */
  48799. handles: {
  48800. /**
  48801. * Width for handles.
  48802. *
  48803. * @sample {highstock} stock/navigator/styled-handles/
  48804. * Styled handles
  48805. *
  48806. * @since 6.0.0
  48807. */
  48808. width: 7,
  48809. /**
  48810. * Height for handles.
  48811. *
  48812. * @sample {highstock} stock/navigator/styled-handles/
  48813. * Styled handles
  48814. *
  48815. * @since 6.0.0
  48816. */
  48817. height: 15,
  48818. /**
  48819. * Array to define shapes of handles. 0-index for left, 1-index for
  48820. * right.
  48821. *
  48822. * Additionally, the URL to a graphic can be given on this form:
  48823. * `url(graphic.png)`. Note that for the image to be applied to
  48824. * exported charts, its URL needs to be accessible by the export
  48825. * server.
  48826. *
  48827. * Custom callbacks for symbol path generation can also be added to
  48828. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  48829. * used by its method name, as shown in the demo.
  48830. *
  48831. * @sample {highstock} stock/navigator/styled-handles/
  48832. * Styled handles
  48833. *
  48834. * @type {Array<string>}
  48835. * @default ["navigator-handle", "navigator-handle"]
  48836. * @since 6.0.0
  48837. */
  48838. symbols: ['navigator-handle', 'navigator-handle'],
  48839. /**
  48840. * Allows to enable/disable handles.
  48841. *
  48842. * @since 6.0.0
  48843. */
  48844. enabled: true,
  48845. /**
  48846. * The width for the handle border and the stripes inside.
  48847. *
  48848. * @sample {highstock} stock/navigator/styled-handles/
  48849. * Styled handles
  48850. *
  48851. * @since 6.0.0
  48852. * @apioption navigator.handles.lineWidth
  48853. */
  48854. lineWidth: 1,
  48855. /**
  48856. * The fill for the handle.
  48857. *
  48858. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48859. */
  48860. backgroundColor: "#f2f2f2" /* Palette.neutralColor5 */,
  48861. /**
  48862. * The stroke for the handle border and the stripes inside.
  48863. *
  48864. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48865. */
  48866. borderColor: "#999999" /* Palette.neutralColor40 */
  48867. },
  48868. /**
  48869. * The color of the mask covering the areas of the navigator series
  48870. * that are currently not visible in the main series. The default
  48871. * color is bluish with an opacity of 0.3 to see the series below.
  48872. *
  48873. * @see In styled mode, the mask is styled with the
  48874. * `.highcharts-navigator-mask` and
  48875. * `.highcharts-navigator-mask-inside` classes.
  48876. *
  48877. * @sample {highstock} stock/navigator/maskfill/
  48878. * Blue, semi transparent mask
  48879. *
  48880. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48881. * @default rgba(102,133,194,0.3)
  48882. */
  48883. maskFill: color("#667aff" /* Palette.highlightColor60 */).setOpacity(0.3).get(),
  48884. /**
  48885. * The color of the line marking the currently zoomed area in the
  48886. * navigator.
  48887. *
  48888. * @sample {highstock} stock/navigator/outline/
  48889. * 2px blue outline
  48890. *
  48891. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48892. * @default #cccccc
  48893. */
  48894. outlineColor: "#999999" /* Palette.neutralColor40 */,
  48895. /**
  48896. * The width of the line marking the currently zoomed area in the
  48897. * navigator.
  48898. *
  48899. * @see In styled mode, the outline stroke width is set with the
  48900. * `.highcharts-navigator-outline` class.
  48901. *
  48902. * @sample {highstock} stock/navigator/outline/
  48903. * 2px blue outline
  48904. *
  48905. * @type {number}
  48906. */
  48907. outlineWidth: 1,
  48908. /**
  48909. * Options for the navigator series. Available options are the same
  48910. * as any series, documented at [plotOptions](#plotOptions.series)
  48911. * and [series](#series).
  48912. *
  48913. * Unless data is explicitly defined on navigator.series, the data
  48914. * is borrowed from the first series in the chart.
  48915. *
  48916. * Default series options for the navigator series are:
  48917. * ```js
  48918. * series: {
  48919. * type: 'areaspline',
  48920. * fillOpacity: 0.05,
  48921. * dataGrouping: {
  48922. * smoothed: true
  48923. * },
  48924. * lineWidth: 1,
  48925. * marker: {
  48926. * enabled: false
  48927. * }
  48928. * }
  48929. * ```
  48930. *
  48931. * @see In styled mode, the navigator series is styled with the
  48932. * `.highcharts-navigator-series` class.
  48933. *
  48934. * @sample {highstock} stock/navigator/series-data/
  48935. * Using a separate data set for the navigator
  48936. * @sample {highstock} stock/navigator/series/
  48937. * A green navigator series
  48938. *
  48939. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  48940. */
  48941. series: {
  48942. /**
  48943. * The type of the navigator series.
  48944. *
  48945. * Heads up:
  48946. * In column-type navigator, zooming is limited to at least one
  48947. * point with its `pointRange`.
  48948. *
  48949. * @sample {highstock} stock/navigator/column/
  48950. * Column type navigator
  48951. *
  48952. * @type {string}
  48953. * @default {highstock} `areaspline` if defined, otherwise `line`
  48954. * @default {gantt} gantt
  48955. */
  48956. type: (typeof seriesTypes.areaspline === 'undefined' ?
  48957. 'line' :
  48958. 'areaspline'),
  48959. /**
  48960. * The fill opacity of the navigator series.
  48961. */
  48962. fillOpacity: 0.05,
  48963. /**
  48964. * The pixel line width of the navigator series.
  48965. */
  48966. lineWidth: 1,
  48967. /**
  48968. * @ignore-option
  48969. */
  48970. compare: null,
  48971. /**
  48972. * @ignore-option
  48973. */
  48974. sonification: {
  48975. enabled: false
  48976. },
  48977. /**
  48978. * Unless data is explicitly defined, the data is borrowed from the
  48979. * first series in the chart.
  48980. *
  48981. * @type {Array<number|Array<number|string|null>|object|null>}
  48982. * @product highstock
  48983. * @apioption navigator.series.data
  48984. */
  48985. /**
  48986. * Data grouping options for the navigator series.
  48987. *
  48988. * @extends plotOptions.series.dataGrouping
  48989. */
  48990. dataGrouping: {
  48991. approximation: 'average',
  48992. enabled: true,
  48993. groupPixelWidth: 2,
  48994. // Replace smoothed property by anchors, #12455.
  48995. firstAnchor: 'firstPoint',
  48996. anchor: 'middle',
  48997. lastAnchor: 'lastPoint',
  48998. // Day and week differs from plotOptions.series.dataGrouping
  48999. units: [
  49000. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  49001. ['second', [1, 2, 5, 10, 15, 30]],
  49002. ['minute', [1, 2, 5, 10, 15, 30]],
  49003. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  49004. ['day', [1, 2, 3, 4]],
  49005. ['week', [1, 2, 3]],
  49006. ['month', [1, 3, 6]],
  49007. ['year', null]
  49008. ]
  49009. },
  49010. /**
  49011. * Data label options for the navigator series. Data labels are
  49012. * disabled by default on the navigator series.
  49013. *
  49014. * @extends plotOptions.series.dataLabels
  49015. */
  49016. dataLabels: {
  49017. enabled: false,
  49018. zIndex: 2 // #1839
  49019. },
  49020. id: 'highcharts-navigator-series',
  49021. className: 'highcharts-navigator-series',
  49022. /**
  49023. * Sets the fill color of the navigator series.
  49024. *
  49025. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49026. * @apioption navigator.series.color
  49027. */
  49028. /**
  49029. * Line color for the navigator series. Allows setting the color
  49030. * while disallowing the default candlestick setting.
  49031. *
  49032. * @type {Highcharts.ColorString|null}
  49033. */
  49034. lineColor: null,
  49035. marker: {
  49036. enabled: false
  49037. },
  49038. /**
  49039. * Since Highcharts Stock v8, default value is the same as default
  49040. * `pointRange` defined for a specific type (e.g. `null` for
  49041. * column type).
  49042. *
  49043. * In Highcharts Stock version < 8, defaults to 0.
  49044. *
  49045. * @extends plotOptions.series.pointRange
  49046. * @type {number|null}
  49047. * @apioption navigator.series.pointRange
  49048. */
  49049. /**
  49050. * The threshold option. Setting it to 0 will make the default
  49051. * navigator area series draw its area from the 0 value and up.
  49052. *
  49053. * @type {number|null}
  49054. */
  49055. threshold: null
  49056. },
  49057. /**
  49058. * Enable or disable navigator sticking to right, while adding new
  49059. * points. If `undefined`, the navigator sticks to the axis maximum only
  49060. * if it was already at the maximum prior to adding points.
  49061. *
  49062. * @type {boolean}
  49063. * @default undefined
  49064. * @since 10.2.1
  49065. * @sample {highstock} stock/navigator/sticktomax-false/
  49066. * stickToMax set to false
  49067. * @apioption navigator.stickToMax
  49068. */
  49069. /**
  49070. * Options for the navigator X axis. Default series options for the
  49071. * navigator xAxis are:
  49072. * ```js
  49073. * xAxis: {
  49074. * tickWidth: 0,
  49075. * lineWidth: 0,
  49076. * gridLineWidth: 1,
  49077. * tickPixelInterval: 200,
  49078. * labels: {
  49079. * align: 'left',
  49080. * style: {
  49081. * color: '#888'
  49082. * },
  49083. * x: 3,
  49084. * y: -4
  49085. * }
  49086. * }
  49087. * ```
  49088. *
  49089. * @extends xAxis
  49090. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  49091. * showEmpty, maxRange
  49092. */
  49093. xAxis: {
  49094. /**
  49095. * Additional range on the right side of the xAxis. Works similar to
  49096. * xAxis.maxPadding, but value is set in milliseconds.
  49097. * Can be set for both, main xAxis and navigator's xAxis.
  49098. *
  49099. * @since 6.0.0
  49100. */
  49101. overscroll: 0,
  49102. className: 'highcharts-navigator-xaxis',
  49103. tickLength: 0,
  49104. lineWidth: 0,
  49105. gridLineColor: "#e6e6e6" /* Palette.neutralColor10 */,
  49106. gridLineWidth: 1,
  49107. tickPixelInterval: 200,
  49108. labels: {
  49109. align: 'left',
  49110. /**
  49111. * @type {Highcharts.CSSObject}
  49112. */
  49113. style: {
  49114. /** @ignore */
  49115. color: "#000000" /* Palette.neutralColor100 */,
  49116. /** @ignore */
  49117. fontSize: '0.7em',
  49118. /** @ignore */
  49119. opacity: 0.6,
  49120. /** @ignore */
  49121. textOutline: '2px contrast'
  49122. },
  49123. x: 3,
  49124. y: -4
  49125. },
  49126. crosshair: false
  49127. },
  49128. /**
  49129. * Options for the navigator Y axis. Default series options for the
  49130. * navigator yAxis are:
  49131. * ```js
  49132. * yAxis: {
  49133. * gridLineWidth: 0,
  49134. * startOnTick: false,
  49135. * endOnTick: false,
  49136. * minPadding: 0.1,
  49137. * maxPadding: 0.1,
  49138. * labels: {
  49139. * enabled: false
  49140. * },
  49141. * title: {
  49142. * text: null
  49143. * },
  49144. * tickWidth: 0
  49145. * }
  49146. * ```
  49147. *
  49148. * @extends yAxis
  49149. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  49150. * showEmpty, scrollbar, top, units, maxRange, minLength,
  49151. * maxLength, resize
  49152. */
  49153. yAxis: {
  49154. className: 'highcharts-navigator-yaxis',
  49155. gridLineWidth: 0,
  49156. startOnTick: false,
  49157. endOnTick: false,
  49158. minPadding: 0.1,
  49159. maxPadding: 0.1,
  49160. labels: {
  49161. enabled: false
  49162. },
  49163. crosshair: false,
  49164. title: {
  49165. text: null
  49166. },
  49167. tickLength: 0,
  49168. tickWidth: 0
  49169. }
  49170. };
  49171. /* *
  49172. *
  49173. * Default Export
  49174. *
  49175. * */
  49176. /* *
  49177. *
  49178. * API Options
  49179. *
  49180. * */
  49181. /**
  49182. * Maximum range which can be set using the navigator's handles.
  49183. * Opposite of [xAxis.minRange](#xAxis.minRange).
  49184. *
  49185. * @sample {highstock} stock/navigator/maxrange/
  49186. * Defined max and min range
  49187. *
  49188. * @type {number}
  49189. * @since 6.0.0
  49190. * @product highstock gantt
  49191. * @apioption xAxis.maxRange
  49192. */
  49193. (''); // keeps doclets above in JS file
  49194. return NavigatorDefaults;
  49195. });
  49196. _registerModule(_modules, 'Stock/Navigator/NavigatorSymbols.js', [], function () {
  49197. /* *
  49198. *
  49199. * (c) 2010-2021 Torstein Honsi
  49200. *
  49201. * License: www.highcharts.com/license
  49202. *
  49203. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49204. *
  49205. * */
  49206. /* *
  49207. *
  49208. * Constants
  49209. *
  49210. * */
  49211. /**
  49212. * Draw one of the handles on the side of the zoomed range in the navigator.
  49213. * @private
  49214. */
  49215. function navigatorHandle(_x, _y, width, height, options = {}) {
  49216. const halfWidth = options.width ? options.width / 2 : width, markerPosition = Math.round(halfWidth / 3) + 0.5;
  49217. height = options.height || height;
  49218. return [
  49219. ['M', -halfWidth - 1, 0.5],
  49220. ['L', halfWidth, 0.5],
  49221. ['L', halfWidth, height + 0.5],
  49222. ['L', -halfWidth - 1, height + 0.5],
  49223. ['L', -halfWidth - 1, 0.5],
  49224. ['M', -markerPosition, 4],
  49225. ['L', -markerPosition, height - 3],
  49226. ['M', markerPosition - 1, 4],
  49227. ['L', markerPosition - 1, height - 3]
  49228. ];
  49229. }
  49230. /* *
  49231. *
  49232. * Default Export
  49233. *
  49234. * */
  49235. const NavigatorSymbols = {
  49236. 'navigator-handle': navigatorHandle
  49237. };
  49238. return NavigatorSymbols;
  49239. });
  49240. _registerModule(_modules, 'Stock/Navigator/NavigatorComposition.js', [_modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxisComposition.js'], _modules['Stock/Navigator/NavigatorDefaults.js'], _modules['Stock/Navigator/NavigatorSymbols.js'], _modules['Core/Renderer/RendererRegistry.js'], _modules['Core/Utilities.js']], function (D, H, NavigatorAxisAdditions, NavigatorDefaults, NavigatorSymbols, RendererRegistry, U) {
  49241. /* *
  49242. *
  49243. * (c) 2010-2021 Torstein Honsi
  49244. *
  49245. * License: www.highcharts.com/license
  49246. *
  49247. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49248. *
  49249. * */
  49250. const { defaultOptions, setOptions } = D;
  49251. const { isTouchDevice } = H;
  49252. const { getRendererType } = RendererRegistry;
  49253. const { addEvent, extend, merge, pick } = U;
  49254. /* *
  49255. *
  49256. * Constants
  49257. *
  49258. * */
  49259. const composedMembers = [];
  49260. /* *
  49261. *
  49262. * Variables
  49263. *
  49264. * */
  49265. let NavigatorConstructor;
  49266. /* *
  49267. *
  49268. * Functions
  49269. *
  49270. * */
  49271. /**
  49272. * @private
  49273. */
  49274. function compose(AxisClass, ChartClass, NavigatorClass, SeriesClass) {
  49275. NavigatorAxisAdditions.compose(AxisClass);
  49276. NavigatorConstructor = NavigatorClass;
  49277. if (U.pushUnique(composedMembers, ChartClass)) {
  49278. const chartProto = ChartClass.prototype;
  49279. chartProto.callbacks.push(onChartCallback);
  49280. addEvent(ChartClass, 'afterAddSeries', onChartAfterAddSeries);
  49281. addEvent(ChartClass, 'afterSetChartSize', onChartAfterSetChartSize);
  49282. addEvent(ChartClass, 'afterUpdate', onChartAfterUpdate);
  49283. addEvent(ChartClass, 'beforeRender', onChartBeforeRender);
  49284. addEvent(ChartClass, 'beforeShowResetZoom', onChartBeforeShowResetZoom);
  49285. addEvent(ChartClass, 'update', onChartUpdate);
  49286. }
  49287. if (U.pushUnique(composedMembers, SeriesClass)) {
  49288. addEvent(SeriesClass, 'afterUpdate', onSeriesAfterUpdate);
  49289. }
  49290. if (U.pushUnique(composedMembers, getRendererType)) {
  49291. extend(getRendererType().prototype.symbols, NavigatorSymbols);
  49292. }
  49293. if (U.pushUnique(composedMembers, setOptions)) {
  49294. extend(defaultOptions, { navigator: NavigatorDefaults });
  49295. }
  49296. }
  49297. /**
  49298. * Handle adding new series.
  49299. * @private
  49300. */
  49301. function onChartAfterAddSeries() {
  49302. if (this.navigator) {
  49303. // Recompute which series should be shown in navigator, and add them
  49304. this.navigator.setBaseSeries(null, false);
  49305. }
  49306. }
  49307. /**
  49308. * For stock charts, extend the Chart.setChartSize method so that we can set the
  49309. * final top position of the navigator once the height of the chart, including
  49310. * the legend, is determined. #367. We can't use Chart.getMargins, because
  49311. * labels offsets are not calculated yet.
  49312. * @private
  49313. */
  49314. function onChartAfterSetChartSize() {
  49315. var _a;
  49316. const legend = this.legend, navigator = this.navigator;
  49317. let legendOptions, xAxis, yAxis;
  49318. if (navigator) {
  49319. legendOptions = legend && legend.options;
  49320. xAxis = navigator.xAxis;
  49321. yAxis = navigator.yAxis;
  49322. const { scrollbarHeight, scrollButtonSize } = navigator;
  49323. // Compute the top position
  49324. if (this.inverted) {
  49325. navigator.left = navigator.opposite ?
  49326. this.chartWidth - scrollbarHeight -
  49327. navigator.height :
  49328. this.spacing[3] + scrollbarHeight;
  49329. navigator.top = this.plotTop + scrollButtonSize;
  49330. }
  49331. else {
  49332. navigator.left = pick(xAxis.left, this.plotLeft + scrollButtonSize);
  49333. navigator.top = navigator.navigatorOptions.top ||
  49334. this.chartHeight -
  49335. navigator.height -
  49336. scrollbarHeight -
  49337. (((_a = this.scrollbar) === null || _a === void 0 ? void 0 : _a.options.margin) || 0) -
  49338. this.spacing[2] -
  49339. (this.rangeSelector && this.extraBottomMargin ?
  49340. this.rangeSelector.getHeight() :
  49341. 0) -
  49342. ((legendOptions &&
  49343. legendOptions.verticalAlign === 'bottom' &&
  49344. legendOptions.layout !== 'proximate' && // #13392
  49345. legendOptions.enabled &&
  49346. !legendOptions.floating) ?
  49347. legend.legendHeight +
  49348. pick(legendOptions.margin, 10) :
  49349. 0) -
  49350. (this.titleOffset ? this.titleOffset[2] : 0);
  49351. }
  49352. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  49353. if (this.inverted) {
  49354. xAxis.options.left = yAxis.options.left = navigator.left;
  49355. }
  49356. else {
  49357. xAxis.options.top = yAxis.options.top = navigator.top;
  49358. }
  49359. xAxis.setAxisSize();
  49360. yAxis.setAxisSize();
  49361. }
  49362. }
  49363. }
  49364. /**
  49365. * Initialize navigator, if no scrolling exists yet.
  49366. * @private
  49367. */
  49368. function onChartAfterUpdate(event) {
  49369. if (!this.navigator && !this.scroller &&
  49370. (this.options.navigator.enabled ||
  49371. this.options.scrollbar.enabled)) {
  49372. this.scroller = this.navigator = new NavigatorConstructor(this);
  49373. if (pick(event.redraw, true)) {
  49374. this.redraw(event.animation); // #7067
  49375. }
  49376. }
  49377. }
  49378. /**
  49379. * Initialize navigator for stock charts
  49380. * @private
  49381. */
  49382. function onChartBeforeRender() {
  49383. const options = this.options;
  49384. if (options.navigator.enabled ||
  49385. options.scrollbar.enabled) {
  49386. this.scroller = this.navigator = new NavigatorConstructor(this);
  49387. }
  49388. }
  49389. /**
  49390. * For Stock charts. For x only zooming, do not to create the zoom button
  49391. * because X axis zooming is already allowed by the Navigator and Range
  49392. * selector. (#9285)
  49393. * @private
  49394. */
  49395. function onChartBeforeShowResetZoom() {
  49396. const chartOptions = this.options, navigator = chartOptions.navigator, rangeSelector = chartOptions.rangeSelector;
  49397. if (((navigator && navigator.enabled) ||
  49398. (rangeSelector && rangeSelector.enabled)) &&
  49399. ((!isTouchDevice &&
  49400. this.zooming.type === 'x') ||
  49401. (isTouchDevice && this.zooming.pinchType === 'x'))) {
  49402. return false;
  49403. }
  49404. }
  49405. /**
  49406. * @private
  49407. */
  49408. function onChartCallback(chart) {
  49409. const navigator = chart.navigator;
  49410. // Initialize the navigator
  49411. if (navigator && chart.xAxis[0]) {
  49412. const extremes = chart.xAxis[0].getExtremes();
  49413. navigator.render(extremes.min, extremes.max);
  49414. }
  49415. }
  49416. /**
  49417. * Merge options, if no scrolling exists yet
  49418. * @private
  49419. */
  49420. function onChartUpdate(e) {
  49421. const navigatorOptions = (e.options.navigator || {}), scrollbarOptions = (e.options.scrollbar || {});
  49422. if (!this.navigator && !this.scroller &&
  49423. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  49424. merge(true, this.options.navigator, navigatorOptions);
  49425. merge(true, this.options.scrollbar, scrollbarOptions);
  49426. delete e.options.navigator;
  49427. delete e.options.scrollbar;
  49428. }
  49429. }
  49430. /**
  49431. * Handle updating series
  49432. * @private
  49433. */
  49434. function onSeriesAfterUpdate() {
  49435. if (this.chart.navigator && !this.options.isInternal) {
  49436. this.chart.navigator.setBaseSeries(null, false);
  49437. }
  49438. }
  49439. /* *
  49440. *
  49441. * Default Export
  49442. *
  49443. * */
  49444. const NavigatorComposition = {
  49445. compose
  49446. };
  49447. return NavigatorComposition;
  49448. });
  49449. _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Utilities.js']], function (U) {
  49450. /* *
  49451. *
  49452. * (c) 2010-2021 Torstein Honsi
  49453. *
  49454. * License: www.highcharts.com/license
  49455. *
  49456. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49457. *
  49458. * */
  49459. const { addEvent, defined, pick } = U;
  49460. /* *
  49461. *
  49462. * Constants
  49463. *
  49464. * */
  49465. const composedMembers = [];
  49466. /* *
  49467. *
  49468. * Composition
  49469. *
  49470. * */
  49471. /* eslint-disable no-invalid-this, valid-jsdoc */
  49472. /**
  49473. * Creates scrollbars if enabled.
  49474. * @private
  49475. */
  49476. class ScrollbarAxis {
  49477. /* *
  49478. *
  49479. * Static Properties
  49480. *
  49481. * */
  49482. /**
  49483. * Attaches to axis events to create scrollbars if enabled.
  49484. *
  49485. * @private
  49486. *
  49487. * @param AxisClass
  49488. * Axis class to extend.
  49489. *
  49490. * @param ScrollbarClass
  49491. * Scrollbar class to use.
  49492. */
  49493. static compose(AxisClass, ScrollbarClass) {
  49494. if (!U.pushUnique(composedMembers, AxisClass)) {
  49495. return AxisClass;
  49496. }
  49497. const getExtremes = (axis) => {
  49498. const axisMin = pick(axis.options && axis.options.min, axis.min);
  49499. const axisMax = pick(axis.options && axis.options.max, axis.max);
  49500. return {
  49501. axisMin,
  49502. axisMax,
  49503. scrollMin: defined(axis.dataMin) ?
  49504. Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin,
  49505. scrollMax: defined(axis.dataMax) ?
  49506. Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax
  49507. };
  49508. };
  49509. // Wrap axis initialization and create scrollbar if enabled:
  49510. addEvent(AxisClass, 'afterInit', function () {
  49511. const axis = this;
  49512. if (axis.options &&
  49513. axis.options.scrollbar &&
  49514. axis.options.scrollbar.enabled) {
  49515. // Predefined options:
  49516. axis.options.scrollbar.vertical = !axis.horiz;
  49517. axis.options.startOnTick = axis.options.endOnTick = false;
  49518. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  49519. addEvent(axis.scrollbar, 'changed', function (e) {
  49520. let { axisMin, axisMax, scrollMin: unitedMin, scrollMax: unitedMax } = getExtremes(axis), range = unitedMax - unitedMin, to, from;
  49521. // #12834, scroll when show/hide series, wrong extremes
  49522. if (!defined(axisMin) || !defined(axisMax)) {
  49523. return;
  49524. }
  49525. if ((axis.horiz && !axis.reversed) ||
  49526. (!axis.horiz && axis.reversed)) {
  49527. to = unitedMin + range * this.to;
  49528. from = unitedMin + range * this.from;
  49529. }
  49530. else {
  49531. // y-values in browser are reversed, but this also
  49532. // applies for reversed horizontal axis:
  49533. to = unitedMin + range * (1 - this.from);
  49534. from = unitedMin + range * (1 - this.to);
  49535. }
  49536. if (this.shouldUpdateExtremes(e.DOMType)) {
  49537. // #17977, set animation to undefined instead of true
  49538. const animate = e.DOMType === 'mousemove' ||
  49539. e.DOMType === 'touchmove' ? false : void 0;
  49540. axis.setExtremes(from, to, true, animate, e);
  49541. }
  49542. else {
  49543. // When live redraw is disabled, don't change extremes
  49544. // Only change the position of the scollbar thumb
  49545. this.setRange(this.from, this.to);
  49546. }
  49547. });
  49548. }
  49549. });
  49550. // Wrap rendering axis, and update scrollbar if one is created:
  49551. addEvent(AxisClass, 'afterRender', function () {
  49552. let axis = this, { scrollMin, scrollMax } = getExtremes(axis), scrollbar = axis.scrollbar, offset = (axis.axisTitleMargin + (axis.titleOffset || 0)), scrollbarsOffsets = axis.chart.scrollbarsOffsets, axisMargin = axis.options.margin || 0, offsetsIndex, from, to;
  49553. if (scrollbar) {
  49554. if (axis.horiz) {
  49555. // Reserve space for labels/title
  49556. if (!axis.opposite) {
  49557. scrollbarsOffsets[1] += offset;
  49558. }
  49559. scrollbar.position(axis.left, (axis.top +
  49560. axis.height +
  49561. 2 +
  49562. scrollbarsOffsets[1] -
  49563. (axis.opposite ? axisMargin : 0)), axis.width, axis.height);
  49564. // Next scrollbar should reserve space for margin (if set)
  49565. if (!axis.opposite) {
  49566. scrollbarsOffsets[1] += axisMargin;
  49567. }
  49568. offsetsIndex = 1;
  49569. }
  49570. else {
  49571. // Reserve space for labels/title
  49572. if (axis.opposite) {
  49573. scrollbarsOffsets[0] += offset;
  49574. }
  49575. let xPosition;
  49576. if (!scrollbar.options.opposite) {
  49577. xPosition = axis.opposite ? 0 : axisMargin;
  49578. }
  49579. else {
  49580. xPosition = axis.left +
  49581. axis.width +
  49582. 2 +
  49583. scrollbarsOffsets[0] -
  49584. (axis.opposite ? 0 : axisMargin);
  49585. }
  49586. scrollbar.position(xPosition, axis.top, axis.width, axis.height);
  49587. // Next scrollbar should reserve space for margin (if set)
  49588. if (axis.opposite) {
  49589. scrollbarsOffsets[0] += axisMargin;
  49590. }
  49591. offsetsIndex = 0;
  49592. }
  49593. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  49594. (scrollbar.options.margin || 0);
  49595. if (isNaN(scrollMin) ||
  49596. isNaN(scrollMax) ||
  49597. !defined(axis.min) ||
  49598. !defined(axis.max) ||
  49599. axis.min === axis.max // #10733
  49600. ) {
  49601. // default action: when extremes are the same or there is
  49602. // not extremes on the axis, but scrollbar exists, make it
  49603. // full size
  49604. scrollbar.setRange(0, 1);
  49605. }
  49606. else {
  49607. from = ((axis.min - scrollMin) /
  49608. (scrollMax - scrollMin));
  49609. to = ((axis.max - scrollMin) /
  49610. (scrollMax - scrollMin));
  49611. if ((axis.horiz && !axis.reversed) ||
  49612. (!axis.horiz && axis.reversed)) {
  49613. scrollbar.setRange(from, to);
  49614. }
  49615. else {
  49616. // inverse vertical axis
  49617. scrollbar.setRange(1 - to, 1 - from);
  49618. }
  49619. }
  49620. }
  49621. });
  49622. // Make space for a scrollbar:
  49623. addEvent(AxisClass, 'afterGetOffset', function () {
  49624. const axis = this, scrollbar = axis.scrollbar, opposite = scrollbar && !scrollbar.options.opposite, index = axis.horiz ? 2 : opposite ? 3 : 1;
  49625. if (scrollbar) {
  49626. // reset scrollbars offsets
  49627. axis.chart.scrollbarsOffsets = [0, 0];
  49628. axis.chart.axisOffset[index] +=
  49629. scrollbar.size + (scrollbar.options.margin || 0);
  49630. }
  49631. });
  49632. return AxisClass;
  49633. }
  49634. }
  49635. return ScrollbarAxis;
  49636. });
  49637. _registerModule(_modules, 'Stock/Scrollbar/ScrollbarDefaults.js', [_modules['Core/Globals.js']], function (H) {
  49638. /* *
  49639. *
  49640. * (c) 2010-2021 Torstein Honsi
  49641. *
  49642. * License: www.highcharts.com/license
  49643. *
  49644. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49645. *
  49646. * */
  49647. const { isTouchDevice } = H;
  49648. /* *
  49649. *
  49650. * Constant
  49651. *
  49652. * */
  49653. /**
  49654. *
  49655. * The scrollbar is a means of panning over the X axis of a stock chart.
  49656. * Scrollbars can also be applied to other types of axes.
  49657. *
  49658. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  49659. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  49660. * is especially suitable for simpler cartesian charts on mobile.
  49661. *
  49662. * In styled mode, all the presentational options for the
  49663. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  49664. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  49665. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  49666. *
  49667. * @sample stock/yaxis/inverted-bar-scrollbar/
  49668. * A scrollbar on a simple bar chart
  49669. *
  49670. * @product highstock gantt
  49671. * @optionparent scrollbar
  49672. *
  49673. * @private
  49674. */
  49675. const ScrollbarDefaults = {
  49676. /**
  49677. * The height of the scrollbar. If `buttonsEnabled` is true , the height
  49678. * also applies to the width of the scroll arrows so that they are always
  49679. * squares.
  49680. *
  49681. * @sample stock/scrollbar/style/
  49682. * Non-default height
  49683. *
  49684. * @type {number}
  49685. */
  49686. height: 10,
  49687. /**
  49688. * The border rounding radius of the bar.
  49689. *
  49690. * @sample stock/scrollbar/style/
  49691. * Scrollbar styling
  49692. */
  49693. barBorderRadius: 5,
  49694. /**
  49695. * The corner radius of the scrollbar buttons.
  49696. *
  49697. * @sample stock/scrollbar/style/
  49698. * Scrollbar styling
  49699. */
  49700. buttonBorderRadius: 0,
  49701. /**
  49702. * Enable or disable the buttons at the end of the scrollbar.
  49703. *
  49704. * @since 11.0.0
  49705. */
  49706. buttonsEnabled: false,
  49707. /**
  49708. * Enable or disable the scrollbar.
  49709. *
  49710. * @sample stock/scrollbar/enabled/
  49711. * Disable the scrollbar, only use navigator
  49712. *
  49713. * @type {boolean}
  49714. * @default true
  49715. * @apioption scrollbar.enabled
  49716. */
  49717. /**
  49718. * Whether to redraw the main chart as the scrollbar or the navigator
  49719. * zoomed window is moved. Defaults to `true` for modern browsers and
  49720. * `false` for legacy IE browsers as well as mobile devices.
  49721. *
  49722. * @sample stock/scrollbar/liveredraw
  49723. * Setting live redraw to false
  49724. *
  49725. * @type {boolean}
  49726. * @since 1.3
  49727. */
  49728. liveRedraw: void 0,
  49729. /**
  49730. * The margin between the scrollbar and its axis when the scrollbar is
  49731. * applied directly to an axis, or the navigator in case that is enabled.
  49732. * Defaults to 10 for axis, 0 for navigator.
  49733. *
  49734. * @type {number|undefined}
  49735. */
  49736. margin: void 0,
  49737. /**
  49738. * The minimum width of the scrollbar.
  49739. *
  49740. * @since 1.2.5
  49741. */
  49742. minWidth: 6,
  49743. /** @ignore-option */
  49744. opposite: true,
  49745. /**
  49746. * Whether to show or hide the scrollbar when the scrolled content is
  49747. * zoomed out to it full extent.
  49748. *
  49749. * @type {boolean}
  49750. * @default true
  49751. * @apioption scrollbar.showFull
  49752. */
  49753. step: 0.2,
  49754. /**
  49755. * The z index of the scrollbar group.
  49756. */
  49757. zIndex: 3,
  49758. /**
  49759. * The background color of the scrollbar itself.
  49760. *
  49761. * @sample stock/scrollbar/style/
  49762. * Scrollbar styling
  49763. *
  49764. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49765. */
  49766. barBackgroundColor: "#cccccc" /* Palette.neutralColor20 */,
  49767. /**
  49768. * The width of the bar's border.
  49769. *
  49770. * @sample stock/scrollbar/style/
  49771. * Scrollbar styling
  49772. */
  49773. barBorderWidth: 0,
  49774. /**
  49775. * The color of the scrollbar's border.
  49776. *
  49777. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49778. */
  49779. barBorderColor: "#cccccc" /* Palette.neutralColor20 */,
  49780. /**
  49781. * The color of the small arrow inside the scrollbar buttons.
  49782. *
  49783. * @sample stock/scrollbar/style/
  49784. * Scrollbar styling
  49785. *
  49786. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49787. */
  49788. buttonArrowColor: "#333333" /* Palette.neutralColor80 */,
  49789. /**
  49790. * The color of scrollbar buttons.
  49791. *
  49792. * @sample stock/scrollbar/style/
  49793. * Scrollbar styling
  49794. *
  49795. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49796. */
  49797. buttonBackgroundColor: "#e6e6e6" /* Palette.neutralColor10 */,
  49798. /**
  49799. * The color of the border of the scrollbar buttons.
  49800. *
  49801. * @sample stock/scrollbar/style/
  49802. * Scrollbar styling
  49803. *
  49804. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49805. */
  49806. buttonBorderColor: "#cccccc" /* Palette.neutralColor20 */,
  49807. /**
  49808. * The border width of the scrollbar buttons.
  49809. *
  49810. * @sample stock/scrollbar/style/
  49811. * Scrollbar styling
  49812. */
  49813. buttonBorderWidth: 1,
  49814. /**
  49815. * The color of the small rifles in the middle of the scrollbar.
  49816. *
  49817. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49818. */
  49819. rifleColor: 'none',
  49820. /**
  49821. * The color of the track background.
  49822. *
  49823. * @sample stock/scrollbar/style/
  49824. * Scrollbar styling
  49825. *
  49826. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49827. */
  49828. trackBackgroundColor: 'rgba(255, 255, 255, 0.001)',
  49829. /**
  49830. * The color of the border of the scrollbar track.
  49831. *
  49832. * @sample stock/scrollbar/style/
  49833. * Scrollbar styling
  49834. *
  49835. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49836. */
  49837. trackBorderColor: "#cccccc" /* Palette.neutralColor20 */,
  49838. /**
  49839. * The corner radius of the border of the scrollbar track.
  49840. *
  49841. * @sample stock/scrollbar/style/
  49842. * Scrollbar styling
  49843. */
  49844. trackBorderRadius: 5,
  49845. /**
  49846. * The width of the border of the scrollbar track.
  49847. *
  49848. * @sample stock/scrollbar/style/
  49849. * Scrollbar styling
  49850. */
  49851. trackBorderWidth: 1
  49852. };
  49853. /* *
  49854. *
  49855. * Default Export
  49856. *
  49857. * */
  49858. return ScrollbarDefaults;
  49859. });
  49860. _registerModule(_modules, 'Stock/Scrollbar/Scrollbar.js', [_modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Core/Axis/ScrollbarAxis.js'], _modules['Stock/Scrollbar/ScrollbarDefaults.js'], _modules['Core/Utilities.js']], function (D, H, ScrollbarAxis, ScrollbarDefaults, U) {
  49861. /* *
  49862. *
  49863. * (c) 2010-2021 Torstein Honsi
  49864. *
  49865. * License: www.highcharts.com/license
  49866. *
  49867. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49868. *
  49869. * */
  49870. const { defaultOptions } = D;
  49871. const { addEvent, correctFloat, defined, destroyObjectProperties, fireEvent, merge, pick, removeEvent } = U;
  49872. /* *
  49873. *
  49874. * Constants
  49875. *
  49876. * */
  49877. /* eslint-disable no-invalid-this, valid-jsdoc */
  49878. /**
  49879. * A reusable scrollbar, internally used in Highcharts Stock's
  49880. * navigator and optionally on individual axes.
  49881. *
  49882. * @private
  49883. * @class
  49884. * @name Highcharts.Scrollbar
  49885. * @param {Highcharts.SVGRenderer} renderer
  49886. * @param {Highcharts.ScrollbarOptions} options
  49887. * @param {Highcharts.Chart} chart
  49888. */
  49889. class Scrollbar {
  49890. /* *
  49891. *
  49892. * Static Functions
  49893. *
  49894. * */
  49895. static compose(AxisClass) {
  49896. ScrollbarAxis.compose(AxisClass, Scrollbar);
  49897. }
  49898. /**
  49899. * When we have vertical scrollbar, rifles and arrow in buttons should be
  49900. * rotated. The same method is used in Navigator's handles, to rotate them.
  49901. *
  49902. * @function Highcharts.swapXY
  49903. *
  49904. * @param {Highcharts.SVGPathArray} path
  49905. * Path to be rotated.
  49906. *
  49907. * @param {boolean} [vertical]
  49908. * If vertical scrollbar, swap x-y values.
  49909. *
  49910. * @return {Highcharts.SVGPathArray}
  49911. * Rotated path.
  49912. *
  49913. * @requires modules/stock
  49914. */
  49915. static swapXY(path, vertical) {
  49916. if (vertical) {
  49917. path.forEach((seg) => {
  49918. const len = seg.length;
  49919. let temp;
  49920. for (let i = 0; i < len; i += 2) {
  49921. temp = seg[i + 1];
  49922. if (typeof temp === 'number') {
  49923. seg[i + 1] = seg[i + 2];
  49924. seg[i + 2] = temp;
  49925. }
  49926. }
  49927. });
  49928. }
  49929. return path;
  49930. }
  49931. /* *
  49932. *
  49933. * Constructors
  49934. *
  49935. * */
  49936. constructor(renderer, options, chart) {
  49937. /* *
  49938. *
  49939. * Properties
  49940. *
  49941. * */
  49942. this._events = [];
  49943. this.chart = void 0;
  49944. this.chartX = 0;
  49945. this.chartY = 0;
  49946. this.from = 0;
  49947. this.group = void 0;
  49948. this.options = void 0;
  49949. this.renderer = void 0;
  49950. this.scrollbar = void 0;
  49951. this.scrollbarButtons = [];
  49952. this.scrollbarGroup = void 0;
  49953. this.scrollbarLeft = 0;
  49954. this.scrollbarRifles = void 0;
  49955. this.scrollbarStrokeWidth = 1;
  49956. this.scrollbarTop = 0;
  49957. this.size = 0;
  49958. this.to = 0;
  49959. this.track = void 0;
  49960. this.trackBorderWidth = 1;
  49961. this.userOptions = void 0;
  49962. this.x = 0;
  49963. this.y = 0;
  49964. this.init(renderer, options, chart);
  49965. }
  49966. /* *
  49967. *
  49968. * Functions
  49969. *
  49970. * */
  49971. /**
  49972. * Set up the mouse and touch events for the Scrollbar
  49973. *
  49974. * @private
  49975. * @function Highcharts.Scrollbar#addEvents
  49976. */
  49977. addEvents() {
  49978. const buttonsOrder = this.options.inverted ? [1, 0] : [0, 1], buttons = this.scrollbarButtons, bar = this.scrollbarGroup.element, track = this.track.element, mouseDownHandler = this.mouseDownHandler.bind(this), mouseMoveHandler = this.mouseMoveHandler.bind(this), mouseUpHandler = this.mouseUpHandler.bind(this);
  49979. // Mouse events
  49980. const _events = [
  49981. [
  49982. buttons[buttonsOrder[0]].element,
  49983. 'click',
  49984. this.buttonToMinClick.bind(this)
  49985. ],
  49986. [
  49987. buttons[buttonsOrder[1]].element,
  49988. 'click',
  49989. this.buttonToMaxClick.bind(this)
  49990. ],
  49991. [track, 'click', this.trackClick.bind(this)],
  49992. [bar, 'mousedown', mouseDownHandler],
  49993. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  49994. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  49995. ];
  49996. // Touch events
  49997. if (H.hasTouch) {
  49998. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  49999. }
  50000. // Add them all
  50001. _events.forEach(function (args) {
  50002. addEvent.apply(null, args);
  50003. });
  50004. this._events = _events;
  50005. }
  50006. buttonToMaxClick(e) {
  50007. const scroller = this;
  50008. const range = ((scroller.to - scroller.from) *
  50009. pick(scroller.options.step, 0.2));
  50010. scroller.updatePosition(scroller.from + range, scroller.to + range);
  50011. fireEvent(scroller, 'changed', {
  50012. from: scroller.from,
  50013. to: scroller.to,
  50014. trigger: 'scrollbar',
  50015. DOMEvent: e
  50016. });
  50017. }
  50018. buttonToMinClick(e) {
  50019. const scroller = this;
  50020. const range = correctFloat(scroller.to - scroller.from) *
  50021. pick(scroller.options.step, 0.2);
  50022. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  50023. fireEvent(scroller, 'changed', {
  50024. from: scroller.from,
  50025. to: scroller.to,
  50026. trigger: 'scrollbar',
  50027. DOMEvent: e
  50028. });
  50029. }
  50030. /**
  50031. * Get normalized (0-1) cursor position over the scrollbar
  50032. *
  50033. * @private
  50034. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  50035. *
  50036. * @param {*} normalizedEvent
  50037. * normalized event, with chartX and chartY values
  50038. *
  50039. * @return {Highcharts.Dictionary<number>}
  50040. * Local position {chartX, chartY}
  50041. */
  50042. cursorToScrollbarPosition(normalizedEvent) {
  50043. const scroller = this, options = scroller.options, minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  50044. options.minWidth :
  50045. 0; // minWidth distorts translation
  50046. return {
  50047. chartX: (normalizedEvent.chartX - scroller.x -
  50048. scroller.xOffset) /
  50049. (scroller.barWidth - minWidthDifference),
  50050. chartY: (normalizedEvent.chartY - scroller.y -
  50051. scroller.yOffset) /
  50052. (scroller.barWidth - minWidthDifference)
  50053. };
  50054. }
  50055. /**
  50056. * Destroys allocated elements.
  50057. *
  50058. * @private
  50059. * @function Highcharts.Scrollbar#destroy
  50060. */
  50061. destroy() {
  50062. const scroller = this, navigator = scroller.chart.scroller;
  50063. // Disconnect events added in addEvents
  50064. scroller.removeEvents();
  50065. // Destroy properties
  50066. [
  50067. 'track',
  50068. 'scrollbarRifles',
  50069. 'scrollbar',
  50070. 'scrollbarGroup',
  50071. 'group'
  50072. ].forEach(function (prop) {
  50073. if (scroller[prop] && scroller[prop].destroy) {
  50074. scroller[prop] = scroller[prop].destroy();
  50075. }
  50076. });
  50077. // #6421, chart may have more scrollbars
  50078. if (navigator && scroller === navigator.scrollbar) {
  50079. navigator.scrollbar = null;
  50080. // Destroy elements in collection
  50081. destroyObjectProperties(navigator.scrollbarButtons);
  50082. }
  50083. }
  50084. /**
  50085. * Draw the scrollbar buttons with arrows
  50086. *
  50087. * @private
  50088. * @function Highcharts.Scrollbar#drawScrollbarButton
  50089. * @param {number} index
  50090. * 0 is left, 1 is right
  50091. */
  50092. drawScrollbarButton(index) {
  50093. const scroller = this, renderer = scroller.renderer, scrollbarButtons = scroller.scrollbarButtons, options = scroller.options, size = scroller.size, group = renderer.g().add(scroller.group);
  50094. scrollbarButtons.push(group);
  50095. if (options.buttonsEnabled) {
  50096. // Create a rectangle for the scrollbar button
  50097. const rect = renderer.rect()
  50098. .addClass('highcharts-scrollbar-button')
  50099. .add(group);
  50100. // Presentational attributes
  50101. if (!scroller.chart.styledMode) {
  50102. rect.attr({
  50103. stroke: options.buttonBorderColor,
  50104. 'stroke-width': options.buttonBorderWidth,
  50105. fill: options.buttonBackgroundColor
  50106. });
  50107. }
  50108. // Place the rectangle based on the rendered stroke width
  50109. rect.attr(rect.crisp({
  50110. x: -0.5,
  50111. y: -0.5,
  50112. // +1 to compensate for crispifying in rect method
  50113. width: size + 1,
  50114. height: size + 1,
  50115. r: options.buttonBorderRadius
  50116. }, rect.strokeWidth()));
  50117. // Button arrow
  50118. const arrow = renderer
  50119. .path(Scrollbar.swapXY([[
  50120. 'M',
  50121. size / 2 + (index ? -1 : 1),
  50122. size / 2 - 3
  50123. ], [
  50124. 'L',
  50125. size / 2 + (index ? -1 : 1),
  50126. size / 2 + 3
  50127. ], [
  50128. 'L',
  50129. size / 2 + (index ? 2 : -2),
  50130. size / 2
  50131. ]], options.vertical))
  50132. .addClass('highcharts-scrollbar-arrow')
  50133. .add(scrollbarButtons[index]);
  50134. if (!scroller.chart.styledMode) {
  50135. arrow.attr({
  50136. fill: options.buttonArrowColor
  50137. });
  50138. }
  50139. }
  50140. }
  50141. /**
  50142. * @private
  50143. * @function Highcharts.Scrollbar#init
  50144. * @param {Highcharts.SVGRenderer} renderer
  50145. * @param {Highcharts.ScrollbarOptions} options
  50146. * @param {Highcharts.Chart} chart
  50147. */
  50148. init(renderer, options, chart) {
  50149. const scroller = this;
  50150. scroller.scrollbarButtons = [];
  50151. scroller.renderer = renderer;
  50152. scroller.userOptions = options;
  50153. scroller.options = merge(ScrollbarDefaults, defaultOptions.scrollbar, options);
  50154. scroller.options.margin = pick(scroller.options.margin, 10);
  50155. scroller.chart = chart;
  50156. // backward compatibility
  50157. scroller.size = pick(scroller.options.size, scroller.options.height);
  50158. // Init
  50159. if (options.enabled) {
  50160. scroller.render();
  50161. scroller.addEvents();
  50162. }
  50163. }
  50164. mouseDownHandler(e) {
  50165. const scroller = this, normalizedEvent = scroller.chart.pointer.normalize(e), mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  50166. scroller.chartX = mousePosition.chartX;
  50167. scroller.chartY = mousePosition.chartY;
  50168. scroller.initPositions = [scroller.from, scroller.to];
  50169. scroller.grabbedCenter = true;
  50170. }
  50171. /**
  50172. * Event handler for the mouse move event.
  50173. * @private
  50174. */
  50175. mouseMoveHandler(e) {
  50176. const scroller = this, normalizedEvent = scroller.chart.pointer.normalize(e), options = scroller.options, direction = options.vertical ?
  50177. 'chartY' : 'chartX', initPositions = scroller.initPositions || [];
  50178. let scrollPosition, chartPosition, change;
  50179. // In iOS, a mousemove event with e.pageX === 0 is fired when
  50180. // holding the finger down in the center of the scrollbar. This
  50181. // should be ignored.
  50182. if (scroller.grabbedCenter &&
  50183. // #4696, scrollbar failed on Android
  50184. (!e.touches || e.touches[0][direction] !== 0)) {
  50185. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  50186. scrollPosition = scroller[direction];
  50187. change = chartPosition - scrollPosition;
  50188. scroller.hasDragged = true;
  50189. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  50190. if (scroller.hasDragged) {
  50191. fireEvent(scroller, 'changed', {
  50192. from: scroller.from,
  50193. to: scroller.to,
  50194. trigger: 'scrollbar',
  50195. DOMType: e.type,
  50196. DOMEvent: e
  50197. });
  50198. }
  50199. }
  50200. }
  50201. /**
  50202. * Event handler for the mouse up event.
  50203. * @private
  50204. */
  50205. mouseUpHandler(e) {
  50206. const scroller = this;
  50207. if (scroller.hasDragged) {
  50208. fireEvent(scroller, 'changed', {
  50209. from: scroller.from,
  50210. to: scroller.to,
  50211. trigger: 'scrollbar',
  50212. DOMType: e.type,
  50213. DOMEvent: e
  50214. });
  50215. }
  50216. scroller.grabbedCenter =
  50217. scroller.hasDragged =
  50218. scroller.chartX =
  50219. scroller.chartY = null;
  50220. }
  50221. /**
  50222. * Position the scrollbar, method called from a parent with defined
  50223. * dimensions.
  50224. *
  50225. * @private
  50226. * @function Highcharts.Scrollbar#position
  50227. * @param {number} x
  50228. * x-position on the chart
  50229. * @param {number} y
  50230. * y-position on the chart
  50231. * @param {number} width
  50232. * width of the scrollbar
  50233. * @param {number} height
  50234. * height of the scorllbar
  50235. */
  50236. position(x, y, width, height) {
  50237. const scroller = this, options = scroller.options, { buttonsEnabled, margin = 0, vertical } = options, method = scroller.rendered ? 'animate' : 'attr';
  50238. let xOffset = height, yOffset = 0;
  50239. // Make the scrollbar visible when it is repositioned, #15763.
  50240. scroller.group.show();
  50241. scroller.x = x;
  50242. scroller.y = y + this.trackBorderWidth;
  50243. scroller.width = width; // width with buttons
  50244. scroller.height = height;
  50245. scroller.xOffset = xOffset;
  50246. scroller.yOffset = yOffset;
  50247. // If Scrollbar is a vertical type, swap options:
  50248. if (vertical) {
  50249. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  50250. scroller.xOffset = xOffset = 0;
  50251. scroller.yOffset = yOffset = buttonsEnabled ? scroller.size : 0;
  50252. // width without buttons
  50253. scroller.barWidth = height - (buttonsEnabled ? width * 2 : 0);
  50254. scroller.x = x = x + margin;
  50255. }
  50256. else {
  50257. scroller.height = height = scroller.size;
  50258. scroller.xOffset = xOffset = buttonsEnabled ? scroller.size : 0;
  50259. // width without buttons
  50260. scroller.barWidth = width - (buttonsEnabled ? height * 2 : 0);
  50261. scroller.y = scroller.y + margin;
  50262. }
  50263. // Set general position for a group:
  50264. scroller.group[method]({
  50265. translateX: x,
  50266. translateY: scroller.y
  50267. });
  50268. // Resize background/track:
  50269. scroller.track[method]({
  50270. width: width,
  50271. height: height
  50272. });
  50273. // Move right/bottom button to its place:
  50274. scroller.scrollbarButtons[1][method]({
  50275. translateX: vertical ? 0 : width - xOffset,
  50276. translateY: vertical ? height - yOffset : 0
  50277. });
  50278. }
  50279. /**
  50280. * Removes the event handlers attached previously with addEvents.
  50281. *
  50282. * @private
  50283. * @function Highcharts.Scrollbar#removeEvents
  50284. */
  50285. removeEvents() {
  50286. this._events.forEach(function (args) {
  50287. removeEvent.apply(null, args);
  50288. });
  50289. this._events.length = 0;
  50290. }
  50291. /**
  50292. * Render scrollbar with all required items.
  50293. *
  50294. * @private
  50295. * @function Highcharts.Scrollbar#render
  50296. */
  50297. render() {
  50298. const scroller = this, renderer = scroller.renderer, options = scroller.options, size = scroller.size, styledMode = scroller.chart.styledMode, group = renderer.g('scrollbar')
  50299. .attr({
  50300. zIndex: options.zIndex
  50301. })
  50302. .hide() // initially hide the scrollbar #15863
  50303. .add();
  50304. // Draw the scrollbar group
  50305. scroller.group = group;
  50306. // Draw the scrollbar track:
  50307. scroller.track = renderer.rect()
  50308. .addClass('highcharts-scrollbar-track')
  50309. .attr({
  50310. r: options.trackBorderRadius || 0,
  50311. height: size,
  50312. width: size
  50313. }).add(group);
  50314. if (!styledMode) {
  50315. scroller.track.attr({
  50316. fill: options.trackBackgroundColor,
  50317. stroke: options.trackBorderColor,
  50318. 'stroke-width': options.trackBorderWidth
  50319. });
  50320. }
  50321. const trackBorderWidth = scroller.trackBorderWidth =
  50322. scroller.track.strokeWidth();
  50323. scroller.track.attr({
  50324. x: -trackBorderWidth % 2 / 2,
  50325. y: -trackBorderWidth % 2 / 2
  50326. });
  50327. // Draw the scrollbar itself
  50328. scroller.scrollbarGroup = renderer.g().add(group);
  50329. scroller.scrollbar = renderer.rect()
  50330. .addClass('highcharts-scrollbar-thumb')
  50331. .attr({
  50332. height: size - trackBorderWidth,
  50333. width: size - trackBorderWidth,
  50334. r: options.barBorderRadius || 0
  50335. }).add(scroller.scrollbarGroup);
  50336. scroller.scrollbarRifles = renderer
  50337. .path(Scrollbar.swapXY([
  50338. ['M', -3, size / 4],
  50339. ['L', -3, 2 * size / 3],
  50340. ['M', 0, size / 4],
  50341. ['L', 0, 2 * size / 3],
  50342. ['M', 3, size / 4],
  50343. ['L', 3, 2 * size / 3]
  50344. ], options.vertical))
  50345. .addClass('highcharts-scrollbar-rifles')
  50346. .add(scroller.scrollbarGroup);
  50347. if (!styledMode) {
  50348. scroller.scrollbar.attr({
  50349. fill: options.barBackgroundColor,
  50350. stroke: options.barBorderColor,
  50351. 'stroke-width': options.barBorderWidth
  50352. });
  50353. scroller.scrollbarRifles.attr({
  50354. stroke: options.rifleColor,
  50355. 'stroke-width': 1
  50356. });
  50357. }
  50358. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  50359. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  50360. // Draw the buttons:
  50361. scroller.drawScrollbarButton(0);
  50362. scroller.drawScrollbarButton(1);
  50363. }
  50364. /**
  50365. * Set scrollbar size, with a given scale.
  50366. *
  50367. * @private
  50368. * @function Highcharts.Scrollbar#setRange
  50369. * @param {number} from
  50370. * scale (0-1) where bar should start
  50371. * @param {number} to
  50372. * scale (0-1) where bar should end
  50373. */
  50374. setRange(from, to) {
  50375. const scroller = this, options = scroller.options, vertical = options.vertical, minWidth = options.minWidth, fullWidth = scroller.barWidth, method = (this.rendered &&
  50376. !this.hasDragged &&
  50377. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  50378. if (!defined(fullWidth)) {
  50379. return;
  50380. }
  50381. const toPX = fullWidth * Math.min(to, 1);
  50382. let fromPX, newSize;
  50383. from = Math.max(from, 0);
  50384. fromPX = Math.ceil(fullWidth * from);
  50385. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  50386. // We need to recalculate position, if minWidth is used
  50387. if (newSize < minWidth) {
  50388. fromPX = (fullWidth - minWidth + newSize) * from;
  50389. newSize = minWidth;
  50390. }
  50391. const newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  50392. const newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  50393. // Store current position:
  50394. scroller.from = from;
  50395. scroller.to = to;
  50396. if (!vertical) {
  50397. scroller.scrollbarGroup[method]({
  50398. translateX: newPos
  50399. });
  50400. scroller.scrollbar[method]({
  50401. width: newSize
  50402. });
  50403. scroller.scrollbarRifles[method]({
  50404. translateX: newRiflesPos
  50405. });
  50406. scroller.scrollbarLeft = newPos;
  50407. scroller.scrollbarTop = 0;
  50408. }
  50409. else {
  50410. scroller.scrollbarGroup[method]({
  50411. translateY: newPos
  50412. });
  50413. scroller.scrollbar[method]({
  50414. height: newSize
  50415. });
  50416. scroller.scrollbarRifles[method]({
  50417. translateY: newRiflesPos
  50418. });
  50419. scroller.scrollbarTop = newPos;
  50420. scroller.scrollbarLeft = 0;
  50421. }
  50422. if (newSize <= 12) {
  50423. scroller.scrollbarRifles.hide();
  50424. }
  50425. else {
  50426. scroller.scrollbarRifles.show();
  50427. }
  50428. // Show or hide the scrollbar based on the showFull setting
  50429. if (options.showFull === false) {
  50430. if (from <= 0 && to >= 1) {
  50431. scroller.group.hide();
  50432. }
  50433. else {
  50434. scroller.group.show();
  50435. }
  50436. }
  50437. scroller.rendered = true;
  50438. }
  50439. /**
  50440. * Checks if the extremes should be updated in response to a scrollbar
  50441. * change event.
  50442. *
  50443. * @private
  50444. * @function Highcharts.Scrollbar#shouldUpdateExtremes
  50445. */
  50446. shouldUpdateExtremes(eventType) {
  50447. return (pick(this.options.liveRedraw, H.svg &&
  50448. !H.isTouchDevice &&
  50449. !this.chart.boosted) ||
  50450. // Mouseup always should change extremes
  50451. eventType === 'mouseup' ||
  50452. eventType === 'touchend' ||
  50453. // Internal events
  50454. !defined(eventType));
  50455. }
  50456. trackClick(e) {
  50457. const scroller = this;
  50458. const normalizedEvent = scroller.chart.pointer.normalize(e), range = scroller.to - scroller.from, top = scroller.y + scroller.scrollbarTop, left = scroller.x + scroller.scrollbarLeft;
  50459. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  50460. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  50461. // On the top or on the left side of the track:
  50462. scroller.updatePosition(scroller.from + range, scroller.to + range);
  50463. }
  50464. else {
  50465. // On the bottom or the right side of the track:
  50466. scroller.updatePosition(scroller.from - range, scroller.to - range);
  50467. }
  50468. fireEvent(scroller, 'changed', {
  50469. from: scroller.from,
  50470. to: scroller.to,
  50471. trigger: 'scrollbar',
  50472. DOMEvent: e
  50473. });
  50474. }
  50475. /**
  50476. * Update the scrollbar with new options
  50477. *
  50478. * @private
  50479. * @function Highcharts.Scrollbar#update
  50480. * @param {Highcharts.ScrollbarOptions} options
  50481. */
  50482. update(options) {
  50483. this.destroy();
  50484. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  50485. }
  50486. /**
  50487. * Update position option in the Scrollbar, with normalized 0-1 scale
  50488. *
  50489. * @private
  50490. * @function Highcharts.Scrollbar#updatePosition
  50491. * @param {number} from
  50492. * @param {number} to
  50493. */
  50494. updatePosition(from, to) {
  50495. if (to > 1) {
  50496. from = correctFloat(1 - correctFloat(to - from));
  50497. to = 1;
  50498. }
  50499. if (from < 0) {
  50500. to = correctFloat(to - from);
  50501. from = 0;
  50502. }
  50503. this.from = from;
  50504. this.to = to;
  50505. }
  50506. }
  50507. /* *
  50508. *
  50509. * Static Properties
  50510. *
  50511. * */
  50512. Scrollbar.defaultOptions = ScrollbarDefaults;
  50513. /* *
  50514. *
  50515. * Registry
  50516. *
  50517. * */
  50518. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  50519. /* *
  50520. *
  50521. * Default Export
  50522. *
  50523. * */
  50524. return Scrollbar;
  50525. });
  50526. _registerModule(_modules, 'Stock/Navigator/Navigator.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Core/Axis/NavigatorAxisComposition.js'], _modules['Stock/Navigator/NavigatorComposition.js'], _modules['Stock/Scrollbar/Scrollbar.js'], _modules['Core/Utilities.js']], function (Axis, D, H, NavigatorAxisAdditions, NavigatorComposition, Scrollbar, U) {
  50527. /* *
  50528. *
  50529. * (c) 2010-2021 Torstein Honsi
  50530. *
  50531. * License: www.highcharts.com/license
  50532. *
  50533. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  50534. *
  50535. * */
  50536. const { defaultOptions } = D;
  50537. const { hasTouch, isTouchDevice } = H;
  50538. const { addEvent, clamp, correctFloat, defined, destroyObjectProperties, erase, extend, find, isArray, isNumber, merge, pick, removeEvent, splat } = U;
  50539. /* *
  50540. *
  50541. * Functions
  50542. *
  50543. * */
  50544. /**
  50545. * Finding the min or max of a set of variables where we don't know if they are
  50546. * defined, is a pattern that is repeated several places in Highcharts. Consider
  50547. * making this a global utility method.
  50548. * @private
  50549. */
  50550. function numExt(extreme, ...args) {
  50551. const numbers = [].filter.call(args, isNumber);
  50552. if (numbers.length) {
  50553. return Math[extreme].apply(0, numbers);
  50554. }
  50555. }
  50556. /* *
  50557. *
  50558. * Class
  50559. *
  50560. * */
  50561. /**
  50562. * The Navigator class
  50563. *
  50564. * @private
  50565. * @class
  50566. * @name Highcharts.Navigator
  50567. *
  50568. * @param {Highcharts.Chart} chart
  50569. * Chart object
  50570. */
  50571. class Navigator {
  50572. /* *
  50573. *
  50574. * Static Functions
  50575. *
  50576. * */
  50577. static compose(AxisClass, ChartClass, SeriesClass) {
  50578. NavigatorComposition.compose(AxisClass, ChartClass, Navigator, SeriesClass);
  50579. }
  50580. /* *
  50581. *
  50582. * Constructor
  50583. *
  50584. * */
  50585. constructor(chart) {
  50586. /* *
  50587. *
  50588. * Properties
  50589. *
  50590. * */
  50591. this.baseSeries = void 0;
  50592. this.chart = void 0;
  50593. this.handles = void 0;
  50594. this.height = void 0;
  50595. this.left = void 0;
  50596. this.navigatorEnabled = void 0;
  50597. this.navigatorGroup = void 0;
  50598. this.navigatorOptions = void 0;
  50599. this.navigatorSeries = void 0;
  50600. this.navigatorSize = void 0;
  50601. this.opposite = void 0;
  50602. this.outline = void 0;
  50603. this.range = void 0;
  50604. this.rendered = void 0;
  50605. this.scrollbarHeight = 0;
  50606. this.scrollButtonSize = void 0;
  50607. this.shades = void 0;
  50608. this.size = void 0;
  50609. this.top = void 0;
  50610. this.xAxis = void 0;
  50611. this.yAxis = void 0;
  50612. this.zoomedMax = void 0;
  50613. this.zoomedMin = void 0;
  50614. this.init(chart);
  50615. }
  50616. /* *
  50617. *
  50618. * Functions
  50619. *
  50620. * */
  50621. /**
  50622. * Draw one of the handles on the side of the zoomed range in the navigator.
  50623. *
  50624. * @private
  50625. * @function Highcharts.Navigator#drawHandle
  50626. *
  50627. * @param {number} x
  50628. * The x center for the handle
  50629. *
  50630. * @param {number} index
  50631. * 0 for left and 1 for right
  50632. *
  50633. * @param {boolean|undefined} inverted
  50634. * Flag for chart.inverted
  50635. *
  50636. * @param {string} verb
  50637. * Use 'animate' or 'attr'
  50638. */
  50639. drawHandle(x, index, inverted, verb) {
  50640. const navigator = this, height = navigator.navigatorOptions.handles.height;
  50641. // Place it
  50642. navigator.handles[index][verb](inverted ? {
  50643. translateX: Math.round(navigator.left + navigator.height / 2),
  50644. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  50645. } : {
  50646. translateX: Math.round(navigator.left + parseInt(x, 10)),
  50647. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  50648. });
  50649. }
  50650. /**
  50651. * Render outline around the zoomed range
  50652. *
  50653. * @private
  50654. * @function Highcharts.Navigator#drawOutline
  50655. *
  50656. * @param {number} zoomedMin
  50657. * in pixels position where zoomed range starts
  50658. *
  50659. * @param {number} zoomedMax
  50660. * in pixels position where zoomed range ends
  50661. *
  50662. * @param {boolean|undefined} inverted
  50663. * flag if chart is inverted
  50664. *
  50665. * @param {string} verb
  50666. * use 'animate' or 'attr'
  50667. */
  50668. drawOutline(zoomedMin, zoomedMax, inverted, verb) {
  50669. const navigator = this, maskInside = navigator.navigatorOptions.maskInside, outlineWidth = navigator.outline.strokeWidth(), halfOutline = outlineWidth / 2, outlineCorrection = (outlineWidth % 2) / 2, // #5800
  50670. scrollButtonSize = navigator.scrollButtonSize, navigatorSize = navigator.size, navigatorTop = navigator.top, height = navigator.height, lineTop = navigatorTop - halfOutline, lineBtm = navigatorTop + height;
  50671. let left = navigator.left, verticalMin, path;
  50672. if (inverted) {
  50673. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  50674. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  50675. path = [
  50676. [
  50677. 'M',
  50678. left + height,
  50679. navigatorTop - scrollButtonSize - outlineCorrection
  50680. ],
  50681. // top right of zoomed range
  50682. ['L', left + height, verticalMin],
  50683. ['L', left, verticalMin],
  50684. ['M', left, zoomedMax],
  50685. ['L', left + height, zoomedMax],
  50686. [
  50687. 'L',
  50688. left + height,
  50689. navigatorTop + navigatorSize + scrollButtonSize
  50690. ]
  50691. ];
  50692. if (maskInside) {
  50693. path.push(
  50694. // upper left of zoomed range
  50695. ['M', left + height, verticalMin - halfOutline],
  50696. // upper right of z.r.
  50697. [
  50698. 'L',
  50699. left + height,
  50700. zoomedMax + halfOutline
  50701. ]);
  50702. }
  50703. }
  50704. else {
  50705. left -= scrollButtonSize;
  50706. zoomedMin += left + scrollButtonSize - outlineCorrection;
  50707. zoomedMax += left + scrollButtonSize - outlineCorrection;
  50708. path = [
  50709. // left
  50710. ['M', left, lineTop],
  50711. // upper left of zoomed range
  50712. ['L', zoomedMin, lineTop],
  50713. // lower left of z.r.
  50714. ['L', zoomedMin, lineBtm],
  50715. // lower right of z.r.
  50716. ['M', zoomedMax, lineBtm],
  50717. // upper right of z.r.
  50718. ['L', zoomedMax, lineTop],
  50719. // right
  50720. [
  50721. 'L',
  50722. left + navigatorSize + scrollButtonSize * 2,
  50723. navigatorTop + halfOutline
  50724. ]
  50725. ];
  50726. if (maskInside) {
  50727. path.push(
  50728. // upper left of zoomed range
  50729. ['M', zoomedMin - halfOutline, lineTop],
  50730. // upper right of z.r.
  50731. ['L', zoomedMax + halfOutline, lineTop]);
  50732. }
  50733. }
  50734. navigator.outline[verb]({
  50735. d: path
  50736. });
  50737. }
  50738. /**
  50739. * Render outline around the zoomed range
  50740. *
  50741. * @private
  50742. * @function Highcharts.Navigator#drawMasks
  50743. *
  50744. * @param {number} zoomedMin
  50745. * in pixels position where zoomed range starts
  50746. *
  50747. * @param {number} zoomedMax
  50748. * in pixels position where zoomed range ends
  50749. *
  50750. * @param {boolean|undefined} inverted
  50751. * flag if chart is inverted
  50752. *
  50753. * @param {string} verb
  50754. * use 'animate' or 'attr'
  50755. */
  50756. drawMasks(zoomedMin, zoomedMax, inverted, verb) {
  50757. const navigator = this, left = navigator.left, top = navigator.top, navigatorHeight = navigator.height;
  50758. let height, width, x, y;
  50759. // Determine rectangle position & size
  50760. // According to (non)inverted position:
  50761. if (inverted) {
  50762. x = [left, left, left];
  50763. y = [top, top + zoomedMin, top + zoomedMax];
  50764. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  50765. height = [
  50766. zoomedMin,
  50767. zoomedMax - zoomedMin,
  50768. navigator.size - zoomedMax
  50769. ];
  50770. }
  50771. else {
  50772. x = [left, left + zoomedMin, left + zoomedMax];
  50773. y = [top, top, top];
  50774. width = [
  50775. zoomedMin,
  50776. zoomedMax - zoomedMin,
  50777. navigator.size - zoomedMax
  50778. ];
  50779. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  50780. }
  50781. navigator.shades.forEach((shade, i) => {
  50782. shade[verb]({
  50783. x: x[i],
  50784. y: y[i],
  50785. width: width[i],
  50786. height: height[i]
  50787. });
  50788. });
  50789. }
  50790. /**
  50791. * Generate DOM elements for a navigator:
  50792. *
  50793. * - main navigator group
  50794. *
  50795. * - all shades
  50796. *
  50797. * - outline
  50798. *
  50799. * - handles
  50800. *
  50801. * @private
  50802. * @function Highcharts.Navigator#renderElements
  50803. */
  50804. renderElements() {
  50805. const navigator = this, navigatorOptions = navigator.navigatorOptions, maskInside = navigatorOptions.maskInside, chart = navigator.chart, inverted = chart.inverted, renderer = chart.renderer, mouseCursor = {
  50806. cursor: inverted ? 'ns-resize' : 'ew-resize'
  50807. },
  50808. // Create the main navigator group
  50809. navigatorGroup = navigator.navigatorGroup = renderer
  50810. .g('navigator')
  50811. .attr({
  50812. zIndex: 8,
  50813. visibility: 'hidden'
  50814. })
  50815. .add();
  50816. // Create masks, each mask will get events and fill:
  50817. [
  50818. !maskInside,
  50819. maskInside,
  50820. !maskInside
  50821. ].forEach((hasMask, index) => {
  50822. const shade = renderer.rect()
  50823. .addClass('highcharts-navigator-mask' +
  50824. (index === 1 ? '-inside' : '-outside'))
  50825. .add(navigatorGroup);
  50826. if (!chart.styledMode) {
  50827. shade.attr({
  50828. fill: hasMask ?
  50829. navigatorOptions.maskFill :
  50830. 'rgba(0,0,0,0)'
  50831. });
  50832. if (index === 1) {
  50833. shade.css(mouseCursor);
  50834. }
  50835. }
  50836. navigator.shades[index] = shade;
  50837. });
  50838. // Create the outline:
  50839. navigator.outline = renderer.path()
  50840. .addClass('highcharts-navigator-outline')
  50841. .add(navigatorGroup);
  50842. if (!chart.styledMode) {
  50843. navigator.outline.attr({
  50844. 'stroke-width': navigatorOptions.outlineWidth,
  50845. stroke: navigatorOptions.outlineColor
  50846. });
  50847. }
  50848. // Create the handlers:
  50849. if (navigatorOptions.handles && navigatorOptions.handles.enabled) {
  50850. const handlesOptions = navigatorOptions.handles, { height, width } = handlesOptions;
  50851. [0, 1].forEach((index) => {
  50852. navigator.handles[index] = renderer.symbol(handlesOptions.symbols[index], -width / 2 - 1, 0, width, height, handlesOptions);
  50853. if (chart.inverted) {
  50854. navigator.handles[index].attr({
  50855. rotation: 90,
  50856. rotationOriginX: Math.floor(-width / 2),
  50857. rotationOriginY: (height + width) / 2
  50858. });
  50859. }
  50860. // zIndex = 6 for right handle, 7 for left.
  50861. // Can't be 10, because of the tooltip in inverted chart #2908
  50862. navigator.handles[index].attr({ zIndex: 7 - index })
  50863. .addClass('highcharts-navigator-handle ' +
  50864. 'highcharts-navigator-handle-' +
  50865. ['left', 'right'][index]).add(navigatorGroup);
  50866. if (!chart.styledMode) {
  50867. navigator.handles[index]
  50868. .attr({
  50869. fill: handlesOptions.backgroundColor,
  50870. stroke: handlesOptions.borderColor,
  50871. 'stroke-width': handlesOptions.lineWidth
  50872. })
  50873. .css(mouseCursor);
  50874. }
  50875. });
  50876. }
  50877. }
  50878. /**
  50879. * Update navigator
  50880. *
  50881. * @private
  50882. * @function Highcharts.Navigator#update
  50883. *
  50884. * @param {Highcharts.NavigatorOptions} options
  50885. * Options to merge in when updating navigator
  50886. */
  50887. update(options) {
  50888. // Remove references to old navigator series in base series
  50889. (this.series || []).forEach((series) => {
  50890. if (series.baseSeries) {
  50891. delete series.baseSeries.navigatorSeries;
  50892. }
  50893. });
  50894. // Destroy and rebuild navigator
  50895. this.destroy();
  50896. const chartOptions = this.chart.options;
  50897. merge(true, chartOptions.navigator, options);
  50898. this.init(this.chart);
  50899. }
  50900. /**
  50901. * Render the navigator
  50902. *
  50903. * @private
  50904. * @function Highcharts.Navigator#render
  50905. * @param {number} min
  50906. * X axis value minimum
  50907. * @param {number} max
  50908. * X axis value maximum
  50909. * @param {number} [pxMin]
  50910. * Pixel value minimum
  50911. * @param {number} [pxMax]
  50912. * Pixel value maximum
  50913. */
  50914. render(min, max, pxMin, pxMax) {
  50915. const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, pointRange = xAxis.pointRange || 0, scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis, navigatorEnabled = navigator.navigatorEnabled, rendered = navigator.rendered, inverted = chart.inverted, minRange = chart.xAxis[0].minRange, maxRange = chart.xAxis[0].options.maxRange, scrollButtonSize = navigator.scrollButtonSize;
  50916. let navigatorWidth, scrollbarLeft, scrollbarTop, scrollbarHeight = navigator.scrollbarHeight, navigatorSize, verb;
  50917. // Don't redraw while moving the handles (#4703).
  50918. if (this.hasDragged && !defined(pxMin)) {
  50919. return;
  50920. }
  50921. min = correctFloat(min - pointRange / 2);
  50922. max = correctFloat(max + pointRange / 2);
  50923. // Don't render the navigator until we have data (#486, #4202, #5172).
  50924. if (!isNumber(min) || !isNumber(max)) {
  50925. // However, if navigator was already rendered, we may need to resize
  50926. // it. For example hidden series, but visible navigator (#6022).
  50927. if (rendered) {
  50928. pxMin = 0;
  50929. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  50930. }
  50931. else {
  50932. return;
  50933. }
  50934. }
  50935. navigator.left = pick(xAxis.left,
  50936. // in case of scrollbar only, without navigator
  50937. chart.plotLeft + scrollButtonSize +
  50938. (inverted ? chart.plotWidth : 0));
  50939. let zoomedMax = navigator.size = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  50940. 2 * scrollButtonSize);
  50941. if (inverted) {
  50942. navigatorWidth = scrollbarHeight;
  50943. }
  50944. else {
  50945. navigatorWidth = navigatorSize + 2 * scrollButtonSize;
  50946. }
  50947. // Get the pixel position of the handles
  50948. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  50949. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  50950. // Verify (#1851, #2238)
  50951. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  50952. pxMin = 0;
  50953. pxMax = navigatorWidth;
  50954. }
  50955. // Are we below the minRange? (#2618, #6191)
  50956. const newMin = xAxis.toValue(pxMin, true), newMax = xAxis.toValue(pxMax, true), currentRange = Math.abs(correctFloat(newMax - newMin));
  50957. if (currentRange < minRange) {
  50958. if (this.grabbedLeft) {
  50959. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  50960. }
  50961. else if (this.grabbedRight) {
  50962. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  50963. }
  50964. }
  50965. else if (defined(maxRange) &&
  50966. correctFloat(currentRange - pointRange) > maxRange) {
  50967. if (this.grabbedLeft) {
  50968. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  50969. }
  50970. else if (this.grabbedRight) {
  50971. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  50972. }
  50973. }
  50974. // Handles are allowed to cross, but never exceed the plot area
  50975. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  50976. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  50977. navigator.zoomedMax - navigator.fixedWidth :
  50978. Math.min(pxMin, pxMax), 0, zoomedMax);
  50979. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  50980. zoomedMax = Math.round(navigator.zoomedMax);
  50981. const zoomedMin = Math.round(navigator.zoomedMin);
  50982. if (navigatorEnabled) {
  50983. navigator.navigatorGroup.attr({
  50984. visibility: 'inherit'
  50985. });
  50986. // Place elements
  50987. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  50988. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  50989. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  50990. if (navigator.navigatorOptions.handles.enabled) {
  50991. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  50992. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  50993. }
  50994. }
  50995. if (navigator.scrollbar) {
  50996. if (inverted) {
  50997. scrollbarTop = navigator.top - scrollButtonSize;
  50998. scrollbarLeft = navigator.left - scrollbarHeight +
  50999. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  51000. // Multiple axes has offsets:
  51001. (scrollbarXAxis.titleOffset || 0) +
  51002. // Self margin from the axis.title
  51003. scrollbarXAxis.axisTitleMargin);
  51004. scrollbarHeight = navigatorSize + 2 * scrollButtonSize;
  51005. }
  51006. else {
  51007. scrollbarTop = navigator.top + (navigatorEnabled ?
  51008. navigator.height :
  51009. -scrollbarHeight);
  51010. scrollbarLeft = navigator.left - scrollButtonSize;
  51011. }
  51012. // Reposition scrollbar
  51013. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  51014. // Keep scale 0-1
  51015. navigator.scrollbar.setRange(
  51016. // Use real value, not rounded because range can be very small
  51017. // (#1716)
  51018. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  51019. }
  51020. navigator.rendered = true;
  51021. }
  51022. /**
  51023. * Set up the mouse and touch events for the navigator
  51024. *
  51025. * @private
  51026. * @function Highcharts.Navigator#addMouseEvents
  51027. */
  51028. addMouseEvents() {
  51029. const navigator = this, chart = navigator.chart, container = chart.container;
  51030. let eventsToUnbind = [], mouseMoveHandler, mouseUpHandler;
  51031. /**
  51032. * Create mouse events' handlers.
  51033. * Make them as separate functions to enable wrapping them:
  51034. */
  51035. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  51036. navigator.onMouseMove(e);
  51037. };
  51038. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  51039. navigator.onMouseUp(e);
  51040. };
  51041. // Add shades and handles mousedown events
  51042. eventsToUnbind = navigator.getPartsEvents('mousedown');
  51043. // Add mouse move and mouseup events. These are bind to doc/container,
  51044. // because Navigator.grabbedSomething flags are stored in mousedown
  51045. // events
  51046. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  51047. // Touch events
  51048. if (hasTouch) {
  51049. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  51050. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  51051. }
  51052. navigator.eventsToUnbind = eventsToUnbind;
  51053. // Data events
  51054. if (navigator.series && navigator.series[0]) {
  51055. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  51056. chart.navigator.modifyNavigatorAxisExtremes();
  51057. }));
  51058. }
  51059. }
  51060. /**
  51061. * Generate events for handles and masks
  51062. *
  51063. * @private
  51064. * @function Highcharts.Navigator#getPartsEvents
  51065. *
  51066. * @param {string} eventName
  51067. * Event name handler, 'mousedown' or 'touchstart'
  51068. *
  51069. * @return {Array<Function>}
  51070. * An array of functions to remove navigator functions from the
  51071. * events again.
  51072. */
  51073. getPartsEvents(eventName) {
  51074. const navigator = this, events = [];
  51075. ['shades', 'handles'].forEach(function (name) {
  51076. navigator[name].forEach(function (navigatorItem, index) {
  51077. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  51078. navigator[name + 'Mousedown'](e, index);
  51079. }));
  51080. });
  51081. });
  51082. return events;
  51083. }
  51084. /**
  51085. * Mousedown on a shaded mask, either:
  51086. *
  51087. * - will be stored for future drag&drop
  51088. *
  51089. * - will directly shift to a new range
  51090. *
  51091. * @private
  51092. * @function Highcharts.Navigator#shadesMousedown
  51093. *
  51094. * @param {Highcharts.PointerEventObject} e
  51095. * Mouse event
  51096. *
  51097. * @param {number} index
  51098. * Index of a mask in Navigator.shades array
  51099. */
  51100. shadesMousedown(e, index) {
  51101. e = this.chart.pointer.normalize(e);
  51102. const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, zoomedMin = navigator.zoomedMin, navigatorSize = navigator.size, range = navigator.range;
  51103. let navigatorPosition = navigator.left, chartX = e.chartX, fixedMax, fixedMin, ext, left;
  51104. // For inverted chart, swap some options:
  51105. if (chart.inverted) {
  51106. chartX = e.chartY;
  51107. navigatorPosition = navigator.top;
  51108. }
  51109. if (index === 1) {
  51110. // Store information for drag&drop
  51111. navigator.grabbedCenter = chartX;
  51112. navigator.fixedWidth = range;
  51113. navigator.dragOffset = chartX - zoomedMin;
  51114. }
  51115. else {
  51116. // Shift the range by clicking on shaded areas
  51117. left = chartX - navigatorPosition - range / 2;
  51118. if (index === 0) {
  51119. left = Math.max(0, left);
  51120. }
  51121. else if (index === 2 && left + range >= navigatorSize) {
  51122. left = navigatorSize - range;
  51123. if (navigator.reversedExtremes) {
  51124. // #7713
  51125. left -= range;
  51126. fixedMin = navigator.getUnionExtremes().dataMin;
  51127. }
  51128. else {
  51129. // #2293, #3543
  51130. fixedMax = navigator.getUnionExtremes().dataMax;
  51131. }
  51132. }
  51133. if (left !== zoomedMin) { // it has actually moved
  51134. navigator.fixedWidth = range; // #1370
  51135. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  51136. if (defined(ext.min)) { // #7411
  51137. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  51138. { trigger: 'navigator' });
  51139. }
  51140. }
  51141. }
  51142. }
  51143. /**
  51144. * Mousedown on a handle mask.
  51145. * Will store necessary information for drag&drop.
  51146. *
  51147. * @private
  51148. * @function Highcharts.Navigator#handlesMousedown
  51149. * @param {Highcharts.PointerEventObject} e
  51150. * Mouse event
  51151. * @param {number} index
  51152. * Index of a handle in Navigator.handles array
  51153. */
  51154. handlesMousedown(e, index) {
  51155. e = this.chart.pointer.normalize(e);
  51156. const navigator = this, chart = navigator.chart, baseXAxis = chart.xAxis[0],
  51157. // For reversed axes, min and max are changed,
  51158. // so the other extreme should be stored
  51159. reverse = navigator.reversedExtremes;
  51160. if (index === 0) {
  51161. // Grab the left handle
  51162. navigator.grabbedLeft = true;
  51163. navigator.otherHandlePos = navigator.zoomedMax;
  51164. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  51165. }
  51166. else {
  51167. // Grab the right handle
  51168. navigator.grabbedRight = true;
  51169. navigator.otherHandlePos = navigator.zoomedMin;
  51170. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  51171. }
  51172. chart.fixedRange = null;
  51173. }
  51174. /**
  51175. * Mouse move event based on x/y mouse position.
  51176. *
  51177. * @private
  51178. * @function Highcharts.Navigator#onMouseMove
  51179. *
  51180. * @param {Highcharts.PointerEventObject} e
  51181. * Mouse event
  51182. */
  51183. onMouseMove(e) {
  51184. const navigator = this, chart = navigator.chart, navigatorSize = navigator.navigatorSize, range = navigator.range, dragOffset = navigator.dragOffset, inverted = chart.inverted;
  51185. let left = navigator.left, chartX;
  51186. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  51187. // the finger down in the center of the scrollbar. This should be
  51188. // ignored.
  51189. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  51190. e = chart.pointer.normalize(e);
  51191. chartX = e.chartX;
  51192. // Swap some options for inverted chart
  51193. if (inverted) {
  51194. left = navigator.top;
  51195. chartX = e.chartY;
  51196. }
  51197. // Drag left handle or top handle
  51198. if (navigator.grabbedLeft) {
  51199. navigator.hasDragged = true;
  51200. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  51201. // Drag right handle or bottom handle
  51202. }
  51203. else if (navigator.grabbedRight) {
  51204. navigator.hasDragged = true;
  51205. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  51206. // Drag scrollbar or open area in navigator
  51207. }
  51208. else if (navigator.grabbedCenter) {
  51209. navigator.hasDragged = true;
  51210. if (chartX < dragOffset) { // outside left
  51211. chartX = dragOffset;
  51212. // outside right
  51213. }
  51214. else if (chartX >
  51215. navigatorSize + dragOffset - range) {
  51216. chartX = navigatorSize + dragOffset - range;
  51217. }
  51218. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  51219. }
  51220. if (navigator.hasDragged &&
  51221. navigator.scrollbar &&
  51222. pick(navigator.scrollbar.options.liveRedraw,
  51223. // By default, don't run live redraw on touch
  51224. // devices or if the chart is in boost.
  51225. !isTouchDevice &&
  51226. !this.chart.boosted)) {
  51227. e.DOMType = e.type;
  51228. setTimeout(function () {
  51229. navigator.onMouseUp(e);
  51230. }, 0);
  51231. }
  51232. }
  51233. }
  51234. /**
  51235. * Mouse up event based on x/y mouse position.
  51236. *
  51237. * @private
  51238. * @function Highcharts.Navigator#onMouseUp
  51239. * @param {Highcharts.PointerEventObject} e
  51240. * Mouse event
  51241. */
  51242. onMouseUp(e) {
  51243. const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, scrollbar = navigator.scrollbar, DOMEvent = e.DOMEvent || e, inverted = chart.inverted, verb = navigator.rendered && !navigator.hasDragged ?
  51244. 'animate' : 'attr';
  51245. let zoomedMax, zoomedMin, unionExtremes, fixedMin, fixedMax, ext;
  51246. if (
  51247. // MouseUp is called for both, navigator and scrollbar (that order),
  51248. // which causes calling afterSetExtremes twice. Prevent first call
  51249. // by checking if scrollbar is going to set new extremes (#6334)
  51250. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  51251. e.trigger === 'scrollbar') {
  51252. unionExtremes = navigator.getUnionExtremes();
  51253. // When dragging one handle, make sure the other one doesn't change
  51254. if (navigator.zoomedMin === navigator.otherHandlePos) {
  51255. fixedMin = navigator.fixedExtreme;
  51256. }
  51257. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  51258. fixedMax = navigator.fixedExtreme;
  51259. }
  51260. // Snap to right edge (#4076)
  51261. if (navigator.zoomedMax === navigator.size) {
  51262. fixedMax = navigator.reversedExtremes ?
  51263. unionExtremes.dataMin :
  51264. unionExtremes.dataMax;
  51265. }
  51266. // Snap to left edge (#7576)
  51267. if (navigator.zoomedMin === 0) {
  51268. fixedMin = navigator.reversedExtremes ?
  51269. unionExtremes.dataMax :
  51270. unionExtremes.dataMin;
  51271. }
  51272. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  51273. if (defined(ext.min)) {
  51274. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  51275. // Run animation when clicking buttons, scrollbar track etc,
  51276. // but not when dragging handles or scrollbar
  51277. navigator.hasDragged ? false : null, {
  51278. trigger: 'navigator',
  51279. triggerOp: 'navigator-drag',
  51280. DOMEvent: DOMEvent // #1838
  51281. });
  51282. }
  51283. }
  51284. if (e.DOMType !== 'mousemove' &&
  51285. e.DOMType !== 'touchmove') {
  51286. navigator.grabbedLeft = navigator.grabbedRight =
  51287. navigator.grabbedCenter = navigator.fixedWidth =
  51288. navigator.fixedExtreme = navigator.otherHandlePos =
  51289. navigator.hasDragged = navigator.dragOffset = null;
  51290. }
  51291. // Update position of navigator shades, outline and handles (#12573)
  51292. if (navigator.navigatorEnabled &&
  51293. isNumber(navigator.zoomedMin) &&
  51294. isNumber(navigator.zoomedMax)) {
  51295. zoomedMin = Math.round(navigator.zoomedMin);
  51296. zoomedMax = Math.round(navigator.zoomedMax);
  51297. if (navigator.shades) {
  51298. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  51299. }
  51300. if (navigator.outline) {
  51301. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  51302. }
  51303. if (navigator.navigatorOptions.handles.enabled &&
  51304. Object.keys(navigator.handles).length ===
  51305. navigator.handles.length) {
  51306. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  51307. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  51308. }
  51309. }
  51310. }
  51311. /**
  51312. * Removes the event handlers attached previously with addEvents.
  51313. *
  51314. * @private
  51315. * @function Highcharts.Navigator#removeEvents
  51316. */
  51317. removeEvents() {
  51318. if (this.eventsToUnbind) {
  51319. this.eventsToUnbind.forEach(function (unbind) {
  51320. unbind();
  51321. });
  51322. this.eventsToUnbind = void 0;
  51323. }
  51324. this.removeBaseSeriesEvents();
  51325. }
  51326. /**
  51327. * Remove data events.
  51328. *
  51329. * @private
  51330. * @function Highcharts.Navigator#removeBaseSeriesEvents
  51331. */
  51332. removeBaseSeriesEvents() {
  51333. const baseSeries = this.baseSeries || [];
  51334. if (this.navigatorEnabled && baseSeries[0]) {
  51335. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  51336. baseSeries.forEach(function (series) {
  51337. removeEvent(series, 'updatedData', this.updatedDataHandler);
  51338. }, this);
  51339. }
  51340. // We only listen for extremes-events on the first baseSeries
  51341. if (baseSeries[0].xAxis) {
  51342. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  51343. }
  51344. }
  51345. }
  51346. /**
  51347. * Initialize the Navigator object
  51348. *
  51349. * @private
  51350. * @function Highcharts.Navigator#init
  51351. */
  51352. init(chart) {
  51353. const chartOptions = chart.options, navigatorOptions = chartOptions.navigator || {}, navigatorEnabled = navigatorOptions.enabled, scrollbarOptions = chartOptions.scrollbar || {}, scrollbarEnabled = scrollbarOptions.enabled, height = navigatorEnabled && navigatorOptions.height || 0, scrollbarHeight = scrollbarEnabled && scrollbarOptions.height || 0, scrollButtonSize = scrollbarOptions.buttonsEnabled && scrollbarHeight || 0;
  51354. this.handles = [];
  51355. this.shades = [];
  51356. this.chart = chart;
  51357. this.setBaseSeries();
  51358. this.height = height;
  51359. this.scrollbarHeight = scrollbarHeight;
  51360. this.scrollButtonSize = scrollButtonSize;
  51361. this.scrollbarEnabled = scrollbarEnabled;
  51362. this.navigatorEnabled = navigatorEnabled;
  51363. this.navigatorOptions = navigatorOptions;
  51364. this.scrollbarOptions = scrollbarOptions;
  51365. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  51366. const navigator = this, baseSeries = navigator.baseSeries, xAxisIndex = chart.xAxis.length, yAxisIndex = chart.yAxis.length, baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  51367. chart.xAxis[0] || { options: {} };
  51368. chart.isDirtyBox = true;
  51369. if (navigator.navigatorEnabled) {
  51370. // an x axis is required for scrollbar also
  51371. navigator.xAxis = new Axis(chart, merge({
  51372. // inherit base xAxis' break and ordinal options
  51373. breaks: baseXaxis.options.breaks,
  51374. ordinal: baseXaxis.options.ordinal
  51375. }, navigatorOptions.xAxis, {
  51376. id: 'navigator-x-axis',
  51377. yAxis: 'navigator-y-axis',
  51378. type: 'datetime',
  51379. index: xAxisIndex,
  51380. isInternal: true,
  51381. offset: 0,
  51382. keepOrdinalPadding: true,
  51383. startOnTick: false,
  51384. endOnTick: false,
  51385. minPadding: 0,
  51386. maxPadding: 0,
  51387. zoomEnabled: false
  51388. }, chart.inverted ? {
  51389. offsets: [scrollButtonSize, 0, -scrollButtonSize, 0],
  51390. width: height
  51391. } : {
  51392. offsets: [0, -scrollButtonSize, 0, scrollButtonSize],
  51393. height: height
  51394. }), 'xAxis');
  51395. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  51396. id: 'navigator-y-axis',
  51397. alignTicks: false,
  51398. offset: 0,
  51399. index: yAxisIndex,
  51400. isInternal: true,
  51401. reversed: pick((navigatorOptions.yAxis &&
  51402. navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),
  51403. zoomEnabled: false
  51404. }, chart.inverted ? {
  51405. width: height
  51406. } : {
  51407. height: height
  51408. }), 'yAxis');
  51409. // If we have a base series, initialize the navigator series
  51410. if (baseSeries || navigatorOptions.series.data) {
  51411. navigator.updateNavigatorSeries(false);
  51412. // If not, set up an event to listen for added series
  51413. }
  51414. else if (chart.series.length === 0) {
  51415. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  51416. // We've got one, now add it as base
  51417. if (chart.series.length > 0 && !navigator.series) {
  51418. navigator.setBaseSeries();
  51419. navigator.unbindRedraw(); // reset
  51420. }
  51421. });
  51422. }
  51423. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  51424. // Render items, so we can bind events to them:
  51425. navigator.renderElements();
  51426. // Add mouse events
  51427. navigator.addMouseEvents();
  51428. // in case of scrollbar only, fake an x axis to get translation
  51429. }
  51430. else {
  51431. navigator.xAxis = {
  51432. chart,
  51433. navigatorAxis: {
  51434. fake: true
  51435. },
  51436. translate: function (value, reverse) {
  51437. const axis = chart.xAxis[0], ext = axis.getExtremes(), scrollTrackWidth = axis.len - 2 * scrollButtonSize, min = numExt('min', axis.options.min, ext.dataMin), valueRange = numExt('max', axis.options.max, ext.dataMax) - min;
  51438. return reverse ?
  51439. // from pixel to value
  51440. (value * valueRange / scrollTrackWidth) + min :
  51441. // from value to pixel
  51442. scrollTrackWidth * (value - min) / valueRange;
  51443. },
  51444. toPixels: function (value) {
  51445. return this.translate(value);
  51446. },
  51447. toValue: function (value) {
  51448. return this.translate(value, true);
  51449. }
  51450. };
  51451. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  51452. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxisAdditions.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  51453. }
  51454. // Initialize the scrollbar
  51455. if (chart.options.scrollbar.enabled) {
  51456. const options = merge(chart.options.scrollbar, { vertical: chart.inverted });
  51457. if (!isNumber(options.margin) && navigator.navigatorEnabled) {
  51458. options.margin = chart.inverted ? -3 : 3;
  51459. }
  51460. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, options, chart);
  51461. addEvent(navigator.scrollbar, 'changed', function (e) {
  51462. const range = navigator.size, to = range * this.to, from = range * this.from;
  51463. navigator.hasDragged = navigator.scrollbar.hasDragged;
  51464. navigator.render(0, 0, from, to);
  51465. if (this.shouldUpdateExtremes(e.DOMType)) {
  51466. setTimeout(function () {
  51467. navigator.onMouseUp(e);
  51468. });
  51469. }
  51470. });
  51471. }
  51472. // Add data events
  51473. navigator.addBaseSeriesEvents();
  51474. // Add redraw events
  51475. navigator.addChartEvents();
  51476. }
  51477. /**
  51478. * Get the union data extremes of the chart - the outer data extremes of the
  51479. * base X axis and the navigator axis.
  51480. *
  51481. * @private
  51482. * @function Highcharts.Navigator#getUnionExtremes
  51483. */
  51484. getUnionExtremes(returnFalseOnNoBaseSeries) {
  51485. const baseAxis = this.chart.xAxis[0], navAxis = this.xAxis, navAxisOptions = navAxis.options, baseAxisOptions = baseAxis.options;
  51486. let ret;
  51487. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  51488. ret = {
  51489. dataMin: pick(// #4053
  51490. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  51491. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  51492. };
  51493. }
  51494. return ret;
  51495. }
  51496. /**
  51497. * Set the base series and update the navigator series from this. With a bit
  51498. * of modification we should be able to make this an API method to be called
  51499. * from the outside
  51500. *
  51501. * @private
  51502. * @function Highcharts.Navigator#setBaseSeries
  51503. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  51504. * Additional series options for a navigator
  51505. * @param {boolean} [redraw]
  51506. * Whether to redraw after update.
  51507. */
  51508. setBaseSeries(baseSeriesOptions, redraw) {
  51509. const chart = this.chart, baseSeries = this.baseSeries = [];
  51510. baseSeriesOptions = (baseSeriesOptions ||
  51511. chart.options && chart.options.navigator.baseSeries ||
  51512. (chart.series.length ?
  51513. // Find the first non-navigator series (#8430)
  51514. find(chart.series, (s) => (!s.options.isInternal)).index :
  51515. 0));
  51516. // Iterate through series and add the ones that should be shown in
  51517. // navigator.
  51518. (chart.series || []).forEach((series, i) => {
  51519. if (
  51520. // Don't include existing nav series
  51521. !series.options.isInternal &&
  51522. (series.options.showInNavigator ||
  51523. (i === baseSeriesOptions ||
  51524. series.options.id === baseSeriesOptions) &&
  51525. series.options.showInNavigator !== false)) {
  51526. baseSeries.push(series);
  51527. }
  51528. });
  51529. // When run after render, this.xAxis already exists
  51530. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  51531. this.updateNavigatorSeries(true, redraw);
  51532. }
  51533. }
  51534. /**
  51535. * Update series in the navigator from baseSeries, adding new if does not
  51536. * exist.
  51537. *
  51538. * @private
  51539. * @function Highcharts.Navigator.updateNavigatorSeries
  51540. */
  51541. updateNavigatorSeries(addEvents, redraw) {
  51542. const navigator = this, chart = navigator.chart, baseSeries = navigator.baseSeries, navSeriesMixin = {
  51543. enableMouseTracking: false,
  51544. index: null,
  51545. linkedTo: null,
  51546. group: 'nav',
  51547. padXAxis: false,
  51548. xAxis: 'navigator-x-axis',
  51549. yAxis: 'navigator-y-axis',
  51550. showInLegend: false,
  51551. stacking: void 0,
  51552. isInternal: true,
  51553. states: {
  51554. inactive: {
  51555. opacity: 1
  51556. }
  51557. }
  51558. },
  51559. // Remove navigator series that are no longer in the baseSeries
  51560. navigatorSeries = navigator.series =
  51561. (navigator.series || []).filter((navSeries) => {
  51562. const base = navSeries.baseSeries;
  51563. if (baseSeries.indexOf(base) < 0) { // Not in array
  51564. // If there is still a base series connected to this
  51565. // series, remove event handler and reference.
  51566. if (base) {
  51567. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  51568. delete base.navigatorSeries;
  51569. }
  51570. // Kill the nav series. It may already have been
  51571. // destroyed (#8715).
  51572. if (navSeries.chart) {
  51573. navSeries.destroy();
  51574. }
  51575. return false;
  51576. }
  51577. return true;
  51578. });
  51579. let baseOptions, mergedNavSeriesOptions, chartNavigatorSeriesOptions = navigator.navigatorOptions.series, baseNavigatorOptions;
  51580. // Go through each base series and merge the options to create new
  51581. // series
  51582. if (baseSeries && baseSeries.length) {
  51583. baseSeries.forEach((base) => {
  51584. const linkedNavSeries = base.navigatorSeries, userNavOptions = extend(
  51585. // Grab color and visibility from base as default
  51586. {
  51587. color: base.color,
  51588. visible: base.visible
  51589. }, !isArray(chartNavigatorSeriesOptions) ?
  51590. chartNavigatorSeriesOptions :
  51591. defaultOptions.navigator.series);
  51592. // Don't update if the series exists in nav and we have disabled
  51593. // adaptToUpdatedData.
  51594. if (linkedNavSeries &&
  51595. navigator.navigatorOptions.adaptToUpdatedData === false) {
  51596. return;
  51597. }
  51598. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  51599. baseOptions = base.options || {};
  51600. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  51601. // The dataLabels options are not merged correctly
  51602. // if the settings are an array, #13847.
  51603. userNavOptions.dataLabels = splat(userNavOptions.dataLabels);
  51604. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  51605. // Once nav series type is resolved, pick correct pointRange
  51606. mergedNavSeriesOptions.pointRange = pick(
  51607. // Stricte set pointRange in options
  51608. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  51609. // Fallback to default values, e.g. `null` for column
  51610. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  51611. // Merge data separately. Do a slice to avoid mutating the
  51612. // navigator options from base series (#4923).
  51613. const navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  51614. navigator.hasNavigatorData =
  51615. navigator.hasNavigatorData || !!navigatorSeriesData;
  51616. mergedNavSeriesOptions.data =
  51617. navigatorSeriesData ||
  51618. baseOptions.data && baseOptions.data.slice(0);
  51619. // Update or add the series
  51620. if (linkedNavSeries && linkedNavSeries.options) {
  51621. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  51622. }
  51623. else {
  51624. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  51625. base.navigatorSeries.baseSeries = base; // Store ref
  51626. navigatorSeries.push(base.navigatorSeries);
  51627. }
  51628. });
  51629. }
  51630. // If user has defined data (and no base series) or explicitly defined
  51631. // navigator.series as an array, we create these series on top of any
  51632. // base series.
  51633. if (chartNavigatorSeriesOptions.data &&
  51634. !(baseSeries && baseSeries.length) ||
  51635. isArray(chartNavigatorSeriesOptions)) {
  51636. navigator.hasNavigatorData = false;
  51637. // Allow navigator.series to be an array
  51638. chartNavigatorSeriesOptions =
  51639. splat(chartNavigatorSeriesOptions);
  51640. chartNavigatorSeriesOptions.forEach((userSeriesOptions, i) => {
  51641. navSeriesMixin.name =
  51642. 'Navigator ' + (navigatorSeries.length + 1);
  51643. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  51644. // Since we don't have a base series to pull color from,
  51645. // try to fake it by using color from series with same
  51646. // index. Otherwise pull from the colors array. We need
  51647. // an explicit color as otherwise updates will increment
  51648. // color counter and we'll get a new color for each
  51649. // update of the nav series.
  51650. color: chart.series[i] &&
  51651. !chart.series[i].options.isInternal &&
  51652. chart.series[i].color ||
  51653. chart.options.colors[i] ||
  51654. chart.options.colors[0]
  51655. }, navSeriesMixin, userSeriesOptions);
  51656. mergedNavSeriesOptions.data = userSeriesOptions.data;
  51657. if (mergedNavSeriesOptions.data) {
  51658. navigator.hasNavigatorData = true;
  51659. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  51660. }
  51661. });
  51662. }
  51663. if (addEvents) {
  51664. this.addBaseSeriesEvents();
  51665. }
  51666. }
  51667. /**
  51668. * Add data events.
  51669. * For example when main series is updated we need to recalculate extremes
  51670. *
  51671. * @private
  51672. * @function Highcharts.Navigator#addBaseSeriesEvent
  51673. */
  51674. addBaseSeriesEvents() {
  51675. const navigator = this, baseSeries = navigator.baseSeries || [];
  51676. // Bind modified extremes event to first base's xAxis only.
  51677. // In event of > 1 base-xAxes, the navigator will ignore those.
  51678. // Adding this multiple times to the same axis is no problem, as
  51679. // duplicates should be discarded by the browser.
  51680. if (baseSeries[0] && baseSeries[0].xAxis) {
  51681. baseSeries[0].eventsToUnbind.push(addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes));
  51682. }
  51683. baseSeries.forEach((base) => {
  51684. // Link base series show/hide to navigator series visibility
  51685. base.eventsToUnbind.push(addEvent(base, 'show', function () {
  51686. if (this.navigatorSeries) {
  51687. this.navigatorSeries.setVisible(true, false);
  51688. }
  51689. }));
  51690. base.eventsToUnbind.push(addEvent(base, 'hide', function () {
  51691. if (this.navigatorSeries) {
  51692. this.navigatorSeries.setVisible(false, false);
  51693. }
  51694. }));
  51695. // Respond to updated data in the base series, unless explicitily
  51696. // not adapting to data changes.
  51697. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  51698. if (base.xAxis) {
  51699. base.eventsToUnbind.push(addEvent(base, 'updatedData', this.updatedDataHandler));
  51700. }
  51701. }
  51702. // Handle series removal
  51703. base.eventsToUnbind.push(addEvent(base, 'remove', function () {
  51704. if (this.navigatorSeries) {
  51705. erase(navigator.series, this.navigatorSeries);
  51706. if (defined(this.navigatorSeries.options)) {
  51707. this.navigatorSeries.remove(false);
  51708. }
  51709. delete this.navigatorSeries;
  51710. }
  51711. }));
  51712. });
  51713. }
  51714. /**
  51715. * Get minimum from all base series connected to the navigator
  51716. * @private
  51717. * @param {number} currentSeriesMin
  51718. * Minium from the current series
  51719. * @return {number}
  51720. * Minimum from all series
  51721. */
  51722. getBaseSeriesMin(currentSeriesMin) {
  51723. return this.baseSeries.reduce(function (min, series) {
  51724. // (#10193)
  51725. return Math.min(min, series.xData && series.xData.length ?
  51726. series.xData[0] : min);
  51727. }, currentSeriesMin);
  51728. }
  51729. /**
  51730. * Set the navigator x axis extremes to reflect the total. The navigator
  51731. * extremes should always be the extremes of the union of all series in the
  51732. * chart as well as the navigator series.
  51733. *
  51734. * @private
  51735. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  51736. */
  51737. modifyNavigatorAxisExtremes() {
  51738. const xAxis = this.xAxis;
  51739. if (typeof xAxis.getExtremes !== 'undefined') {
  51740. const unionExtremes = this.getUnionExtremes(true);
  51741. if (unionExtremes &&
  51742. (unionExtremes.dataMin !== xAxis.min ||
  51743. unionExtremes.dataMax !== xAxis.max)) {
  51744. xAxis.min = unionExtremes.dataMin;
  51745. xAxis.max = unionExtremes.dataMax;
  51746. }
  51747. }
  51748. }
  51749. /**
  51750. * Hook to modify the base axis extremes with information from the Navigator
  51751. *
  51752. * @private
  51753. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  51754. */
  51755. modifyBaseAxisExtremes() {
  51756. const baseXAxis = this, navigator = baseXAxis.chart.navigator, baseExtremes = baseXAxis.getExtremes(), baseMin = baseExtremes.min, baseMax = baseExtremes.max, baseDataMin = baseExtremes.dataMin, baseDataMax = baseExtremes.dataMax, range = baseMax - baseMin, stickToMin = navigator.stickToMin, stickToMax = navigator.stickToMax, overscroll = pick(baseXAxis.options.overscroll, 0), navigatorSeries = navigator.series && navigator.series[0], hasSetExtremes = !!baseXAxis.setExtremes,
  51757. // When the extremes have been set by range selector button, don't
  51758. // stick to min or max. The range selector buttons will handle the
  51759. // extremes. (#5489)
  51760. unmutable = baseXAxis.eventArgs &&
  51761. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  51762. let newMax, newMin;
  51763. if (!unmutable) {
  51764. // If the zoomed range is already at the min, move it to the right
  51765. // as new data comes in
  51766. if (stickToMin) {
  51767. newMin = baseDataMin;
  51768. newMax = newMin + range;
  51769. }
  51770. // If the zoomed range is already at the max, move it to the right
  51771. // as new data comes in
  51772. if (stickToMax) {
  51773. newMax = baseDataMax + overscroll;
  51774. // If stickToMin is true, the new min value is set above
  51775. if (!stickToMin) {
  51776. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  51777. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  51778. navigatorSeries.xData[0] :
  51779. -Number.MAX_VALUE));
  51780. }
  51781. }
  51782. // Update the extremes
  51783. if (hasSetExtremes && (stickToMin || stickToMax)) {
  51784. if (isNumber(newMin)) {
  51785. baseXAxis.min = baseXAxis.userMin = newMin;
  51786. baseXAxis.max = baseXAxis.userMax = newMax;
  51787. }
  51788. }
  51789. }
  51790. // Reset
  51791. navigator.stickToMin =
  51792. navigator.stickToMax = null;
  51793. }
  51794. /**
  51795. * Handler for updated data on the base series. When data is modified, the
  51796. * navigator series must reflect it. This is called from the Chart.redraw
  51797. * function before axis and series extremes are computed.
  51798. *
  51799. * @private
  51800. * @function Highcharts.Navigator#updateDataHandler
  51801. */
  51802. updatedDataHandler() {
  51803. const navigator = this.chart.navigator, baseSeries = this, navigatorSeries = this.navigatorSeries, shouldStickToMax = navigator.reversedExtremes ?
  51804. Math.round(navigator.zoomedMin) === 0 :
  51805. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  51806. // If the scrollbar is scrolled all the way to the right, keep right as
  51807. // new data comes in, unless user set navigator.stickToMax to false.
  51808. navigator.stickToMax = pick(this.chart.options.navigator &&
  51809. this.chart.options.navigator.stickToMax, shouldStickToMax);
  51810. navigator.stickToMin = navigator.shouldStickToMin(baseSeries, navigator);
  51811. // Set the navigator series data to the new data of the base series
  51812. if (navigatorSeries && !navigator.hasNavigatorData) {
  51813. navigatorSeries.options.pointStart = baseSeries.xData[0];
  51814. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  51815. }
  51816. }
  51817. /**
  51818. * Detect if the zoomed area should stick to the minimum, #14742.
  51819. *
  51820. * @private
  51821. * @function Highcharts.Navigator#shouldStickToMin
  51822. */
  51823. shouldStickToMin(baseSeries, navigator) {
  51824. const xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]), xAxis = baseSeries.xAxis, max = xAxis.max, min = xAxis.min, range = xAxis.options.range;
  51825. let stickToMin = true;
  51826. if (isNumber(max) && isNumber(min)) {
  51827. // If range declared, stick to the minimum only if the range
  51828. // is smaller than the data set range.
  51829. if (range && max - xDataMin > 0) {
  51830. stickToMin = max - xDataMin < range;
  51831. }
  51832. else {
  51833. // If the current axis minimum falls outside the new
  51834. // updated dataset, we must adjust.
  51835. stickToMin = min <= xDataMin;
  51836. }
  51837. }
  51838. else {
  51839. stickToMin = false; // #15864
  51840. }
  51841. return stickToMin;
  51842. }
  51843. /**
  51844. * Add chart events, like redrawing navigator, when chart requires that.
  51845. *
  51846. * @private
  51847. * @function Highcharts.Navigator#addChartEvents
  51848. */
  51849. addChartEvents() {
  51850. if (!this.eventsToUnbind) {
  51851. this.eventsToUnbind = [];
  51852. }
  51853. this.eventsToUnbind.push(
  51854. // Move the scrollbar after redraw, like after data updata even if
  51855. // axes don't redraw
  51856. addEvent(this.chart, 'redraw', function () {
  51857. const navigator = this.navigator, xAxis = navigator && (navigator.baseSeries &&
  51858. navigator.baseSeries[0] &&
  51859. navigator.baseSeries[0].xAxis ||
  51860. this.xAxis[0]); // #5709, #13114
  51861. if (xAxis) {
  51862. navigator.render(xAxis.min, xAxis.max);
  51863. }
  51864. }),
  51865. // Make room for the navigator, can be placed around the chart:
  51866. addEvent(this.chart, 'getMargins', function () {
  51867. let chart = this, navigator = chart.navigator, marginName = navigator.opposite ?
  51868. 'plotTop' : 'marginBottom';
  51869. if (chart.inverted) {
  51870. marginName = navigator.opposite ?
  51871. 'marginRight' : 'plotLeft';
  51872. }
  51873. chart[marginName] =
  51874. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  51875. navigator.height + navigator.scrollbarHeight :
  51876. 0) + navigator.navigatorOptions.margin;
  51877. }));
  51878. }
  51879. /**
  51880. * Destroys allocated elements.
  51881. *
  51882. * @private
  51883. * @function Highcharts.Navigator#destroy
  51884. */
  51885. destroy() {
  51886. // Disconnect events added in addEvents
  51887. this.removeEvents();
  51888. if (this.xAxis) {
  51889. erase(this.chart.xAxis, this.xAxis);
  51890. erase(this.chart.axes, this.xAxis);
  51891. }
  51892. if (this.yAxis) {
  51893. erase(this.chart.yAxis, this.yAxis);
  51894. erase(this.chart.axes, this.yAxis);
  51895. }
  51896. // Destroy series
  51897. (this.series || []).forEach((s) => {
  51898. if (s.destroy) {
  51899. s.destroy();
  51900. }
  51901. });
  51902. // Destroy properties
  51903. [
  51904. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  51905. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  51906. 'rendered'
  51907. ].forEach((prop) => {
  51908. if (this[prop] && this[prop].destroy) {
  51909. this[prop].destroy();
  51910. }
  51911. this[prop] = null;
  51912. });
  51913. // Destroy elements in collection
  51914. [this.handles].forEach((coll) => {
  51915. destroyObjectProperties(coll);
  51916. });
  51917. }
  51918. }
  51919. /* *
  51920. *
  51921. * Default Export
  51922. *
  51923. * */
  51924. return Navigator;
  51925. });
  51926. _registerModule(_modules, 'Stock/RangeSelector/RangeSelectorDefaults.js', [], function () {
  51927. /* *
  51928. *
  51929. * (c) 2010-2021 Torstein Honsi
  51930. *
  51931. * License: www.highcharts.com/license
  51932. *
  51933. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  51934. *
  51935. * */
  51936. /* *
  51937. *
  51938. * Declarations
  51939. *
  51940. * */
  51941. /**
  51942. * Language object. The language object is global and it can't be set
  51943. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  51944. * set it before any chart is initialized.
  51945. *
  51946. * ```js
  51947. * Highcharts.setOptions({
  51948. * lang: {
  51949. * months: [
  51950. * 'Janvier', 'Février', 'Mars', 'Avril',
  51951. * 'Mai', 'Juin', 'Juillet', 'Août',
  51952. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  51953. * ],
  51954. * weekdays: [
  51955. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  51956. * 'Jeudi', 'Vendredi', 'Samedi'
  51957. * ]
  51958. * }
  51959. * });
  51960. * ```
  51961. *
  51962. * @optionparent lang
  51963. */
  51964. const lang = {
  51965. /**
  51966. * The text for the label for the range selector buttons.
  51967. *
  51968. * @product highstock gantt
  51969. */
  51970. rangeSelectorZoom: 'Zoom',
  51971. /**
  51972. * The text for the label for the "from" input box in the range
  51973. * selector. Since v9.0, this string is empty as the label is not
  51974. * rendered by default.
  51975. *
  51976. * @product highstock gantt
  51977. */
  51978. rangeSelectorFrom: '',
  51979. /**
  51980. * The text for the label for the "to" input box in the range selector.
  51981. *
  51982. * @product highstock gantt
  51983. */
  51984. rangeSelectorTo: '→'
  51985. };
  51986. /**
  51987. * The range selector is a tool for selecting ranges to display within
  51988. * the chart. It provides buttons to select preconfigured ranges in
  51989. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  51990. * boxes where min and max dates can be manually input.
  51991. *
  51992. * @product highstock gantt
  51993. * @optionparent rangeSelector
  51994. */
  51995. const rangeSelector = {
  51996. /**
  51997. * Whether to enable all buttons from the start. By default buttons are
  51998. * only enabled if the corresponding time range exists on the X axis,
  51999. * but enabling all buttons allows for dynamically loading different
  52000. * time ranges.
  52001. *
  52002. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  52003. * All buttons enabled
  52004. *
  52005. * @since 2.0.3
  52006. */
  52007. allButtonsEnabled: false,
  52008. /**
  52009. * An array of configuration objects for the buttons.
  52010. *
  52011. * Defaults to:
  52012. * ```js
  52013. * buttons: [{
  52014. * type: 'month',
  52015. * count: 1,
  52016. * text: '1m',
  52017. * title: 'View 1 month'
  52018. * }, {
  52019. * type: 'month',
  52020. * count: 3,
  52021. * text: '3m',
  52022. * title: 'View 3 months'
  52023. * }, {
  52024. * type: 'month',
  52025. * count: 6,
  52026. * text: '6m',
  52027. * title: 'View 6 months'
  52028. * }, {
  52029. * type: 'ytd',
  52030. * text: 'YTD',
  52031. * title: 'View year to date'
  52032. * }, {
  52033. * type: 'year',
  52034. * count: 1,
  52035. * text: '1y',
  52036. * title: 'View 1 year'
  52037. * }, {
  52038. * type: 'all',
  52039. * text: 'All',
  52040. * title: 'View all'
  52041. * }]
  52042. * ```
  52043. *
  52044. * @sample {highstock} stock/rangeselector/datagrouping/
  52045. * Data grouping by buttons
  52046. *
  52047. * @type {Array<*>}
  52048. */
  52049. buttons: void 0,
  52050. /**
  52051. * How many units of the defined type the button should span. If `type`
  52052. * is "month" and `count` is 3, the button spans three months.
  52053. *
  52054. * @type {number}
  52055. * @default 1
  52056. * @apioption rangeSelector.buttons.count
  52057. */
  52058. /**
  52059. * Fires when clicking on the rangeSelector button. One parameter,
  52060. * event, is passed to the function, containing common event
  52061. * information.
  52062. *
  52063. * ```js
  52064. * click: function(e) {
  52065. * console.log(this);
  52066. * }
  52067. * ```
  52068. *
  52069. * Return false to stop default button's click action.
  52070. *
  52071. * @sample {highstock} stock/rangeselector/button-click/
  52072. * Click event on the button
  52073. *
  52074. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  52075. * @apioption rangeSelector.buttons.events.click
  52076. */
  52077. /**
  52078. * Additional range (in milliseconds) added to the end of the calculated
  52079. * time span.
  52080. *
  52081. * @sample {highstock} stock/rangeselector/min-max-offsets/
  52082. * Button offsets
  52083. *
  52084. * @type {number}
  52085. * @default 0
  52086. * @since 6.0.0
  52087. * @apioption rangeSelector.buttons.offsetMax
  52088. */
  52089. /**
  52090. * Additional range (in milliseconds) added to the start of the
  52091. * calculated time span.
  52092. *
  52093. * @sample {highstock} stock/rangeselector/min-max-offsets/
  52094. * Button offsets
  52095. *
  52096. * @type {number}
  52097. * @default 0
  52098. * @since 6.0.0
  52099. * @apioption rangeSelector.buttons.offsetMin
  52100. */
  52101. /**
  52102. * When buttons apply dataGrouping on a series, by default zooming
  52103. * in/out will deselect buttons and unset dataGrouping. Enable this
  52104. * option to keep buttons selected when extremes change.
  52105. *
  52106. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  52107. * Different preserveDataGrouping settings
  52108. *
  52109. * @type {boolean}
  52110. * @default false
  52111. * @since 6.1.2
  52112. * @apioption rangeSelector.buttons.preserveDataGrouping
  52113. */
  52114. /**
  52115. * A custom data grouping object for each button.
  52116. *
  52117. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  52118. *
  52119. * @sample {highstock} stock/rangeselector/datagrouping/
  52120. * Data grouping by range selector buttons
  52121. *
  52122. * @type {*}
  52123. * @extends plotOptions.series.dataGrouping
  52124. * @apioption rangeSelector.buttons.dataGrouping
  52125. */
  52126. /**
  52127. * The text for the button itself.
  52128. *
  52129. * @type {string}
  52130. * @apioption rangeSelector.buttons.text
  52131. */
  52132. /**
  52133. * Explanation for the button, shown as a tooltip on hover, and used by
  52134. * assistive technology.
  52135. *
  52136. * @type {string}
  52137. * @apioption rangeSelector.buttons.title
  52138. */
  52139. /**
  52140. * Defined the time span for the button. Can be one of `millisecond`,
  52141. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  52142. * and `all`.
  52143. *
  52144. * @type {Highcharts.RangeSelectorButtonTypeValue}
  52145. * @apioption rangeSelector.buttons.type
  52146. */
  52147. /**
  52148. * The space in pixels between the buttons in the range selector.
  52149. */
  52150. buttonSpacing: 5,
  52151. /**
  52152. * Whether to collapse the range selector buttons into a dropdown when
  52153. * there is not enough room to show everything in a single row, instead
  52154. * of dividing the range selector into multiple rows.
  52155. * Can be one of the following:
  52156. * - `always`: Always collapse
  52157. * - `responsive`: Only collapse when there is not enough room
  52158. * - `never`: Never collapse
  52159. *
  52160. * @sample {highstock} stock/rangeselector/dropdown/
  52161. * Dropdown option
  52162. *
  52163. * @validvalue ["always", "responsive", "never"]
  52164. * @since 9.0.0
  52165. */
  52166. dropdown: 'responsive',
  52167. /**
  52168. * Enable or disable the range selector. Default to `true` for stock
  52169. * charts, using the `stockChart` factory.
  52170. *
  52171. * @sample {highstock} stock/rangeselector/enabled/
  52172. * Disable the range selector
  52173. *
  52174. * @type {boolean|undefined}
  52175. * @default {highstock} true
  52176. */
  52177. enabled: void 0,
  52178. /**
  52179. * The vertical alignment of the rangeselector box. Allowed properties
  52180. * are `top`, `middle`, `bottom`.
  52181. *
  52182. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  52183. * Middle
  52184. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  52185. * Bottom
  52186. *
  52187. * @type {Highcharts.VerticalAlignValue}
  52188. * @since 6.0.0
  52189. */
  52190. verticalAlign: 'top',
  52191. /**
  52192. * A collection of attributes for the buttons. The object takes SVG
  52193. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  52194. * a collection of CSS properties for the text.
  52195. *
  52196. * The object can also be extended with states, so you can set
  52197. * presentational options for `hover`, `select` or `disabled` button
  52198. * states.
  52199. *
  52200. * CSS styles for the text label.
  52201. *
  52202. * In styled mode, the buttons are styled by the
  52203. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  52204. * different states.
  52205. *
  52206. * @sample {highstock} stock/rangeselector/styling/
  52207. * Styling the buttons and inputs
  52208. *
  52209. * @type {Highcharts.SVGAttributes}
  52210. */
  52211. buttonTheme: {
  52212. /** @ignore */
  52213. width: 28,
  52214. /** @ignore */
  52215. height: 18,
  52216. /** @ignore */
  52217. padding: 2,
  52218. /** @ignore */
  52219. zIndex: 7 // #484, #852
  52220. },
  52221. /**
  52222. * When the rangeselector is floating, the plot area does not reserve
  52223. * space for it. This opens for positioning anywhere on the chart.
  52224. *
  52225. * @sample {highstock} stock/rangeselector/floating/
  52226. * Placing the range selector between the plot area and the
  52227. * navigator
  52228. *
  52229. * @since 6.0.0
  52230. */
  52231. floating: false,
  52232. /**
  52233. * The x offset of the range selector relative to its horizontal
  52234. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  52235. *
  52236. * @since 6.0.0
  52237. */
  52238. x: 0,
  52239. /**
  52240. * The y offset of the range selector relative to its horizontal
  52241. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  52242. *
  52243. * @since 6.0.0
  52244. */
  52245. y: 0,
  52246. /**
  52247. * Deprecated. The height of the range selector. Currently it is
  52248. * calculated dynamically.
  52249. *
  52250. * @deprecated
  52251. * @type {number|undefined}
  52252. * @since 2.1.9
  52253. */
  52254. height: void 0,
  52255. /**
  52256. * The border color of the date input boxes.
  52257. *
  52258. * @sample {highstock} stock/rangeselector/styling/
  52259. * Styling the buttons and inputs
  52260. *
  52261. * @type {Highcharts.ColorString}
  52262. * @since 1.3.7
  52263. */
  52264. inputBoxBorderColor: 'none',
  52265. /**
  52266. * The pixel height of the date input boxes.
  52267. *
  52268. * @sample {highstock} stock/rangeselector/styling/
  52269. * Styling the buttons and inputs
  52270. *
  52271. * @since 1.3.7
  52272. */
  52273. inputBoxHeight: 17,
  52274. /**
  52275. * The pixel width of the date input boxes. When `undefined`, the width
  52276. * is fitted to the rendered content.
  52277. *
  52278. * @sample {highstock} stock/rangeselector/styling/
  52279. * Styling the buttons and inputs
  52280. *
  52281. * @type {number|undefined}
  52282. * @since 1.3.7
  52283. */
  52284. inputBoxWidth: void 0,
  52285. /**
  52286. * The date format in the input boxes when not selected for editing.
  52287. * Defaults to `%e %b %Y`.
  52288. *
  52289. * This is used to determine which type of input to show,
  52290. * `datetime-local`, `date` or `time` and falling back to `text` when
  52291. * the browser does not support the input type or the format contains
  52292. * milliseconds.
  52293. *
  52294. * @sample {highstock} stock/rangeselector/input-type/
  52295. * Input types
  52296. * @sample {highstock} stock/rangeselector/input-format/
  52297. * Milliseconds in the range selector
  52298. *
  52299. */
  52300. inputDateFormat: '%e %b %Y',
  52301. /**
  52302. * A custom callback function to parse values entered in the input boxes
  52303. * and return a valid JavaScript time as milliseconds since 1970.
  52304. * The first argument passed is a value to parse,
  52305. * second is a boolean indicating use of the UTC time.
  52306. *
  52307. * This will only get called for inputs of type `text`. Since v8.2.3,
  52308. * the input type is dynamically determined based on the granularity
  52309. * of the `inputDateFormat` and the browser support.
  52310. *
  52311. * @sample {highstock} stock/rangeselector/input-format/
  52312. * Milliseconds in the range selector
  52313. *
  52314. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  52315. * @since 1.3.3
  52316. */
  52317. inputDateParser: void 0,
  52318. /**
  52319. * The date format in the input boxes when they are selected for
  52320. * editing. This must be a format that is recognized by JavaScript
  52321. * Date.parse.
  52322. *
  52323. * This will only be used for inputs of type `text`. Since v8.2.3,
  52324. * the input type is dynamically determined based on the granularity
  52325. * of the `inputDateFormat` and the browser support.
  52326. *
  52327. * @sample {highstock} stock/rangeselector/input-format/
  52328. * Milliseconds in the range selector
  52329. *
  52330. */
  52331. inputEditDateFormat: '%Y-%m-%d',
  52332. /**
  52333. * Enable or disable the date input boxes.
  52334. */
  52335. inputEnabled: true,
  52336. /**
  52337. * Positioning for the input boxes. Allowed properties are `align`,
  52338. * `x` and `y`.
  52339. *
  52340. * @since 1.2.4
  52341. */
  52342. inputPosition: {
  52343. /**
  52344. * The alignment of the input box. Allowed properties are `left`,
  52345. * `center`, `right`.
  52346. *
  52347. * @sample {highstock} stock/rangeselector/input-button-position/
  52348. * Alignment
  52349. *
  52350. * @type {Highcharts.AlignValue}
  52351. * @since 6.0.0
  52352. */
  52353. align: 'right',
  52354. /**
  52355. * X offset of the input row.
  52356. */
  52357. x: 0,
  52358. /**
  52359. * Y offset of the input row.
  52360. */
  52361. y: 0
  52362. },
  52363. /**
  52364. * The space in pixels between the labels and the date input boxes in
  52365. * the range selector.
  52366. *
  52367. * @since 9.0.0
  52368. */
  52369. inputSpacing: 5,
  52370. /**
  52371. * The index of the button to appear pre-selected.
  52372. *
  52373. * @type {number}
  52374. */
  52375. selected: void 0,
  52376. /**
  52377. * Positioning for the button row.
  52378. *
  52379. * @since 1.2.4
  52380. */
  52381. buttonPosition: {
  52382. /**
  52383. * The alignment of the input box. Allowed properties are `left`,
  52384. * `center`, `right`.
  52385. *
  52386. * @sample {highstock} stock/rangeselector/input-button-position/
  52387. * Alignment
  52388. *
  52389. * @type {Highcharts.AlignValue}
  52390. * @since 6.0.0
  52391. */
  52392. align: 'left',
  52393. /**
  52394. * X offset of the button row.
  52395. */
  52396. x: 0,
  52397. /**
  52398. * Y offset of the button row.
  52399. */
  52400. y: 0
  52401. },
  52402. /**
  52403. * CSS for the HTML inputs in the range selector.
  52404. *
  52405. * In styled mode, the inputs are styled by the
  52406. * `.highcharts-range-input text` rule in SVG mode, and
  52407. * `input.highcharts-range-selector` when active.
  52408. *
  52409. * @sample {highstock} stock/rangeselector/styling/
  52410. * Styling the buttons and inputs
  52411. *
  52412. * @type {Highcharts.CSSObject}
  52413. * @apioption rangeSelector.inputStyle
  52414. */
  52415. inputStyle: {
  52416. /** @ignore */
  52417. color: "#334eff" /* Palette.highlightColor80 */,
  52418. /** @ignore */
  52419. cursor: 'pointer',
  52420. /** @ignore */
  52421. fontSize: '0.8em'
  52422. },
  52423. /**
  52424. * CSS styles for the labels - the Zoom, From and To texts.
  52425. *
  52426. * In styled mode, the labels are styled by the
  52427. * `.highcharts-range-label` class.
  52428. *
  52429. * @sample {highstock} stock/rangeselector/styling/
  52430. * Styling the buttons and inputs
  52431. *
  52432. * @type {Highcharts.CSSObject}
  52433. */
  52434. labelStyle: {
  52435. /** @ignore */
  52436. color: "#666666" /* Palette.neutralColor60 */,
  52437. /** @ignore */
  52438. fontSize: '0.8em'
  52439. }
  52440. };
  52441. /* *
  52442. *
  52443. * Default Export
  52444. *
  52445. * */
  52446. const RangeSelectorDefaults = {
  52447. lang,
  52448. rangeSelector
  52449. };
  52450. return RangeSelectorDefaults;
  52451. });
  52452. _registerModule(_modules, 'Stock/RangeSelector/RangeSelectorComposition.js', [_modules['Core/Defaults.js'], _modules['Stock/RangeSelector/RangeSelectorDefaults.js'], _modules['Core/Utilities.js']], function (D, RangeSelectorDefaults, U) {
  52453. /* *
  52454. *
  52455. * (c) 2010-2021 Torstein Honsi
  52456. *
  52457. * License: www.highcharts.com/license
  52458. *
  52459. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  52460. *
  52461. * */
  52462. const { defaultOptions, setOptions } = D;
  52463. const { addEvent, defined, extend, find, isNumber, merge, pick } = U;
  52464. /* *
  52465. *
  52466. * Constants
  52467. *
  52468. * */
  52469. const chartDestroyEvents = [];
  52470. const composedMembers = [];
  52471. /* *
  52472. *
  52473. * Variables
  52474. *
  52475. * */
  52476. let RangeSelectorConstructor;
  52477. /* *
  52478. *
  52479. * Functions
  52480. *
  52481. * */
  52482. /**
  52483. * Get the axis min value based on the range option and the current max. For
  52484. * stock charts this is extended via the {@link RangeSelector} so that if the
  52485. * selected range is a multiple of months or years, it is compensated for
  52486. * various month lengths.
  52487. *
  52488. * @private
  52489. * @function Highcharts.Axis#minFromRange
  52490. * @return {number|undefined}
  52491. * The new minimum value.
  52492. */
  52493. function axisMinFromRange() {
  52494. const rangeOptions = this.range, type = rangeOptions.type, max = this.max, time = this.chart.time,
  52495. // Get the true range from a start date
  52496. getTrueRange = function (base, count) {
  52497. const timeName = type === 'year' ?
  52498. 'FullYear' : 'Month';
  52499. const date = new time.Date(base);
  52500. const basePeriod = time.get(timeName, date);
  52501. time.set(timeName, date, basePeriod + count);
  52502. if (basePeriod === time.get(timeName, date)) {
  52503. time.set('Date', date, 0); // #6537
  52504. }
  52505. return date.getTime() - base;
  52506. };
  52507. let min, range;
  52508. if (isNumber(rangeOptions)) {
  52509. min = max - rangeOptions;
  52510. range = rangeOptions;
  52511. }
  52512. else if (rangeOptions) {
  52513. min = max + getTrueRange(max, -(rangeOptions.count || 1));
  52514. // Let the fixedRange reflect initial settings (#5930)
  52515. if (this.chart) {
  52516. this.chart.fixedRange = max - min;
  52517. }
  52518. }
  52519. const dataMin = pick(this.dataMin, Number.MIN_VALUE);
  52520. if (!isNumber(min)) {
  52521. min = dataMin;
  52522. }
  52523. if (min <= dataMin) {
  52524. min = dataMin;
  52525. if (typeof range === 'undefined') { // #4501
  52526. range = getTrueRange(min, rangeOptions.count);
  52527. }
  52528. this.newMax = Math.min(min + range, pick(this.dataMax, Number.MAX_VALUE));
  52529. }
  52530. if (!isNumber(max)) {
  52531. min = void 0;
  52532. }
  52533. else if (!isNumber(rangeOptions) &&
  52534. rangeOptions &&
  52535. rangeOptions._offsetMin) {
  52536. min += rangeOptions._offsetMin;
  52537. }
  52538. return min;
  52539. }
  52540. /**
  52541. * @private
  52542. */
  52543. function compose(AxisClass, ChartClass, RangeSelectorClass) {
  52544. RangeSelectorConstructor = RangeSelectorClass;
  52545. if (U.pushUnique(composedMembers, AxisClass)) {
  52546. AxisClass.prototype.minFromRange = axisMinFromRange;
  52547. }
  52548. if (U.pushUnique(composedMembers, ChartClass)) {
  52549. addEvent(ChartClass, 'afterGetContainer', onChartAfterGetContainer);
  52550. addEvent(ChartClass, 'beforeRender', onChartBeforeRender);
  52551. addEvent(ChartClass, 'destroy', onChartDestroy);
  52552. addEvent(ChartClass, 'getMargins', onChartGetMargins);
  52553. addEvent(ChartClass, 'render', onChartRender);
  52554. addEvent(ChartClass, 'update', onChartUpdate);
  52555. const chartProto = ChartClass.prototype;
  52556. chartProto.callbacks.push(onChartCallback);
  52557. }
  52558. if (U.pushUnique(composedMembers, setOptions)) {
  52559. extend(defaultOptions, { rangeSelector: RangeSelectorDefaults.rangeSelector });
  52560. extend(defaultOptions.lang, RangeSelectorDefaults.lang);
  52561. }
  52562. }
  52563. /**
  52564. * Initialize rangeselector for stock charts
  52565. * @private
  52566. */
  52567. function onChartAfterGetContainer() {
  52568. if (this.options.rangeSelector &&
  52569. this.options.rangeSelector.enabled) {
  52570. this.rangeSelector = new RangeSelectorConstructor(this);
  52571. }
  52572. }
  52573. /**
  52574. * @private
  52575. */
  52576. function onChartBeforeRender() {
  52577. const chart = this, axes = chart.axes, rangeSelector = chart.rangeSelector;
  52578. if (rangeSelector) {
  52579. if (isNumber(rangeSelector.deferredYTDClick)) {
  52580. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  52581. delete rangeSelector.deferredYTDClick;
  52582. }
  52583. axes.forEach((axis) => {
  52584. axis.updateNames();
  52585. axis.setScale();
  52586. });
  52587. chart.getAxisMargins();
  52588. rangeSelector.render();
  52589. const verticalAlign = rangeSelector.options.verticalAlign;
  52590. if (!rangeSelector.options.floating) {
  52591. if (verticalAlign === 'bottom') {
  52592. this.extraBottomMargin = true;
  52593. }
  52594. else if (verticalAlign !== 'middle') {
  52595. this.extraTopMargin = true;
  52596. }
  52597. }
  52598. }
  52599. }
  52600. /**
  52601. * @private
  52602. */
  52603. function onChartCallback(chart) {
  52604. let extremes, legend, alignTo, verticalAlign;
  52605. const rangeSelector = chart.rangeSelector, redraw = () => {
  52606. if (rangeSelector) {
  52607. extremes = chart.xAxis[0].getExtremes();
  52608. legend = chart.legend;
  52609. verticalAlign = (rangeSelector &&
  52610. rangeSelector.options.verticalAlign);
  52611. if (isNumber(extremes.min)) {
  52612. rangeSelector.render(extremes.min, extremes.max);
  52613. }
  52614. // Re-align the legend so that it's below the rangeselector
  52615. if (legend.display &&
  52616. verticalAlign === 'top' &&
  52617. verticalAlign === legend.options.verticalAlign) {
  52618. // Create a new alignment box for the legend.
  52619. alignTo = merge(chart.spacingBox);
  52620. if (legend.options.layout === 'vertical') {
  52621. alignTo.y = chart.plotTop;
  52622. }
  52623. else {
  52624. alignTo.y += rangeSelector.getHeight();
  52625. }
  52626. legend.group.placed = false; // Don't animate the alignment.
  52627. legend.align(alignTo);
  52628. }
  52629. }
  52630. };
  52631. if (rangeSelector) {
  52632. const events = find(chartDestroyEvents, (e) => e[0] === chart);
  52633. if (!events) {
  52634. chartDestroyEvents.push([chart, [
  52635. // redraw the scroller on setExtremes
  52636. addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  52637. if (rangeSelector) {
  52638. rangeSelector.render(e.min, e.max);
  52639. }
  52640. }),
  52641. // redraw the scroller chart resize
  52642. addEvent(chart, 'redraw', redraw)
  52643. ]]);
  52644. }
  52645. // do it now
  52646. redraw();
  52647. }
  52648. }
  52649. /**
  52650. * Remove resize/afterSetExtremes at chart destroy.
  52651. * @private
  52652. */
  52653. function onChartDestroy() {
  52654. for (let i = 0, iEnd = chartDestroyEvents.length; i < iEnd; ++i) {
  52655. const events = chartDestroyEvents[i];
  52656. if (events[0] === this) {
  52657. events[1].forEach((unbind) => unbind());
  52658. chartDestroyEvents.splice(i, 1);
  52659. return;
  52660. }
  52661. }
  52662. }
  52663. function onChartGetMargins() {
  52664. const rangeSelector = this.rangeSelector;
  52665. if (rangeSelector) {
  52666. const rangeSelectorHeight = rangeSelector.getHeight();
  52667. if (this.extraTopMargin) {
  52668. this.plotTop += rangeSelectorHeight;
  52669. }
  52670. if (this.extraBottomMargin) {
  52671. this.marginBottom += rangeSelectorHeight;
  52672. }
  52673. }
  52674. }
  52675. /**
  52676. * @private
  52677. */
  52678. function onChartRender() {
  52679. const chart = this, rangeSelector = chart.rangeSelector;
  52680. if (rangeSelector && !rangeSelector.options.floating) {
  52681. rangeSelector.render();
  52682. const verticalAlign = rangeSelector.options.verticalAlign;
  52683. if (verticalAlign === 'bottom') {
  52684. this.extraBottomMargin = true;
  52685. }
  52686. else if (verticalAlign !== 'middle') {
  52687. this.extraTopMargin = true;
  52688. }
  52689. }
  52690. }
  52691. /**
  52692. * @private
  52693. */
  52694. function onChartUpdate(e) {
  52695. const chart = this, options = e.options, optionsRangeSelector = options.rangeSelector, extraBottomMarginWas = this.extraBottomMargin, extraTopMarginWas = this.extraTopMargin;
  52696. let rangeSelector = chart.rangeSelector;
  52697. if (optionsRangeSelector &&
  52698. optionsRangeSelector.enabled &&
  52699. !defined(rangeSelector) &&
  52700. this.options.rangeSelector) {
  52701. this.options.rangeSelector.enabled = true;
  52702. this.rangeSelector = rangeSelector = new RangeSelectorConstructor(this);
  52703. }
  52704. this.extraBottomMargin = false;
  52705. this.extraTopMargin = false;
  52706. if (rangeSelector) {
  52707. onChartCallback(this);
  52708. const verticalAlign = (optionsRangeSelector &&
  52709. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  52710. if (!rangeSelector.options.floating) {
  52711. if (verticalAlign === 'bottom') {
  52712. this.extraBottomMargin = true;
  52713. }
  52714. else if (verticalAlign !== 'middle') {
  52715. this.extraTopMargin = true;
  52716. }
  52717. }
  52718. if (this.extraBottomMargin !== extraBottomMarginWas ||
  52719. this.extraTopMargin !== extraTopMarginWas) {
  52720. this.isDirtyBox = true;
  52721. }
  52722. }
  52723. }
  52724. /* *
  52725. *
  52726. * Default Export
  52727. *
  52728. * */
  52729. const RangeSelectorComposition = {
  52730. compose
  52731. };
  52732. return RangeSelectorComposition;
  52733. });
  52734. _registerModule(_modules, 'Stock/RangeSelector/RangeSelector.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Defaults.js'], _modules['Core/Globals.js'], _modules['Stock/RangeSelector/RangeSelectorComposition.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (Axis, D, H, RangeSelectorComposition, SVGElement, U) {
  52735. /* *
  52736. *
  52737. * (c) 2010-2021 Torstein Honsi
  52738. *
  52739. * License: www.highcharts.com/license
  52740. *
  52741. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  52742. *
  52743. * */
  52744. const { defaultOptions } = D;
  52745. const { addEvent, createElement, css, defined, destroyObjectProperties, discardElement, extend, fireEvent, isNumber, merge, objectEach, pad, pick, pInt, splat } = U;
  52746. /* *
  52747. *
  52748. * Functions
  52749. *
  52750. * */
  52751. /**
  52752. * Get the preferred input type based on a date format string.
  52753. *
  52754. * @private
  52755. * @function preferredInputType
  52756. */
  52757. function preferredInputType(format) {
  52758. const ms = format.indexOf('%L') !== -1;
  52759. if (ms) {
  52760. return 'text';
  52761. }
  52762. const date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y']
  52763. .some((char) => format.indexOf('%' + char) !== -1);
  52764. const time = ['H', 'k', 'I', 'l', 'M', 'S']
  52765. .some((char) => format.indexOf('%' + char) !== -1);
  52766. if (date && time) {
  52767. return 'datetime-local';
  52768. }
  52769. if (date) {
  52770. return 'date';
  52771. }
  52772. if (time) {
  52773. return 'time';
  52774. }
  52775. return 'text';
  52776. }
  52777. /* *
  52778. *
  52779. * Class
  52780. *
  52781. * */
  52782. /**
  52783. * The range selector.
  52784. *
  52785. * @private
  52786. * @class
  52787. * @name Highcharts.RangeSelector
  52788. * @param {Highcharts.Chart} chart
  52789. */
  52790. class RangeSelector {
  52791. /* *
  52792. *
  52793. * Static Functions
  52794. *
  52795. * */
  52796. /**
  52797. * @private
  52798. */
  52799. static compose(AxisClass, ChartClass) {
  52800. RangeSelectorComposition.compose(AxisClass, ChartClass, RangeSelector);
  52801. }
  52802. /* *
  52803. *
  52804. * Constructor
  52805. *
  52806. * */
  52807. constructor(chart) {
  52808. /* *
  52809. *
  52810. * Properties
  52811. *
  52812. * */
  52813. this.buttons = void 0;
  52814. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  52815. this.initialButtonGroupWidth = 0;
  52816. this.options = void 0;
  52817. this.chart = chart;
  52818. this.init(chart);
  52819. }
  52820. /* *
  52821. *
  52822. * Functions
  52823. *
  52824. * */
  52825. /**
  52826. * The method to run when one of the buttons in the range selectors is
  52827. * clicked
  52828. *
  52829. * @private
  52830. * @function Highcharts.RangeSelector#clickButton
  52831. * @param {number} i
  52832. * The index of the button
  52833. * @param {boolean} [redraw]
  52834. */
  52835. clickButton(i, redraw) {
  52836. const rangeSelector = this, chart = rangeSelector.chart, rangeOptions = rangeSelector.buttonOptions[i], baseAxis = chart.xAxis[0], unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {}, type = rangeOptions.type, dataGrouping = rangeOptions.dataGrouping;
  52837. let dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, newMin, newMax = baseAxis && Math.round(Math.min(baseAxis.max, pick(dataMax, baseAxis.max))), // #1568
  52838. baseXAxisOptions, range = rangeOptions._range, rangeMin, minSetting, rangeSetting, ctx, ytdExtremes, addOffsetMin = true;
  52839. // chart has no data, base series is removed
  52840. if (dataMin === null || dataMax === null) {
  52841. return;
  52842. }
  52843. // Set the fixed range before range is altered
  52844. chart.fixedRange = range;
  52845. rangeSelector.setSelected(i);
  52846. // Apply dataGrouping associated to button
  52847. if (dataGrouping) {
  52848. this.forcedDataGrouping = true;
  52849. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  52850. this.frozenStates = rangeOptions.preserveDataGrouping;
  52851. }
  52852. // Apply range
  52853. if (type === 'month' || type === 'year') {
  52854. if (!baseAxis) {
  52855. // This is set to the user options and picked up later when the
  52856. // axis is instantiated so that we know the min and max.
  52857. range = rangeOptions;
  52858. }
  52859. else {
  52860. ctx = {
  52861. range: rangeOptions,
  52862. max: newMax,
  52863. chart: chart,
  52864. dataMin: dataMin,
  52865. dataMax: dataMax
  52866. };
  52867. newMin = baseAxis.minFromRange.call(ctx);
  52868. if (isNumber(ctx.newMax)) {
  52869. newMax = ctx.newMax;
  52870. }
  52871. // #15799: offsetMin is added in minFromRange so that it works
  52872. // with pre-selected buttons as well
  52873. addOffsetMin = false;
  52874. }
  52875. // Fixed times like minutes, hours, days
  52876. }
  52877. else if (range) {
  52878. newMin = Math.max(newMax - range, dataMin);
  52879. newMax = Math.min(newMin + range, dataMax);
  52880. addOffsetMin = false;
  52881. }
  52882. else if (type === 'ytd') {
  52883. // On user clicks on the buttons, or a delayed action running from
  52884. // the beforeRender event (below), the baseAxis is defined.
  52885. if (baseAxis) {
  52886. // When "ytd" is the pre-selected button for the initial view,
  52887. // its calculation is delayed and rerun in the beforeRender
  52888. // event (below). When the series are initialized, but before
  52889. // the chart is rendered, we have access to the xData array
  52890. // (#942).
  52891. if (typeof dataMax === 'undefined' ||
  52892. typeof dataMin === 'undefined') {
  52893. dataMin = Number.MAX_VALUE;
  52894. dataMax = Number.MIN_VALUE;
  52895. chart.series.forEach((series) => {
  52896. // reassign it to the last item
  52897. const xData = series.xData;
  52898. if (xData) {
  52899. dataMin = Math.min(xData[0], dataMin);
  52900. dataMax = Math.max(xData[xData.length - 1], dataMax);
  52901. }
  52902. });
  52903. redraw = false;
  52904. }
  52905. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  52906. newMin = rangeMin = ytdExtremes.min;
  52907. newMax = ytdExtremes.max;
  52908. // "ytd" is pre-selected. We don't yet have access to processed
  52909. // point and extremes data (things like pointStart and pointInterval
  52910. // are missing), so we delay the process (#942)
  52911. }
  52912. else {
  52913. rangeSelector.deferredYTDClick = i;
  52914. return;
  52915. }
  52916. }
  52917. else if (type === 'all' && baseAxis) {
  52918. // If the navigator exist and the axis range is declared reset that
  52919. // range and from now on only use the range set by a user, #14742.
  52920. if (chart.navigator && chart.navigator.baseSeries[0]) {
  52921. chart.navigator.baseSeries[0].xAxis.options.range = void 0;
  52922. }
  52923. newMin = dataMin;
  52924. newMax = dataMax;
  52925. }
  52926. if (addOffsetMin && rangeOptions._offsetMin && defined(newMin)) {
  52927. newMin += rangeOptions._offsetMin;
  52928. }
  52929. if (rangeOptions._offsetMax && defined(newMax)) {
  52930. newMax += rangeOptions._offsetMax;
  52931. }
  52932. if (this.dropdown) {
  52933. this.dropdown.selectedIndex = i + 1;
  52934. }
  52935. // Update the chart
  52936. if (!baseAxis) {
  52937. // Axis not yet instanciated. Temporarily set min and range
  52938. // options and remove them on chart load (#4317).
  52939. baseXAxisOptions = splat(chart.options.xAxis)[0];
  52940. rangeSetting = baseXAxisOptions.range;
  52941. baseXAxisOptions.range = range;
  52942. minSetting = baseXAxisOptions.min;
  52943. baseXAxisOptions.min = rangeMin;
  52944. addEvent(chart, 'load', function resetMinAndRange() {
  52945. baseXAxisOptions.range = rangeSetting;
  52946. baseXAxisOptions.min = minSetting;
  52947. });
  52948. }
  52949. else {
  52950. // Existing axis object. Set extremes after render time.
  52951. baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation
  52952. {
  52953. trigger: 'rangeSelectorButton',
  52954. rangeSelectorButton: rangeOptions
  52955. });
  52956. }
  52957. fireEvent(this, 'afterBtnClick');
  52958. }
  52959. /**
  52960. * Set the selected option. This method only sets the internal flag, it
  52961. * doesn't update the buttons or the actual zoomed range.
  52962. *
  52963. * @private
  52964. * @function Highcharts.RangeSelector#setSelected
  52965. * @param {number} [selected]
  52966. */
  52967. setSelected(selected) {
  52968. this.selected = this.options.selected = selected;
  52969. }
  52970. /**
  52971. * Initialize the range selector
  52972. *
  52973. * @private
  52974. * @function Highcharts.RangeSelector#init
  52975. * @param {Highcharts.Chart} chart
  52976. */
  52977. init(chart) {
  52978. const rangeSelector = this, options = chart.options.rangeSelector, buttonOptions = (options.buttons || rangeSelector.defaultButtons.slice()), selectedOption = options.selected, blurInputs = function () {
  52979. const minInput = rangeSelector.minInput, maxInput = rangeSelector.maxInput;
  52980. // #3274 in some case blur is not defined
  52981. if (minInput && (minInput.blur)) {
  52982. fireEvent(minInput, 'blur');
  52983. }
  52984. if (maxInput && (maxInput.blur)) {
  52985. fireEvent(maxInput, 'blur');
  52986. }
  52987. };
  52988. rangeSelector.chart = chart;
  52989. rangeSelector.options = options;
  52990. rangeSelector.buttons = [];
  52991. rangeSelector.buttonOptions = buttonOptions;
  52992. this.eventsToUnbind = [];
  52993. this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));
  52994. this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));
  52995. // Extend the buttonOptions with actual range
  52996. buttonOptions.forEach(rangeSelector.computeButtonRange);
  52997. // zoomed range based on a pre-selected button index
  52998. if (typeof selectedOption !== 'undefined' &&
  52999. buttonOptions[selectedOption]) {
  53000. this.clickButton(selectedOption, false);
  53001. }
  53002. this.eventsToUnbind.push(addEvent(chart, 'load', function () {
  53003. // If a data grouping is applied to the current button, release it
  53004. // when extremes change
  53005. if (chart.xAxis && chart.xAxis[0]) {
  53006. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  53007. if (this.max - this.min !==
  53008. chart.fixedRange &&
  53009. e.trigger !== 'rangeSelectorButton' &&
  53010. e.trigger !== 'updatedData' &&
  53011. rangeSelector.forcedDataGrouping &&
  53012. !rangeSelector.frozenStates) {
  53013. this.setDataGrouping(false, false);
  53014. }
  53015. });
  53016. }
  53017. }));
  53018. }
  53019. /**
  53020. * Dynamically update the range selector buttons after a new range has been
  53021. * set
  53022. *
  53023. * @private
  53024. * @function Highcharts.RangeSelector#updateButtonStates
  53025. */
  53026. updateButtonStates() {
  53027. const rangeSelector = this, chart = this.chart, dropdown = this.dropdown, baseAxis = chart.xAxis[0], actualRange = Math.round(baseAxis.max - baseAxis.min), hasNoData = !baseAxis.hasVisibleSeries, day = 24 * 36e5, // A single day in milliseconds
  53028. unionExtremes = (chart.scroller &&
  53029. chart.scroller.getUnionExtremes()) || baseAxis, dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC), ytdMin = ytdExtremes.min, ytdMax = ytdExtremes.max, selected = rangeSelector.selected, allButtonsEnabled = rangeSelector.options.allButtonsEnabled, buttons = rangeSelector.buttons;
  53030. let selectedExists = isNumber(selected);
  53031. rangeSelector.buttonOptions.forEach((rangeOptions, i) => {
  53032. const range = rangeOptions._range, type = rangeOptions.type, count = rangeOptions.count || 1, button = buttons[i], offsetRange = rangeOptions._offsetMax -
  53033. rangeOptions._offsetMin, isSelected = i === selected,
  53034. // Disable buttons where the range exceeds what is allowed in
  53035. // the current view
  53036. isTooGreatRange = range >
  53037. dataMax - dataMin,
  53038. // Disable buttons where the range is smaller than the minimum
  53039. // range
  53040. isTooSmallRange = range < baseAxis.minRange;
  53041. let state = 0,
  53042. // Do not select the YTD button if not explicitly told so
  53043. isYTDButNotSelected = false,
  53044. // Disable the All button if we're already showing all
  53045. isAllButAlreadyShowingAll = false, isSameRange = range === actualRange;
  53046. // Months and years have a variable range so we check the extremes
  53047. if ((type === 'month' || type === 'year') &&
  53048. (actualRange + 36e5 >=
  53049. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  53050. (actualRange - 36e5 <=
  53051. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  53052. isSameRange = true;
  53053. }
  53054. else if (type === 'ytd') {
  53055. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  53056. isYTDButNotSelected = !isSelected;
  53057. }
  53058. else if (type === 'all') {
  53059. isSameRange = (baseAxis.max - baseAxis.min >=
  53060. dataMax - dataMin);
  53061. isAllButAlreadyShowingAll = (!isSelected &&
  53062. selectedExists &&
  53063. isSameRange);
  53064. }
  53065. // The new zoom area happens to match the range for a button - mark
  53066. // it selected. This happens when scrolling across an ordinal gap.
  53067. // It can be seen in the intraday demos when selecting 1h and scroll
  53068. // across the night gap.
  53069. const disable = (!allButtonsEnabled &&
  53070. (isTooGreatRange ||
  53071. isTooSmallRange ||
  53072. isAllButAlreadyShowingAll ||
  53073. hasNoData));
  53074. const select = ((isSelected && isSameRange) ||
  53075. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  53076. (isSelected && rangeSelector.frozenStates));
  53077. if (disable) {
  53078. state = 3;
  53079. }
  53080. else if (select) {
  53081. selectedExists = true; // Only one button can be selected
  53082. state = 2;
  53083. }
  53084. // If state has changed, update the button
  53085. if (button.state !== state) {
  53086. button.setState(state);
  53087. if (dropdown) {
  53088. dropdown.options[i + 1].disabled = disable;
  53089. if (state === 2) {
  53090. dropdown.selectedIndex = i + 1;
  53091. }
  53092. }
  53093. // Reset (#9209)
  53094. if (state === 0 && selected === i) {
  53095. rangeSelector.setSelected();
  53096. }
  53097. }
  53098. });
  53099. }
  53100. /**
  53101. * Compute and cache the range for an individual button
  53102. *
  53103. * @private
  53104. * @function Highcharts.RangeSelector#computeButtonRange
  53105. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  53106. */
  53107. computeButtonRange(rangeOptions) {
  53108. const type = rangeOptions.type, count = rangeOptions.count || 1,
  53109. // these time intervals have a fixed number of milliseconds, as
  53110. // opposed to month, ytd and year
  53111. fixedTimes = {
  53112. millisecond: 1,
  53113. second: 1000,
  53114. minute: 60 * 1000,
  53115. hour: 3600 * 1000,
  53116. day: 24 * 3600 * 1000,
  53117. week: 7 * 24 * 3600 * 1000
  53118. };
  53119. // Store the range on the button object
  53120. if (fixedTimes[type]) {
  53121. rangeOptions._range = fixedTimes[type] * count;
  53122. }
  53123. else if (type === 'month' || type === 'year') {
  53124. rangeOptions._range = {
  53125. month: 30,
  53126. year: 365
  53127. }[type] * 24 * 36e5 * count;
  53128. }
  53129. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  53130. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  53131. rangeOptions._range +=
  53132. rangeOptions._offsetMax - rangeOptions._offsetMin;
  53133. }
  53134. /**
  53135. * Get the unix timestamp of a HTML input for the dates
  53136. *
  53137. * @private
  53138. * @function Highcharts.RangeSelector#getInputValue
  53139. */
  53140. getInputValue(name) {
  53141. const input = name === 'min' ? this.minInput : this.maxInput;
  53142. const options = this.chart.options
  53143. .rangeSelector;
  53144. const time = this.chart.time;
  53145. if (input) {
  53146. return ((input.type === 'text' && options.inputDateParser) ||
  53147. this.defaultInputDateParser)(input.value, time.useUTC, time);
  53148. }
  53149. return 0;
  53150. }
  53151. /**
  53152. * Set the internal and displayed value of a HTML input for the dates
  53153. *
  53154. * @private
  53155. * @function Highcharts.RangeSelector#setInputValue
  53156. */
  53157. setInputValue(name, inputTime) {
  53158. const options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  53159. if (input) {
  53160. const hcTimeAttr = input.getAttribute('data-hc-time');
  53161. let updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
  53162. if (defined(inputTime)) {
  53163. const previousTime = updatedTime;
  53164. if (defined(previousTime)) {
  53165. input.setAttribute('data-hc-time-previous', previousTime);
  53166. }
  53167. input.setAttribute('data-hc-time', inputTime);
  53168. updatedTime = inputTime;
  53169. }
  53170. input.value = time.dateFormat((this.inputTypeFormats[input.type] ||
  53171. options.inputEditDateFormat), updatedTime);
  53172. if (dateBox) {
  53173. dateBox.attr({
  53174. text: time.dateFormat(options.inputDateFormat, updatedTime)
  53175. });
  53176. }
  53177. }
  53178. }
  53179. /**
  53180. * Set the min and max value of a HTML input for the dates
  53181. *
  53182. * @private
  53183. * @function Highcharts.RangeSelector#setInputExtremes
  53184. */
  53185. setInputExtremes(name, min, max) {
  53186. const input = name === 'min' ? this.minInput : this.maxInput;
  53187. if (input) {
  53188. const format = this.inputTypeFormats[input.type];
  53189. const time = this.chart.time;
  53190. if (format) {
  53191. const newMin = time.dateFormat(format, min);
  53192. if (input.min !== newMin) {
  53193. input.min = newMin;
  53194. }
  53195. const newMax = time.dateFormat(format, max);
  53196. if (input.max !== newMax) {
  53197. input.max = newMax;
  53198. }
  53199. }
  53200. }
  53201. }
  53202. /**
  53203. * @private
  53204. * @function Highcharts.RangeSelector#showInput
  53205. * @param {string} name
  53206. */
  53207. showInput(name) {
  53208. const dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  53209. const input = name === 'min' ? this.minInput : this.maxInput;
  53210. if (input && dateBox && this.inputGroup) {
  53211. const isTextInput = input.type === 'text';
  53212. const { translateX, translateY } = this.inputGroup;
  53213. const { inputBoxWidth } = this.options;
  53214. css(input, {
  53215. width: isTextInput ?
  53216. ((dateBox.width + (inputBoxWidth ? -2 : 20)) + 'px') :
  53217. 'auto',
  53218. height: (dateBox.height - 2) + 'px',
  53219. border: '2px solid silver'
  53220. });
  53221. if (isTextInput && inputBoxWidth) {
  53222. css(input, {
  53223. left: (translateX + dateBox.x) + 'px',
  53224. top: translateY + 'px'
  53225. });
  53226. // Inputs of types date, time or datetime-local should be centered
  53227. // on top of the dateBox
  53228. }
  53229. else {
  53230. css(input, {
  53231. left: Math.min(Math.round(dateBox.x +
  53232. translateX -
  53233. (input.offsetWidth - dateBox.width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
  53234. top: (translateY - (input.offsetHeight - dateBox.height) / 2) + 'px'
  53235. });
  53236. }
  53237. }
  53238. }
  53239. /**
  53240. * @private
  53241. * @function Highcharts.RangeSelector#hideInput
  53242. * @param {string} name
  53243. */
  53244. hideInput(name) {
  53245. const input = name === 'min' ? this.minInput : this.maxInput;
  53246. if (input) {
  53247. css(input, {
  53248. top: '-9999em',
  53249. border: 0,
  53250. width: '1px',
  53251. height: '1px'
  53252. });
  53253. }
  53254. }
  53255. /**
  53256. * @private
  53257. * @function Highcharts.RangeSelector#defaultInputDateParser
  53258. */
  53259. defaultInputDateParser(inputDate, useUTC, time) {
  53260. const hasTimezone = (str) => str.length > 6 &&
  53261. (str.lastIndexOf('-') === str.length - 6 ||
  53262. str.lastIndexOf('+') === str.length - 6);
  53263. let input = inputDate.split('/').join('-').split(' ').join('T');
  53264. if (input.indexOf('T') === -1) {
  53265. input += 'T00:00';
  53266. }
  53267. if (useUTC) {
  53268. input += 'Z';
  53269. }
  53270. else if (H.isSafari && !hasTimezone(input)) {
  53271. const offset = new Date(input).getTimezoneOffset() / 60;
  53272. input += offset <= 0 ? `+${pad(-offset)}:00` : `-${pad(offset)}:00`;
  53273. }
  53274. let date = Date.parse(input);
  53275. // If the value isn't parsed directly to a value by the
  53276. // browser's Date.parse method, try
  53277. // parsing it a different way
  53278. if (!isNumber(date)) {
  53279. const parts = inputDate.split('-');
  53280. date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));
  53281. }
  53282. if (time && useUTC && isNumber(date)) {
  53283. date += time.getTimezoneOffset(date);
  53284. }
  53285. return date;
  53286. }
  53287. /**
  53288. * Draw either the 'from' or the 'to' HTML input box of the range selector
  53289. *
  53290. * @private
  53291. * @function Highcharts.RangeSelector#drawInput
  53292. */
  53293. drawInput(name) {
  53294. const { chart, div, inputGroup } = this;
  53295. const rangeSelector = this, chartStyle = chart.renderer.style || {}, renderer = chart.renderer, options = chart.options.rangeSelector, lang = defaultOptions.lang, isMin = name === 'min';
  53296. /**
  53297. * @private
  53298. */
  53299. function updateExtremes() {
  53300. const { maxInput, minInput } = rangeSelector, chartAxis = chart.xAxis[0], dataAxis = chart.scroller && chart.scroller.xAxis ?
  53301. chart.scroller.xAxis :
  53302. chartAxis, dataMin = dataAxis.dataMin, dataMax = dataAxis.dataMax;
  53303. let value = rangeSelector.getInputValue(name);
  53304. if (value !== Number(input.getAttribute('data-hc-time-previous')) &&
  53305. isNumber(value)) {
  53306. input.setAttribute('data-hc-time-previous', value);
  53307. // Validate the extremes. If it goes beyound the data min or
  53308. // max, use the actual data extreme (#2438).
  53309. if (isMin && maxInput && isNumber(dataMin)) {
  53310. if (value > Number(maxInput.getAttribute('data-hc-time'))) {
  53311. value = void 0;
  53312. }
  53313. else if (value < dataMin) {
  53314. value = dataMin;
  53315. }
  53316. }
  53317. else if (minInput && isNumber(dataMax)) {
  53318. if (value < Number(minInput.getAttribute('data-hc-time'))) {
  53319. value = void 0;
  53320. }
  53321. else if (value > dataMax) {
  53322. value = dataMax;
  53323. }
  53324. }
  53325. // Set the extremes
  53326. if (typeof value !== 'undefined') { // @todo typof undefined
  53327. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  53328. }
  53329. }
  53330. }
  53331. // Create the text label
  53332. const text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'] || '';
  53333. const label = renderer
  53334. .label(text, 0)
  53335. .addClass('highcharts-range-label')
  53336. .attr({
  53337. padding: text ? 2 : 0,
  53338. height: text ? options.inputBoxHeight : 0
  53339. })
  53340. .add(inputGroup);
  53341. // Create an SVG label that shows updated date ranges and and records
  53342. // click events that bring in the HTML input.
  53343. const dateBox = renderer
  53344. .label('', 0)
  53345. .addClass('highcharts-range-input')
  53346. .attr({
  53347. padding: 2,
  53348. width: options.inputBoxWidth,
  53349. height: options.inputBoxHeight,
  53350. 'text-align': 'center'
  53351. })
  53352. .on('click', function () {
  53353. // If it is already focused, the onfocus event doesn't fire
  53354. // (#3713)
  53355. rangeSelector.showInput(name);
  53356. rangeSelector[name + 'Input'].focus();
  53357. });
  53358. if (!chart.styledMode) {
  53359. dateBox.attr({
  53360. stroke: options.inputBoxBorderColor,
  53361. 'stroke-width': 1
  53362. });
  53363. }
  53364. dateBox.add(inputGroup);
  53365. // Create the HTML input element. This is rendered as 1x1 pixel then set
  53366. // to the right size when focused.
  53367. const input = createElement('input', {
  53368. name: name,
  53369. className: 'highcharts-range-selector'
  53370. }, void 0, div);
  53371. // #14788: Setting input.type to an unsupported type throws in IE, so
  53372. // we need to use setAttribute instead
  53373. input.setAttribute('type', preferredInputType(options.inputDateFormat || '%e %b %Y'));
  53374. if (!chart.styledMode) {
  53375. // Styles
  53376. label.css(merge(chartStyle, options.labelStyle));
  53377. dateBox.css(merge({
  53378. color: "#333333" /* Palette.neutralColor80 */
  53379. }, chartStyle, options.inputStyle));
  53380. css(input, extend({
  53381. position: 'absolute',
  53382. border: 0,
  53383. boxShadow: '0 0 15px rgba(0,0,0,0.3)',
  53384. width: '1px',
  53385. height: '1px',
  53386. padding: 0,
  53387. textAlign: 'center',
  53388. fontSize: chartStyle.fontSize,
  53389. fontFamily: chartStyle.fontFamily,
  53390. top: '-9999em' // #4798
  53391. }, options.inputStyle));
  53392. }
  53393. // Blow up the input box
  53394. input.onfocus = () => {
  53395. rangeSelector.showInput(name);
  53396. };
  53397. // Hide away the input box
  53398. input.onblur = () => {
  53399. // update extermes only when inputs are active
  53400. if (input === H.doc.activeElement) { // Only when focused
  53401. // Update also when no `change` event is triggered, like when
  53402. // clicking inside the SVG (#4710)
  53403. updateExtremes();
  53404. }
  53405. // #10404 - move hide and blur outside focus
  53406. rangeSelector.hideInput(name);
  53407. rangeSelector.setInputValue(name);
  53408. input.blur(); // #4606
  53409. };
  53410. let keyDown = false;
  53411. // handle changes in the input boxes
  53412. input.onchange = () => {
  53413. // Update extremes and blur input when clicking date input calendar
  53414. if (!keyDown) {
  53415. updateExtremes();
  53416. rangeSelector.hideInput(name);
  53417. input.blur();
  53418. }
  53419. };
  53420. input.onkeypress = (event) => {
  53421. // IE does not fire onchange on enter
  53422. if (event.keyCode === 13) {
  53423. updateExtremes();
  53424. }
  53425. };
  53426. input.onkeydown = (event) => {
  53427. keyDown = true;
  53428. // Arrow keys
  53429. if (event.keyCode === 38 || event.keyCode === 40) {
  53430. updateExtremes();
  53431. }
  53432. };
  53433. input.onkeyup = () => {
  53434. keyDown = false;
  53435. };
  53436. return { dateBox, input, label };
  53437. }
  53438. /**
  53439. * Get the position of the range selector buttons and inputs. This can be
  53440. * overridden from outside for custom positioning.
  53441. *
  53442. * @private
  53443. * @function Highcharts.RangeSelector#getPosition
  53444. */
  53445. getPosition() {
  53446. const chart = this.chart, options = chart.options.rangeSelector, top = options.verticalAlign === 'top' ?
  53447. chart.plotTop - chart.axisOffset[0] :
  53448. 0; // set offset only for varticalAlign top
  53449. return {
  53450. buttonTop: top + options.buttonPosition.y,
  53451. inputTop: top + options.inputPosition.y - 10
  53452. };
  53453. }
  53454. /**
  53455. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  53456. * the current timestamp. Will choose dataMin if its value is higher than
  53457. * the timestamp for the start of current year.
  53458. *
  53459. * @private
  53460. * @function Highcharts.RangeSelector#getYTDExtremes
  53461. * @return {*}
  53462. * Returns min and max for the YTD
  53463. */
  53464. getYTDExtremes(dataMax, dataMin, useUTC) {
  53465. const time = this.chart.time, now = new time.Date(dataMax), year = time.get('FullYear', now), startOfYear = useUTC ?
  53466. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  53467. +new time.Date(year, 0, 1), min = Math.max(dataMin, startOfYear), ts = now.getTime();
  53468. return {
  53469. max: Math.min(dataMax || ts, ts),
  53470. min
  53471. };
  53472. }
  53473. /**
  53474. * Render the range selector including the buttons and the inputs. The first
  53475. * time render is called, the elements are created and positioned. On
  53476. * subsequent calls, they are moved and updated.
  53477. *
  53478. * @private
  53479. * @function Highcharts.RangeSelector#render
  53480. * @param {number} [min]
  53481. * X axis minimum
  53482. * @param {number} [max]
  53483. * X axis maximum
  53484. */
  53485. render(min, max) {
  53486. const chart = this.chart, renderer = chart.renderer, container = chart.container, chartOptions = chart.options, options = chartOptions.rangeSelector,
  53487. // Place inputs above the container
  53488. inputsZIndex = pick(chartOptions.chart.style &&
  53489. chartOptions.chart.style.zIndex, 0) + 1, inputEnabled = options.inputEnabled, rendered = this.rendered;
  53490. if (options.enabled === false) {
  53491. return;
  53492. }
  53493. // create the elements
  53494. if (!rendered) {
  53495. this.group = renderer.g('range-selector-group')
  53496. .attr({
  53497. zIndex: 7
  53498. })
  53499. .add();
  53500. this.div = createElement('div', void 0, {
  53501. position: 'relative',
  53502. height: 0,
  53503. zIndex: inputsZIndex
  53504. });
  53505. if (this.buttonOptions.length) {
  53506. this.renderButtons();
  53507. }
  53508. // First create a wrapper outside the container in order to make
  53509. // the inputs work and make export correct
  53510. if (container.parentNode) {
  53511. container.parentNode.insertBefore(this.div, container);
  53512. }
  53513. if (inputEnabled) {
  53514. // Create the group to keep the inputs
  53515. this.inputGroup = renderer.g('input-group').add(this.group);
  53516. const minElems = this.drawInput('min');
  53517. this.minDateBox = minElems.dateBox;
  53518. this.minLabel = minElems.label;
  53519. this.minInput = minElems.input;
  53520. const maxElems = this.drawInput('max');
  53521. this.maxDateBox = maxElems.dateBox;
  53522. this.maxLabel = maxElems.label;
  53523. this.maxInput = maxElems.input;
  53524. }
  53525. }
  53526. if (inputEnabled) {
  53527. // Set or reset the input values
  53528. this.setInputValue('min', min);
  53529. this.setInputValue('max', max);
  53530. const unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
  53531. if (defined(unionExtremes.dataMin) &&
  53532. defined(unionExtremes.dataMax)) {
  53533. const minRange = chart.xAxis[0].minRange || 0;
  53534. this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
  53535. this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
  53536. }
  53537. // Reflow
  53538. if (this.inputGroup) {
  53539. let x = 0;
  53540. [
  53541. this.minLabel,
  53542. this.minDateBox,
  53543. this.maxLabel,
  53544. this.maxDateBox
  53545. ].forEach((label) => {
  53546. if (label) {
  53547. const { width } = label.getBBox();
  53548. if (width) {
  53549. label.attr({ x });
  53550. x += width + options.inputSpacing;
  53551. }
  53552. }
  53553. });
  53554. }
  53555. }
  53556. this.alignElements();
  53557. this.rendered = true;
  53558. }
  53559. /**
  53560. * Render the range buttons. This only runs the first time, later the
  53561. * positioning is laid out in alignElements.
  53562. *
  53563. * @private
  53564. * @function Highcharts.RangeSelector#renderButtons
  53565. */
  53566. renderButtons() {
  53567. const { buttons, chart, options } = this;
  53568. const lang = defaultOptions.lang;
  53569. const renderer = chart.renderer;
  53570. const buttonTheme = merge(options.buttonTheme);
  53571. const states = buttonTheme && buttonTheme.states;
  53572. // Prevent the button from resetting the width when the button state
  53573. // changes since we need more control over the width when collapsing
  53574. // the buttons
  53575. const width = buttonTheme.width || 28;
  53576. delete buttonTheme.width;
  53577. delete buttonTheme.states;
  53578. this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
  53579. const dropdown = this.dropdown = createElement('select', void 0, {
  53580. position: 'absolute',
  53581. width: '1px',
  53582. height: '1px',
  53583. padding: 0,
  53584. border: 0,
  53585. top: '-9999em',
  53586. cursor: 'pointer',
  53587. opacity: 0.0001
  53588. }, this.div);
  53589. // Prevent page zoom on iPhone
  53590. addEvent(dropdown, 'touchstart', () => {
  53591. dropdown.style.fontSize = '16px';
  53592. });
  53593. // Forward events from select to button
  53594. [
  53595. [H.isMS ? 'mouseover' : 'mouseenter'],
  53596. [H.isMS ? 'mouseout' : 'mouseleave'],
  53597. ['change', 'click']
  53598. ].forEach(([from, to]) => {
  53599. addEvent(dropdown, from, () => {
  53600. const button = buttons[this.currentButtonIndex()];
  53601. if (button) {
  53602. fireEvent(button.element, to || from);
  53603. }
  53604. });
  53605. });
  53606. this.zoomText = renderer
  53607. .label((lang && lang.rangeSelectorZoom) || '', 0)
  53608. .attr({
  53609. padding: options.buttonTheme.padding,
  53610. height: options.buttonTheme.height,
  53611. paddingLeft: 0,
  53612. paddingRight: 0
  53613. })
  53614. .add(this.buttonGroup);
  53615. if (!this.chart.styledMode) {
  53616. this.zoomText.css(options.labelStyle);
  53617. buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);
  53618. }
  53619. createElement('option', {
  53620. textContent: this.zoomText.textStr,
  53621. disabled: true
  53622. }, void 0, dropdown);
  53623. this.buttonOptions.forEach((rangeOptions, i) => {
  53624. createElement('option', {
  53625. textContent: rangeOptions.title || rangeOptions.text
  53626. }, void 0, dropdown);
  53627. buttons[i] = renderer
  53628. .button(rangeOptions.text, 0, 0, (e) => {
  53629. // extract events from button object and call
  53630. const buttonEvents = (rangeOptions.events && rangeOptions.events.click);
  53631. let callDefaultEvent;
  53632. if (buttonEvents) {
  53633. callDefaultEvent =
  53634. buttonEvents.call(rangeOptions, e);
  53635. }
  53636. if (callDefaultEvent !== false) {
  53637. this.clickButton(i);
  53638. }
  53639. this.isActive = true;
  53640. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  53641. .attr({
  53642. 'text-align': 'center',
  53643. width
  53644. })
  53645. .add(this.buttonGroup);
  53646. if (rangeOptions.title) {
  53647. buttons[i].attr('title', rangeOptions.title);
  53648. }
  53649. });
  53650. }
  53651. /**
  53652. * Align the elements horizontally and vertically.
  53653. *
  53654. * @private
  53655. * @function Highcharts.RangeSelector#alignElements
  53656. */
  53657. alignElements() {
  53658. const { buttonGroup, buttons, chart, group, inputGroup, options, zoomText } = this;
  53659. const chartOptions = chart.options;
  53660. const navButtonOptions = (chartOptions.exporting &&
  53661. chartOptions.exporting.enabled !== false &&
  53662. chartOptions.navigation &&
  53663. chartOptions.navigation.buttonOptions);
  53664. const { buttonPosition, inputPosition, verticalAlign } = options;
  53665. // Get the X offset required to avoid overlapping with the exporting
  53666. // button. This is is used both by the buttonGroup and the inputGroup.
  53667. const getXOffsetForExportButton = (group, position) => {
  53668. if (navButtonOptions &&
  53669. this.titleCollision(chart) &&
  53670. verticalAlign === 'top' &&
  53671. position.align === 'right' && ((position.y -
  53672. group.getBBox().height - 12) <
  53673. ((navButtonOptions.y || 0) +
  53674. (navButtonOptions.height || 0) +
  53675. chart.spacing[0]))) {
  53676. return -40;
  53677. }
  53678. return 0;
  53679. };
  53680. let plotLeft = chart.plotLeft;
  53681. if (group && buttonPosition && inputPosition) {
  53682. let translateX = buttonPosition.x - chart.spacing[3];
  53683. if (buttonGroup) {
  53684. this.positionButtons();
  53685. if (!this.initialButtonGroupWidth) {
  53686. let width = 0;
  53687. if (zoomText) {
  53688. width += zoomText.getBBox().width + 5;
  53689. }
  53690. buttons.forEach((button, i) => {
  53691. width += button.width;
  53692. if (i !== buttons.length - 1) {
  53693. width += options.buttonSpacing;
  53694. }
  53695. });
  53696. this.initialButtonGroupWidth = width;
  53697. }
  53698. plotLeft -= chart.spacing[3];
  53699. this.updateButtonStates();
  53700. // Detect collision between button group and exporting
  53701. const xOffsetForExportButton = getXOffsetForExportButton(buttonGroup, buttonPosition);
  53702. this.alignButtonGroup(xOffsetForExportButton);
  53703. // Skip animation
  53704. group.placed = buttonGroup.placed = chart.hasLoaded;
  53705. }
  53706. let xOffsetForExportButton = 0;
  53707. if (inputGroup) {
  53708. // Detect collision between the input group and exporting button
  53709. xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);
  53710. if (inputPosition.align === 'left') {
  53711. translateX = plotLeft;
  53712. }
  53713. else if (inputPosition.align === 'right') {
  53714. translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
  53715. }
  53716. // Update the alignment to the updated spacing box
  53717. inputGroup.align({
  53718. y: inputPosition.y,
  53719. width: inputGroup.getBBox().width,
  53720. align: inputPosition.align,
  53721. // fix wrong getBBox() value on right align
  53722. x: inputPosition.x + translateX - 2
  53723. }, true, chart.spacingBox);
  53724. // Skip animation
  53725. inputGroup.placed = chart.hasLoaded;
  53726. }
  53727. this.handleCollision(xOffsetForExportButton);
  53728. // Vertical align
  53729. group.align({
  53730. verticalAlign
  53731. }, true, chart.spacingBox);
  53732. const alignTranslateY = group.alignAttr.translateY;
  53733. // Set position
  53734. let groupHeight = group.getBBox().height + 20; // # 20 padding
  53735. let translateY = 0;
  53736. // Calculate bottom position
  53737. if (verticalAlign === 'bottom') {
  53738. const legendOptions = chart.legend && chart.legend.options;
  53739. const legendHeight = (legendOptions &&
  53740. legendOptions.verticalAlign === 'bottom' &&
  53741. legendOptions.enabled &&
  53742. !legendOptions.floating ?
  53743. (chart.legend.legendHeight +
  53744. pick(legendOptions.margin, 10)) :
  53745. 0);
  53746. groupHeight = groupHeight + legendHeight - 20;
  53747. translateY = (alignTranslateY -
  53748. groupHeight -
  53749. (options.floating ? 0 : options.y) -
  53750. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  53751. 10 // 10 spacing
  53752. );
  53753. }
  53754. if (verticalAlign === 'top') {
  53755. if (options.floating) {
  53756. translateY = 0;
  53757. }
  53758. if (chart.titleOffset && chart.titleOffset[0]) {
  53759. translateY = chart.titleOffset[0];
  53760. }
  53761. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  53762. }
  53763. else if (verticalAlign === 'middle') {
  53764. if (inputPosition.y === buttonPosition.y) {
  53765. translateY = alignTranslateY;
  53766. }
  53767. else if (inputPosition.y || buttonPosition.y) {
  53768. if (inputPosition.y < 0 ||
  53769. buttonPosition.y < 0) {
  53770. translateY -= Math.min(inputPosition.y, buttonPosition.y);
  53771. }
  53772. else {
  53773. translateY = alignTranslateY - groupHeight;
  53774. }
  53775. }
  53776. }
  53777. group.translate(options.x, options.y + Math.floor(translateY));
  53778. // Translate HTML inputs
  53779. const { minInput, maxInput, dropdown } = this;
  53780. if (options.inputEnabled && minInput && maxInput) {
  53781. minInput.style.marginTop = group.translateY + 'px';
  53782. maxInput.style.marginTop = group.translateY + 'px';
  53783. }
  53784. if (dropdown) {
  53785. dropdown.style.marginTop = group.translateY + 'px';
  53786. }
  53787. }
  53788. }
  53789. /**
  53790. * Align the button group horizontally and vertically.
  53791. *
  53792. * @private
  53793. * @function Highcharts.RangeSelector#alignButtonGroup
  53794. * @param {number} xOffsetForExportButton
  53795. * @param {number} [width]
  53796. */
  53797. alignButtonGroup(xOffsetForExportButton, width) {
  53798. const { chart, options, buttonGroup, buttons } = this;
  53799. const { buttonPosition } = options;
  53800. const plotLeft = chart.plotLeft - chart.spacing[3];
  53801. let translateX = buttonPosition.x - chart.spacing[3];
  53802. if (buttonPosition.align === 'right') {
  53803. translateX += xOffsetForExportButton - plotLeft; // #13014
  53804. }
  53805. else if (buttonPosition.align === 'center') {
  53806. translateX -= plotLeft / 2;
  53807. }
  53808. if (buttonGroup) {
  53809. // Align button group
  53810. buttonGroup.align({
  53811. y: buttonPosition.y,
  53812. width: pick(width, this.initialButtonGroupWidth),
  53813. align: buttonPosition.align,
  53814. x: translateX
  53815. }, true, chart.spacingBox);
  53816. }
  53817. }
  53818. /**
  53819. * @private
  53820. * @function Highcharts.RangeSelector#positionButtons
  53821. */
  53822. positionButtons() {
  53823. const { buttons, chart, options, zoomText } = this;
  53824. const verb = chart.hasLoaded ? 'animate' : 'attr';
  53825. const { buttonPosition } = options;
  53826. const plotLeft = chart.plotLeft;
  53827. let buttonLeft = plotLeft;
  53828. if (zoomText && zoomText.visibility !== 'hidden') {
  53829. // #8769, allow dynamically updating margins
  53830. zoomText[verb]({
  53831. x: pick(plotLeft + buttonPosition.x, plotLeft)
  53832. });
  53833. // Button start position
  53834. buttonLeft += buttonPosition.x +
  53835. zoomText.getBBox().width + 5;
  53836. }
  53837. for (let i = 0, iEnd = this.buttonOptions.length; i < iEnd; ++i) {
  53838. if (buttons[i].visibility !== 'hidden') {
  53839. buttons[i][verb]({ x: buttonLeft });
  53840. // increase button position for the next button
  53841. buttonLeft += buttons[i].width + options.buttonSpacing;
  53842. }
  53843. else {
  53844. buttons[i][verb]({ x: plotLeft });
  53845. }
  53846. }
  53847. }
  53848. /**
  53849. * Handle collision between the button group and the input group
  53850. *
  53851. * @private
  53852. * @function Highcharts.RangeSelector#handleCollision
  53853. *
  53854. * @param {number} xOffsetForExportButton
  53855. * The X offset of the group required to make room for the
  53856. * exporting button
  53857. */
  53858. handleCollision(xOffsetForExportButton) {
  53859. const { chart, buttonGroup, inputGroup } = this;
  53860. const { buttonPosition, dropdown, inputPosition } = this.options;
  53861. const maxButtonWidth = () => {
  53862. let buttonWidth = 0;
  53863. this.buttons.forEach((button) => {
  53864. const bBox = button.getBBox();
  53865. if (bBox.width > buttonWidth) {
  53866. buttonWidth = bBox.width;
  53867. }
  53868. });
  53869. return buttonWidth;
  53870. };
  53871. const groupsOverlap = (buttonGroupWidth) => {
  53872. if (inputGroup && buttonGroup) {
  53873. const inputGroupX = (inputGroup.alignAttr.translateX +
  53874. inputGroup.alignOptions.x -
  53875. xOffsetForExportButton +
  53876. // getBBox for detecing left margin
  53877. inputGroup.getBBox().x +
  53878. // 2px padding to not overlap input and label
  53879. 2);
  53880. const inputGroupWidth = inputGroup.alignOptions.width;
  53881. const buttonGroupX = buttonGroup.alignAttr.translateX +
  53882. buttonGroup.getBBox().x;
  53883. return (buttonGroupX + buttonGroupWidth > inputGroupX) &&
  53884. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  53885. (buttonPosition.y <
  53886. (inputPosition.y +
  53887. inputGroup.getBBox().height));
  53888. }
  53889. return false;
  53890. };
  53891. const moveInputsDown = () => {
  53892. if (inputGroup && buttonGroup) {
  53893. inputGroup.attr({
  53894. translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
  53895. 0 :
  53896. -xOffsetForExportButton),
  53897. translateY: inputGroup.alignAttr.translateY +
  53898. buttonGroup.getBBox().height + 10
  53899. });
  53900. }
  53901. };
  53902. if (buttonGroup) {
  53903. if (dropdown === 'always') {
  53904. this.collapseButtons(xOffsetForExportButton);
  53905. if (groupsOverlap(maxButtonWidth())) {
  53906. // Move the inputs down if there is still a collision
  53907. // after collapsing the buttons
  53908. moveInputsDown();
  53909. }
  53910. return;
  53911. }
  53912. if (dropdown === 'never') {
  53913. this.expandButtons();
  53914. }
  53915. }
  53916. // Detect collision
  53917. if (inputGroup && buttonGroup) {
  53918. if ((inputPosition.align === buttonPosition.align) ||
  53919. // 20 is minimal spacing between elements
  53920. groupsOverlap(this.initialButtonGroupWidth + 20)) {
  53921. if (dropdown === 'responsive') {
  53922. this.collapseButtons(xOffsetForExportButton);
  53923. if (groupsOverlap(maxButtonWidth())) {
  53924. moveInputsDown();
  53925. }
  53926. }
  53927. else {
  53928. moveInputsDown();
  53929. }
  53930. }
  53931. else if (dropdown === 'responsive') {
  53932. this.expandButtons();
  53933. }
  53934. }
  53935. else if (buttonGroup && dropdown === 'responsive') {
  53936. if (this.initialButtonGroupWidth > chart.plotWidth) {
  53937. this.collapseButtons(xOffsetForExportButton);
  53938. }
  53939. else {
  53940. this.expandButtons();
  53941. }
  53942. }
  53943. }
  53944. /**
  53945. * Collapse the buttons and put the select element on top.
  53946. *
  53947. * @private
  53948. * @function Highcharts.RangeSelector#collapseButtons
  53949. * @param {number} xOffsetForExportButton
  53950. */
  53951. collapseButtons(xOffsetForExportButton) {
  53952. const { buttons, buttonOptions, chart, dropdown, options, zoomText } = this;
  53953. const userButtonTheme = (chart.userOptions.rangeSelector &&
  53954. chart.userOptions.rangeSelector.buttonTheme) || {};
  53955. const getAttribs = (text) => ({
  53956. text: text ? `${text} ▾` : '▾',
  53957. width: 'auto',
  53958. paddingLeft: pick(options.buttonTheme.paddingLeft, userButtonTheme.padding, 8),
  53959. paddingRight: pick(options.buttonTheme.paddingRight, userButtonTheme.padding, 8)
  53960. });
  53961. if (zoomText) {
  53962. zoomText.hide();
  53963. }
  53964. let hasActiveButton = false;
  53965. buttonOptions.forEach((rangeOptions, i) => {
  53966. const button = buttons[i];
  53967. if (button.state !== 2) {
  53968. button.hide();
  53969. }
  53970. else {
  53971. button.show();
  53972. button.attr(getAttribs(rangeOptions.text));
  53973. hasActiveButton = true;
  53974. }
  53975. });
  53976. if (!hasActiveButton) {
  53977. if (dropdown) {
  53978. dropdown.selectedIndex = 0;
  53979. }
  53980. buttons[0].show();
  53981. buttons[0].attr(getAttribs(this.zoomText && this.zoomText.textStr));
  53982. }
  53983. const { align } = options.buttonPosition;
  53984. this.positionButtons();
  53985. if (align === 'right' || align === 'center') {
  53986. this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);
  53987. }
  53988. this.showDropdown();
  53989. }
  53990. /**
  53991. * Show all the buttons and hide the select element.
  53992. *
  53993. * @private
  53994. * @function Highcharts.RangeSelector#expandButtons
  53995. */
  53996. expandButtons() {
  53997. const { buttons, buttonOptions, options, zoomText } = this;
  53998. this.hideDropdown();
  53999. if (zoomText) {
  54000. zoomText.show();
  54001. }
  54002. buttonOptions.forEach((rangeOptions, i) => {
  54003. const button = buttons[i];
  54004. button.show();
  54005. button.attr({
  54006. text: rangeOptions.text,
  54007. width: options.buttonTheme.width || 28,
  54008. paddingLeft: pick(options.buttonTheme.paddingLeft, 'unset'),
  54009. paddingRight: pick(options.buttonTheme.paddingRight, 'unset')
  54010. });
  54011. if (button.state < 2) {
  54012. button.setState(0);
  54013. }
  54014. });
  54015. this.positionButtons();
  54016. }
  54017. /**
  54018. * Get the index of the visible button when the buttons are collapsed.
  54019. *
  54020. * @private
  54021. * @function Highcharts.RangeSelector#currentButtonIndex
  54022. */
  54023. currentButtonIndex() {
  54024. const { dropdown } = this;
  54025. if (dropdown && dropdown.selectedIndex > 0) {
  54026. return dropdown.selectedIndex - 1;
  54027. }
  54028. return 0;
  54029. }
  54030. /**
  54031. * Position the select element on top of the button.
  54032. *
  54033. * @private
  54034. * @function Highcharts.RangeSelector#showDropdown
  54035. */
  54036. showDropdown() {
  54037. const { buttonGroup, buttons, chart, dropdown } = this;
  54038. if (buttonGroup && dropdown) {
  54039. const { translateX, translateY } = buttonGroup;
  54040. const bBox = buttons[this.currentButtonIndex()].getBBox();
  54041. css(dropdown, {
  54042. left: (chart.plotLeft + translateX) + 'px',
  54043. top: (translateY + 0.5) + 'px',
  54044. width: bBox.width + 'px',
  54045. height: bBox.height + 'px'
  54046. });
  54047. this.hasVisibleDropdown = true;
  54048. }
  54049. }
  54050. /**
  54051. * @private
  54052. * @function Highcharts.RangeSelector#hideDropdown
  54053. */
  54054. hideDropdown() {
  54055. const { dropdown } = this;
  54056. if (dropdown) {
  54057. css(dropdown, {
  54058. top: '-9999em',
  54059. width: '1px',
  54060. height: '1px'
  54061. });
  54062. this.hasVisibleDropdown = false;
  54063. }
  54064. }
  54065. /**
  54066. * Extracts height of range selector
  54067. *
  54068. * @private
  54069. * @function Highcharts.RangeSelector#getHeight
  54070. * @return {number}
  54071. * Returns rangeSelector height
  54072. */
  54073. getHeight() {
  54074. const rangeSelector = this, options = rangeSelector.options, rangeSelectorGroup = rangeSelector.group, inputPosition = options.inputPosition, buttonPosition = options.buttonPosition, yPosition = options.y, buttonPositionY = buttonPosition.y, inputPositionY = inputPosition.y;
  54075. let rangeSelectorHeight = 0;
  54076. if (options.height) {
  54077. return options.height;
  54078. }
  54079. // Align the elements before we read the height in case we're switching
  54080. // between wrapped and non-wrapped layout
  54081. this.alignElements();
  54082. rangeSelectorHeight = rangeSelectorGroup ?
  54083. // 13px to keep back compatibility
  54084. (rangeSelectorGroup.getBBox(true).height) + 13 +
  54085. yPosition :
  54086. 0;
  54087. const minPosition = Math.min(inputPositionY, buttonPositionY);
  54088. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  54089. (inputPositionY > 0 && buttonPositionY > 0)) {
  54090. rangeSelectorHeight += Math.abs(minPosition);
  54091. }
  54092. return rangeSelectorHeight;
  54093. }
  54094. /**
  54095. * Detect collision with title or subtitle
  54096. *
  54097. * @private
  54098. * @function Highcharts.RangeSelector#titleCollision
  54099. * @return {boolean}
  54100. * Returns collision status
  54101. */
  54102. titleCollision(chart) {
  54103. return !(chart.options.title.text ||
  54104. chart.options.subtitle.text);
  54105. }
  54106. /**
  54107. * Update the range selector with new options
  54108. *
  54109. * @private
  54110. * @function Highcharts.RangeSelector#update
  54111. * @param {Highcharts.RangeSelectorOptions} options
  54112. */
  54113. update(options) {
  54114. const chart = this.chart;
  54115. merge(true, chart.options.rangeSelector, options);
  54116. this.destroy();
  54117. this.init(chart);
  54118. this.render();
  54119. }
  54120. /**
  54121. * Destroys allocated elements.
  54122. *
  54123. * @private
  54124. * @function Highcharts.RangeSelector#destroy
  54125. */
  54126. destroy() {
  54127. const rSelector = this, minInput = rSelector.minInput, maxInput = rSelector.maxInput;
  54128. if (rSelector.eventsToUnbind) {
  54129. rSelector.eventsToUnbind.forEach((unbind) => unbind());
  54130. rSelector.eventsToUnbind = void 0;
  54131. }
  54132. // Destroy elements in collections
  54133. destroyObjectProperties(rSelector.buttons);
  54134. // Clear input element events
  54135. if (minInput) {
  54136. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  54137. }
  54138. if (maxInput) {
  54139. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  54140. }
  54141. // Destroy HTML and SVG elements
  54142. objectEach(rSelector, function (val, key) {
  54143. if (val && key !== 'chart') {
  54144. if (val instanceof SVGElement) {
  54145. // SVGElement
  54146. val.destroy();
  54147. }
  54148. else if (val instanceof window.HTMLElement) {
  54149. // HTML element
  54150. discardElement(val);
  54151. }
  54152. }
  54153. if (val !== RangeSelector.prototype[key]) {
  54154. rSelector[key] = null;
  54155. }
  54156. }, this);
  54157. }
  54158. }
  54159. extend(RangeSelector.prototype, {
  54160. /**
  54161. * The default buttons for pre-selecting time frames.
  54162. * @private
  54163. */
  54164. defaultButtons: [{
  54165. type: 'month',
  54166. count: 1,
  54167. text: '1m',
  54168. title: 'View 1 month'
  54169. }, {
  54170. type: 'month',
  54171. count: 3,
  54172. text: '3m',
  54173. title: 'View 3 months'
  54174. }, {
  54175. type: 'month',
  54176. count: 6,
  54177. text: '6m',
  54178. title: 'View 6 months'
  54179. }, {
  54180. type: 'ytd',
  54181. text: 'YTD',
  54182. title: 'View year to date'
  54183. }, {
  54184. type: 'year',
  54185. count: 1,
  54186. text: '1y',
  54187. title: 'View 1 year'
  54188. }, {
  54189. type: 'all',
  54190. text: 'All',
  54191. title: 'View all'
  54192. }],
  54193. /**
  54194. * The date formats to use when setting min, max and value on date inputs.
  54195. * @private
  54196. */
  54197. inputTypeFormats: {
  54198. 'datetime-local': '%Y-%m-%dT%H:%M:%S',
  54199. 'date': '%Y-%m-%d',
  54200. 'time': '%H:%M:%S'
  54201. }
  54202. });
  54203. /* *
  54204. *
  54205. * Default Export
  54206. *
  54207. * */
  54208. /* *
  54209. *
  54210. * API Options
  54211. *
  54212. * */
  54213. /**
  54214. * Define the time span for the button
  54215. *
  54216. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  54217. */
  54218. /**
  54219. * Callback function to react on button clicks.
  54220. *
  54221. * @callback Highcharts.RangeSelectorClickCallbackFunction
  54222. *
  54223. * @param {global.Event} e
  54224. * Event arguments.
  54225. *
  54226. * @param {boolean|undefined}
  54227. * Return false to cancel the default button event.
  54228. */
  54229. /**
  54230. * Callback function to parse values entered in the input boxes and return a
  54231. * valid JavaScript time as milliseconds since 1970.
  54232. *
  54233. * @callback Highcharts.RangeSelectorParseCallbackFunction
  54234. *
  54235. * @param {string} value
  54236. * Input value to parse.
  54237. *
  54238. * @return {number}
  54239. * Parsed JavaScript time value.
  54240. */
  54241. (''); // keeps doclets above in JS file
  54242. return RangeSelector;
  54243. });
  54244. _registerModule(_modules, 'Core/Axis/OrdinalAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (Axis, H, Series, U) {
  54245. /* *
  54246. *
  54247. * (c) 2010-2021 Torstein Honsi
  54248. *
  54249. * License: www.highcharts.com/license
  54250. *
  54251. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  54252. *
  54253. * */
  54254. const { addEvent, correctFloat, css, defined, error, pick, timeUnits } = U;
  54255. /* *
  54256. *
  54257. * Constants
  54258. *
  54259. * */
  54260. const composedMembers = [];
  54261. /* eslint-disable valid-jsdoc */
  54262. /* *
  54263. *
  54264. * Composition
  54265. *
  54266. * */
  54267. /**
  54268. * Extends the axis with ordinal support.
  54269. * @private
  54270. */
  54271. var OrdinalAxis;
  54272. (function (OrdinalAxis) {
  54273. /* *
  54274. *
  54275. * Declarations
  54276. *
  54277. * */
  54278. /* *
  54279. *
  54280. * Functions
  54281. *
  54282. * */
  54283. /**
  54284. * Extends the axis with ordinal support.
  54285. *
  54286. * @private
  54287. *
  54288. * @param AxisClass
  54289. * Axis class to extend.
  54290. *
  54291. * @param ChartClass
  54292. * Chart class to use.
  54293. *
  54294. * @param SeriesClass
  54295. * Series class to use.
  54296. */
  54297. function compose(AxisClass, SeriesClass, ChartClass) {
  54298. if (U.pushUnique(composedMembers, AxisClass)) {
  54299. const axisProto = AxisClass.prototype;
  54300. axisProto.getTimeTicks = getTimeTicks;
  54301. axisProto.index2val = index2val;
  54302. axisProto.lin2val = lin2val;
  54303. axisProto.val2lin = val2lin;
  54304. // Record this to prevent overwriting by broken-axis module (#5979)
  54305. axisProto.ordinal2lin = axisProto.val2lin;
  54306. addEvent(AxisClass, 'afterInit', onAxisAfterInit);
  54307. addEvent(AxisClass, 'foundExtremes', onAxisFoundExtremes);
  54308. addEvent(AxisClass, 'afterSetScale', onAxisAfterSetScale);
  54309. addEvent(AxisClass, 'initialAxisTranslation', onAxisInitialAxisTranslation);
  54310. }
  54311. if (U.pushUnique(composedMembers, ChartClass)) {
  54312. addEvent(ChartClass, 'pan', onChartPan);
  54313. }
  54314. if (U.pushUnique(composedMembers, SeriesClass)) {
  54315. addEvent(SeriesClass, 'updatedData', onSeriesUpdatedData);
  54316. }
  54317. return AxisClass;
  54318. }
  54319. OrdinalAxis.compose = compose;
  54320. /**
  54321. * In an ordinal axis, there might be areas with dense consentrations of
  54322. * points, then large gaps between some. Creating equally distributed
  54323. * ticks over this entire range may lead to a huge number of ticks that
  54324. * will later be removed. So instead, break the positions up in
  54325. * segments, find the tick positions for each segment then concatenize
  54326. * them. This method is used from both data grouping logic and X axis
  54327. * tick position logic.
  54328. * @private
  54329. */
  54330. function getTimeTicks(normalizedInterval, min, max, startOfWeek, positions = [], closestDistance = 0, findHigherRanks) {
  54331. const higherRanks = {}, tickPixelIntervalOption = this.options.tickPixelInterval, time = this.chart.time,
  54332. // Record all the start positions of a segment, to use when
  54333. // deciding what's a gap in the data.
  54334. segmentStarts = [];
  54335. let end, segmentPositions, hasCrossedHigherRank, info, outsideMax, start = 0, groupPositions = [], lastGroupPosition = -Number.MAX_VALUE;
  54336. // The positions are not always defined, for example for ordinal
  54337. // positions when data has regular interval (#1557, #2090)
  54338. if ((!this.options.ordinal && !this.options.breaks) ||
  54339. !positions ||
  54340. positions.length < 3 ||
  54341. typeof min === 'undefined') {
  54342. return time.getTimeTicks.apply(time, arguments);
  54343. }
  54344. // Analyze the positions array to split it into segments on gaps
  54345. // larger than 5 times the closest distance. The closest distance is
  54346. // already found at this point, so we reuse that instead of
  54347. // computing it again.
  54348. const posLength = positions.length;
  54349. for (end = 0; end < posLength; end++) {
  54350. outsideMax = end && positions[end - 1] > max;
  54351. if (positions[end] < min) { // Set the last position before min
  54352. start = end;
  54353. }
  54354. if (end === posLength - 1 ||
  54355. positions[end + 1] - positions[end] > closestDistance * 5 ||
  54356. outsideMax) {
  54357. // For each segment, calculate the tick positions from the
  54358. // getTimeTicks utility function. The interval will be the
  54359. // same regardless of how long the segment is.
  54360. if (positions[end] > lastGroupPosition) { // #1475
  54361. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  54362. // Prevent duplicate groups, for example for multiple
  54363. // segments within one larger time frame (#1475)
  54364. while (segmentPositions.length &&
  54365. segmentPositions[0] <= lastGroupPosition) {
  54366. segmentPositions.shift();
  54367. }
  54368. if (segmentPositions.length) {
  54369. lastGroupPosition =
  54370. segmentPositions[segmentPositions.length - 1];
  54371. }
  54372. segmentStarts.push(groupPositions.length);
  54373. groupPositions = groupPositions.concat(segmentPositions);
  54374. }
  54375. // Set start of next segment
  54376. start = end + 1;
  54377. }
  54378. if (outsideMax) {
  54379. break;
  54380. }
  54381. }
  54382. // Get the grouping info from the last of the segments. The info is
  54383. // the same for all segments.
  54384. if (segmentPositions) {
  54385. info = segmentPositions.info;
  54386. // Optionally identify ticks with higher rank, for example
  54387. // when the ticks have crossed midnight.
  54388. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  54389. end = groupPositions.length - 1;
  54390. // Compare points two by two
  54391. for (start = 1; start < end; start++) {
  54392. if (time.dateFormat('%d', groupPositions[start]) !==
  54393. time.dateFormat('%d', groupPositions[start - 1])) {
  54394. higherRanks[groupPositions[start]] = 'day';
  54395. hasCrossedHigherRank = true;
  54396. }
  54397. }
  54398. // If the complete array has crossed midnight, we want
  54399. // to mark the first positions also as higher rank
  54400. if (hasCrossedHigherRank) {
  54401. higherRanks[groupPositions[0]] = 'day';
  54402. }
  54403. info.higherRanks = higherRanks;
  54404. }
  54405. // Save the info
  54406. info.segmentStarts = segmentStarts;
  54407. groupPositions.info = info;
  54408. }
  54409. else {
  54410. error(12, false, this.chart);
  54411. }
  54412. // Don't show ticks within a gap in the ordinal axis, where the
  54413. // space between two points is greater than a portion of the tick
  54414. // pixel interval
  54415. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  54416. const length = groupPositions.length, translatedArr = [], distances = [];
  54417. let itemToRemove, translated, lastTranslated, medianDistance, distance, i = length;
  54418. // Find median pixel distance in order to keep a reasonably even
  54419. // distance between ticks (#748)
  54420. while (i--) {
  54421. translated = this.translate(groupPositions[i]);
  54422. if (lastTranslated) {
  54423. distances[i] = lastTranslated - translated;
  54424. }
  54425. translatedArr[i] = lastTranslated = translated;
  54426. }
  54427. distances.sort();
  54428. medianDistance = distances[Math.floor(distances.length / 2)];
  54429. if (medianDistance < tickPixelIntervalOption * 0.6) {
  54430. medianDistance = null;
  54431. }
  54432. // Now loop over again and remove ticks where needed
  54433. i = groupPositions[length - 1] > max ? length - 1 : length; // #817
  54434. lastTranslated = void 0;
  54435. while (i--) {
  54436. translated = translatedArr[i];
  54437. distance = Math.abs(lastTranslated - translated);
  54438. // #4175 - when axis is reversed, the distance, is negative but
  54439. // tickPixelIntervalOption positive, so we need to compare the
  54440. // same values
  54441. // Remove ticks that are closer than 0.6 times the pixel
  54442. // interval from the one to the right, but not if it is close to
  54443. // the median distance (#748).
  54444. if (lastTranslated &&
  54445. distance < tickPixelIntervalOption * 0.8 &&
  54446. (medianDistance === null || distance < medianDistance * 0.8)) {
  54447. // Is this a higher ranked position with a normal
  54448. // position to the right?
  54449. if (higherRanks[groupPositions[i]] &&
  54450. !higherRanks[groupPositions[i + 1]]) {
  54451. // Yes: remove the lower ranked neighbour to the
  54452. // right
  54453. itemToRemove = i + 1;
  54454. lastTranslated = translated; // #709
  54455. }
  54456. else {
  54457. // No: remove this one
  54458. itemToRemove = i;
  54459. }
  54460. groupPositions.splice(itemToRemove, 1);
  54461. }
  54462. else {
  54463. lastTranslated = translated;
  54464. }
  54465. }
  54466. }
  54467. return groupPositions;
  54468. }
  54469. /**
  54470. * Get axis position of given index of the extended ordinal positions.
  54471. * Used only when panning an ordinal axis.
  54472. *
  54473. * @private
  54474. * @function Highcharts.Axis#index2val
  54475. * @param {number} index
  54476. * The index value of searched point
  54477. */
  54478. function index2val(index) {
  54479. const axis = this, ordinal = axis.ordinal,
  54480. // Context could be changed to extendedOrdinalPositions.
  54481. ordinalPositions = ordinal.positions;
  54482. // The visible range contains only equally spaced values.
  54483. if (!ordinalPositions) {
  54484. return index;
  54485. }
  54486. let i = ordinalPositions.length - 1, distance;
  54487. if (index < 0) { // out of range, in effect panning to the left
  54488. index = ordinalPositions[0];
  54489. }
  54490. else if (index > i) { // out of range, panning to the right
  54491. index = ordinalPositions[i];
  54492. }
  54493. else { // split it up
  54494. i = Math.floor(index);
  54495. distance = index - i; // the decimal
  54496. }
  54497. if (typeof distance !== 'undefined' &&
  54498. typeof ordinalPositions[i] !== 'undefined') {
  54499. return ordinalPositions[i] + (distance ?
  54500. distance *
  54501. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  54502. 0);
  54503. }
  54504. return index;
  54505. }
  54506. /**
  54507. * Translate from linear (internal) to axis value.
  54508. *
  54509. * @private
  54510. * @function Highcharts.Axis#lin2val
  54511. * @param {number} val
  54512. * The linear abstracted value.
  54513. */
  54514. function lin2val(val) {
  54515. const axis = this, ordinal = axis.ordinal, localMin = axis.old ? axis.old.min : axis.min, localA = axis.old ? axis.old.transA : axis.transA;
  54516. let positions = ordinal.positions; // for the current visible range
  54517. // The visible range contains only equally spaced values.
  54518. if (!positions) {
  54519. return val;
  54520. }
  54521. // Convert back from modivied value to pixels. // #15970
  54522. const pixelVal = correctFloat((val - localMin) * localA +
  54523. axis.minPixelPadding), isInside = val >= positions[0] &&
  54524. val <= positions[positions.length - 1];
  54525. // If the value is not inside the plot area, use the extended positions.
  54526. // (array contains also points that are outside of the plotArea).
  54527. if (!isInside) {
  54528. // When iterating for the first time,
  54529. // get the extended ordinal positional and assign them.
  54530. if (!ordinal.extendedOrdinalPositions) {
  54531. ordinal.extendedOrdinalPositions = (ordinal.getExtendedPositions());
  54532. }
  54533. positions = ordinal.extendedOrdinalPositions;
  54534. }
  54535. // In some cases (especially in early stages of the chart creation) the
  54536. // getExtendedPositions might return undefined.
  54537. if (positions && positions.length) {
  54538. const indexOf = positions.indexOf(val);
  54539. const index = indexOf !== -1 ? indexOf : correctFloat(ordinal.getIndexOfPoint(pixelVal, positions)), mantissa = correctFloat(index % 1);
  54540. // Check if the index is inside position array. If true,
  54541. // read/approximate value for that exact index.
  54542. if (index >= 0 && index <= positions.length - 1) {
  54543. const leftNeighbour = positions[Math.floor(index)], rightNeighbour = positions[Math.ceil(index)], distance = rightNeighbour - leftNeighbour;
  54544. return positions[Math.floor(index)] + mantissa * distance;
  54545. }
  54546. // For cases when the index is not in the extended ordinal position
  54547. // array, like when the value we are looking for exceed the
  54548. // available data, approximate that value based on the calculated
  54549. // slope.
  54550. const positionsLength = positions.length, firstPositionsValue = positions[0], lastPositionsValue = positions[positionsLength - 1], slope = (lastPositionsValue - firstPositionsValue) / (positionsLength - 1);
  54551. if (index < 0) {
  54552. return firstPositionsValue + slope * index;
  54553. }
  54554. return lastPositionsValue + slope * (index - positionsLength);
  54555. }
  54556. return val;
  54557. }
  54558. /**
  54559. * Internal function to calculate the precise index in ordinalPositions
  54560. * array.
  54561. * @private
  54562. */
  54563. function getIndexInArray(ordinalPositions, val) {
  54564. const index = OrdinalAxis.Additions.findIndexOf(ordinalPositions, val, true);
  54565. if (ordinalPositions[index] === val) {
  54566. return index;
  54567. }
  54568. const percent = (val - ordinalPositions[index]) /
  54569. (ordinalPositions[index + 1] - ordinalPositions[index]);
  54570. return index + percent;
  54571. }
  54572. /**
  54573. * @private
  54574. */
  54575. function onAxisAfterInit() {
  54576. const axis = this;
  54577. if (!axis.ordinal) {
  54578. axis.ordinal = new OrdinalAxis.Additions(axis);
  54579. }
  54580. }
  54581. /**
  54582. * @private
  54583. */
  54584. function onAxisFoundExtremes() {
  54585. const axis = this;
  54586. if (axis.isXAxis &&
  54587. defined(axis.options.overscroll) &&
  54588. axis.max === axis.dataMax &&
  54589. (
  54590. // Panning is an execption. We don't want to apply
  54591. // overscroll when panning over the dataMax
  54592. !axis.chart.mouseIsDown ||
  54593. axis.isInternal) && (
  54594. // Scrollbar buttons are the other execption:
  54595. !axis.eventArgs ||
  54596. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  54597. axis.max += axis.options.overscroll;
  54598. // Live data and buttons require translation for the min:
  54599. if (!axis.isInternal && defined(axis.userMin)) {
  54600. axis.min += axis.options.overscroll;
  54601. }
  54602. }
  54603. }
  54604. /**
  54605. * For ordinal axis, that loads data async, redraw axis after data is
  54606. * loaded. If we don't do that, axis will have the same extremes as
  54607. * previously, but ordinal positions won't be calculated. See #10290
  54608. * @private
  54609. */
  54610. function onAxisAfterSetScale() {
  54611. const axis = this;
  54612. if (axis.horiz && !axis.isDirty) {
  54613. axis.isDirty = axis.isOrdinal &&
  54614. axis.chart.navigator &&
  54615. !axis.chart.navigator.adaptToUpdatedData;
  54616. }
  54617. }
  54618. /**
  54619. * @private
  54620. */
  54621. function onAxisInitialAxisTranslation() {
  54622. const axis = this;
  54623. if (axis.ordinal) {
  54624. axis.ordinal.beforeSetTickPositions();
  54625. axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
  54626. }
  54627. }
  54628. /**
  54629. * Extending the Chart.pan method for ordinal axes
  54630. * @private
  54631. */
  54632. function onChartPan(e) {
  54633. const chart = this, xAxis = chart.xAxis[0], overscroll = xAxis.options.overscroll, chartX = e.originalEvent.chartX, panning = chart.options.chart.panning;
  54634. let runBase = false;
  54635. if (panning &&
  54636. panning.type !== 'y' &&
  54637. xAxis.options.ordinal &&
  54638. xAxis.series.length) {
  54639. const mouseDownX = chart.mouseDownX, extremes = xAxis.getExtremes(), dataMax = extremes.dataMax, min = extremes.min, max = extremes.max, hoverPoints = chart.hoverPoints, closestPointRange = (xAxis.closestPointRange ||
  54640. (xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)), pointPixelWidth = (xAxis.translationSlope *
  54641. (xAxis.ordinal.slope || closestPointRange)),
  54642. // how many ordinal units did we move?
  54643. movedUnits = Math.round((mouseDownX - chartX) / pointPixelWidth),
  54644. // get index of all the chart's points
  54645. extendedOrdinalPositions = xAxis.ordinal.getExtendedPositions(), extendedAxis = {
  54646. ordinal: {
  54647. positions: extendedOrdinalPositions,
  54648. extendedOrdinalPositions: extendedOrdinalPositions
  54649. }
  54650. }, index2val = xAxis.index2val, val2lin = xAxis.val2lin;
  54651. let trimmedRange, ordinalPositions, searchAxisLeft, searchAxisRight;
  54652. // we have an ordinal axis, but the data is equally spaced
  54653. if (!extendedAxis.ordinal.positions) {
  54654. runBase = true;
  54655. }
  54656. else if (Math.abs(movedUnits) > 1) {
  54657. // Remove active points for shared tooltip
  54658. if (hoverPoints) {
  54659. hoverPoints.forEach(function (point) {
  54660. point.setState();
  54661. });
  54662. }
  54663. if (movedUnits < 0) {
  54664. searchAxisLeft = extendedAxis;
  54665. searchAxisRight = xAxis.ordinal.positions ?
  54666. xAxis : extendedAxis;
  54667. }
  54668. else {
  54669. searchAxisLeft = xAxis.ordinal.positions ?
  54670. xAxis : extendedAxis;
  54671. searchAxisRight = extendedAxis;
  54672. }
  54673. // In grouped data series, the last ordinal position represents
  54674. // the grouped data, which is to the left of the real data max.
  54675. // If we don't compensate for this, we will be allowed to pan
  54676. // grouped data series passed the right of the plot area.
  54677. ordinalPositions = searchAxisRight.ordinal.positions;
  54678. if (dataMax >
  54679. ordinalPositions[ordinalPositions.length - 1]) {
  54680. ordinalPositions.push(dataMax);
  54681. }
  54682. // Get the new min and max values by getting the ordinal index
  54683. // for the current extreme, then add the moved units and
  54684. // translate back to values. This happens on the extended
  54685. // ordinal positions if the new position is out of range, else
  54686. // it happens on the current x axis which is smaller and faster.
  54687. chart.fixedRange = max - min;
  54688. trimmedRange = xAxis.navigatorAxis
  54689. .toFixedRange(void 0, void 0, index2val.apply(searchAxisLeft, [
  54690. val2lin.apply(searchAxisLeft, [min, true]) +
  54691. movedUnits
  54692. ]), index2val.apply(searchAxisRight, [
  54693. val2lin.apply(searchAxisRight, [max, true]) +
  54694. movedUnits
  54695. ]));
  54696. // Apply it if it is within the available data range
  54697. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  54698. trimmedRange.max <= Math.max(dataMax, max) +
  54699. overscroll) {
  54700. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  54701. }
  54702. chart.mouseDownX = chartX; // set new reference for next run
  54703. css(chart.container, { cursor: 'move' });
  54704. }
  54705. }
  54706. else {
  54707. runBase = true;
  54708. }
  54709. // revert to the linear chart.pan version
  54710. if (runBase || (panning && /y/.test(panning.type))) {
  54711. if (overscroll) {
  54712. xAxis.max = xAxis.dataMax + overscroll;
  54713. }
  54714. }
  54715. else {
  54716. e.preventDefault();
  54717. }
  54718. }
  54719. /**
  54720. * @private
  54721. */
  54722. function onSeriesUpdatedData() {
  54723. const xAxis = this.xAxis;
  54724. // Destroy the extended ordinal index on updated data
  54725. // and destroy extendedOrdinalPositions, #16055.
  54726. if (xAxis && xAxis.options.ordinal) {
  54727. delete xAxis.ordinal.index;
  54728. delete xAxis.ordinal.extendedOrdinalPositions;
  54729. }
  54730. }
  54731. /**
  54732. * Translate from a linear axis value to the corresponding ordinal axis
  54733. * position. If there are no gaps in the ordinal axis this will be the
  54734. * same. The translated value is the value that the point would have if
  54735. * the axis was linear, using the same min and max.
  54736. *
  54737. * @private
  54738. * @function Highcharts.Axis#val2lin
  54739. * @param {number} val
  54740. * The axis value.
  54741. * @param {boolean} [toIndex]
  54742. * Whether to return the index in the ordinalPositions or the new value.
  54743. */
  54744. function val2lin(val, toIndex) {
  54745. const axis = this, ordinal = axis.ordinal, ordinalPositions = ordinal.positions;
  54746. let slope = ordinal.slope, extendedOrdinalPositions = ordinal.extendedOrdinalPositions;
  54747. if (!ordinalPositions) {
  54748. return val;
  54749. }
  54750. const ordinalLength = ordinalPositions.length;
  54751. let ordinalIndex;
  54752. // If the searched value is inside visible plotArea, ivastigate the
  54753. // value basing on ordinalPositions.
  54754. if (ordinalPositions[0] <= val &&
  54755. ordinalPositions[ordinalLength - 1] >= val) {
  54756. ordinalIndex = getIndexInArray(ordinalPositions, val);
  54757. // final return value is based on ordinalIndex
  54758. }
  54759. else {
  54760. if (!extendedOrdinalPositions) {
  54761. extendedOrdinalPositions =
  54762. ordinal.getExtendedPositions &&
  54763. ordinal.getExtendedPositions();
  54764. ordinal.extendedOrdinalPositions = extendedOrdinalPositions;
  54765. }
  54766. if (!(extendedOrdinalPositions && extendedOrdinalPositions.length)) {
  54767. return val;
  54768. }
  54769. const length = extendedOrdinalPositions.length;
  54770. if (!slope) {
  54771. slope =
  54772. (extendedOrdinalPositions[length - 1] -
  54773. extendedOrdinalPositions[0]) /
  54774. length;
  54775. }
  54776. // OriginalPointReference is equal to the index of
  54777. // first point of ordinalPositions in extendedOrdinalPositions.
  54778. const originalPositionsReference = getIndexInArray(extendedOrdinalPositions, ordinalPositions[0]);
  54779. // If the searched value is outside the visiblePlotArea,
  54780. // check if it is inside extendedOrdinalPositions.
  54781. if (val >= extendedOrdinalPositions[0] &&
  54782. val <=
  54783. extendedOrdinalPositions[length - 1]) {
  54784. // Return Value
  54785. ordinalIndex = getIndexInArray(extendedOrdinalPositions, val) -
  54786. originalPositionsReference;
  54787. }
  54788. else {
  54789. // Since ordinal.slope is the average distance between 2
  54790. // points on visible plotArea, this can be used to calculete
  54791. // the approximate position of the point, which is outside
  54792. // the extededOrdinalPositions.
  54793. if (val < extendedOrdinalPositions[0]) {
  54794. const diff = extendedOrdinalPositions[0] - val, approximateIndexOffset = diff / slope;
  54795. ordinalIndex =
  54796. -originalPositionsReference -
  54797. approximateIndexOffset;
  54798. }
  54799. else {
  54800. const diff = val -
  54801. extendedOrdinalPositions[length - 1], approximateIndexOffset = diff / slope;
  54802. ordinalIndex =
  54803. approximateIndexOffset +
  54804. length -
  54805. originalPositionsReference;
  54806. }
  54807. }
  54808. }
  54809. return toIndex ? ordinalIndex : slope * (ordinalIndex || 0) +
  54810. ordinal.offset;
  54811. }
  54812. /* *
  54813. *
  54814. * Classes
  54815. *
  54816. * */
  54817. /**
  54818. * @private
  54819. */
  54820. class Additions {
  54821. /* *
  54822. *
  54823. * Constructors
  54824. *
  54825. * */
  54826. /**
  54827. * @private
  54828. */
  54829. constructor(axis) {
  54830. this.index = {};
  54831. this.axis = axis;
  54832. }
  54833. /* *
  54834. *
  54835. * Functions
  54836. *
  54837. * */
  54838. /**
  54839. * Calculate the ordinal positions before tick positions are calculated.
  54840. * @private
  54841. */
  54842. beforeSetTickPositions() {
  54843. const axis = this.axis, ordinal = axis.ordinal, extremes = axis.getExtremes(), min = extremes.min, max = extremes.max, hasBreaks = axis.isXAxis && !!axis.options.breaks, isOrdinal = axis.options.ordinal, ignoreHiddenSeries = axis.chart.options.chart.ignoreHiddenSeries;
  54844. let len, uniqueOrdinalPositions, dist, minIndex, maxIndex, slope, i, ordinalPositions = [], overscrollPointsRange = Number.MAX_VALUE, useOrdinal = false, adjustOrdinalExtremesPoints = false, isBoosted = false;
  54845. // Apply the ordinal logic
  54846. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  54847. let distanceBetweenPoint = 0;
  54848. axis.series.forEach(function (series, i) {
  54849. uniqueOrdinalPositions = [];
  54850. // For an axis with multiple series, check if the distance
  54851. // between points is identical throughout all series.
  54852. if (i > 0 &&
  54853. series.options.id !== 'highcharts-navigator-series' &&
  54854. series.processedXData.length > 1) {
  54855. adjustOrdinalExtremesPoints =
  54856. distanceBetweenPoint !== series.processedXData[1] -
  54857. series.processedXData[0];
  54858. }
  54859. distanceBetweenPoint =
  54860. series.processedXData[1] - series.processedXData[0];
  54861. if (series.boosted) {
  54862. isBoosted = series.boosted;
  54863. }
  54864. if ((!ignoreHiddenSeries || series.visible !== false) &&
  54865. (series
  54866. .takeOrdinalPosition !== false ||
  54867. hasBreaks)) {
  54868. // concatenate the processed X data into the existing
  54869. // positions, or the empty array
  54870. ordinalPositions = ordinalPositions.concat(series.processedXData);
  54871. len = ordinalPositions.length;
  54872. // remove duplicates (#1588)
  54873. ordinalPositions.sort(function (a, b) {
  54874. // without a custom function it is sorted as strings
  54875. return a - b;
  54876. });
  54877. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  54878. // Check for a single-point series:
  54879. series.closestPointRange, overscrollPointsRange));
  54880. if (len) {
  54881. i = 0;
  54882. while (i < len - 1) {
  54883. if (ordinalPositions[i] !==
  54884. ordinalPositions[i + 1]) {
  54885. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  54886. }
  54887. i++;
  54888. }
  54889. // Check first item:
  54890. if (uniqueOrdinalPositions[0] !==
  54891. ordinalPositions[0]) {
  54892. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  54893. }
  54894. ordinalPositions = uniqueOrdinalPositions;
  54895. }
  54896. }
  54897. });
  54898. // If the distance between points is not identical throughout
  54899. // all series, remove the first and last ordinal position to
  54900. // avoid enabling ordinal logic when it is not needed, #17405.
  54901. // Only for boosted series because changes are negligible.
  54902. if (adjustOrdinalExtremesPoints && isBoosted) {
  54903. ordinalPositions.pop();
  54904. ordinalPositions.shift();
  54905. }
  54906. // cache the length
  54907. len = ordinalPositions.length;
  54908. // Check if we really need the overhead of mapping axis data
  54909. // against the ordinal positions. If the series consist of
  54910. // evenly spaced data any way, we don't need any ordinal logic.
  54911. if (len > 2) { // two points have equal distance by default
  54912. dist = ordinalPositions[1] - ordinalPositions[0];
  54913. i = len - 1;
  54914. while (i-- && !useOrdinal) {
  54915. if (ordinalPositions[i + 1] - ordinalPositions[i] !==
  54916. dist) {
  54917. useOrdinal = true;
  54918. }
  54919. }
  54920. // When zooming in on a week, prevent axis padding for
  54921. // weekends even though the data within the week is evenly
  54922. // spaced.
  54923. if (!axis.options.keepOrdinalPadding &&
  54924. (ordinalPositions[0] - min > dist ||
  54925. (max -
  54926. ordinalPositions[ordinalPositions.length - 1]) > dist)) {
  54927. useOrdinal = true;
  54928. }
  54929. }
  54930. else if (axis.options.overscroll) {
  54931. if (len === 2) {
  54932. // Exactly two points, distance for overscroll is fixed:
  54933. overscrollPointsRange =
  54934. ordinalPositions[1] - ordinalPositions[0];
  54935. }
  54936. else if (len === 1) {
  54937. // We have just one point, closest distance is unknown.
  54938. // Assume then it is last point and overscrolled range:
  54939. overscrollPointsRange = axis.options.overscroll;
  54940. ordinalPositions = [
  54941. ordinalPositions[0],
  54942. ordinalPositions[0] + overscrollPointsRange
  54943. ];
  54944. }
  54945. else {
  54946. // In case of zooming in on overscrolled range, stick to
  54947. // the old range:
  54948. overscrollPointsRange = ordinal.overscrollPointsRange;
  54949. }
  54950. }
  54951. // Record the slope and offset to compute the linear values from
  54952. // the array index. Since the ordinal positions may exceed the
  54953. // current range, get the start and end positions within it
  54954. // (#719, #665b)
  54955. if (useOrdinal || axis.forceOrdinal) {
  54956. if (axis.options.overscroll) {
  54957. ordinal.overscrollPointsRange = overscrollPointsRange;
  54958. ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
  54959. }
  54960. // Register
  54961. ordinal.positions = ordinalPositions;
  54962. // This relies on the ordinalPositions being set. Use
  54963. // Math.max and Math.min to prevent padding on either sides
  54964. // of the data.
  54965. minIndex = axis.ordinal2lin(// #5979
  54966. Math.max(min, ordinalPositions[0]), true);
  54967. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  54968. // Set the slope and offset of the values compared to the
  54969. // indices in the ordinal positions
  54970. ordinal.slope = slope = (max - min) / (maxIndex - minIndex);
  54971. ordinal.offset = min - (minIndex * slope);
  54972. }
  54973. else {
  54974. ordinal.overscrollPointsRange = pick(axis.closestPointRange, ordinal.overscrollPointsRange);
  54975. ordinal.positions = axis.ordinal.slope = ordinal.offset =
  54976. void 0;
  54977. }
  54978. }
  54979. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  54980. ordinal.groupIntervalFactor = null; // reset for next run
  54981. }
  54982. /**
  54983. * Faster way of using the Array.indexOf method.
  54984. * Works for sorted arrays only with unique values.
  54985. *
  54986. * @param {Array} sortedArray
  54987. * The sorted array inside which we are looking for.
  54988. * @param {number} key
  54989. * The key to being found.
  54990. * @param {boolean} indirectSearch
  54991. * In case of lack of the point in the array, should return
  54992. * value be equal to -1 or the closest smaller index.
  54993. * @private
  54994. */
  54995. static findIndexOf(sortedArray, key, indirectSearch) {
  54996. let start = 0, end = sortedArray.length - 1, middle;
  54997. while (start < end) {
  54998. middle = Math.ceil((start + end) / 2);
  54999. // Key found as the middle element.
  55000. if (sortedArray[middle] <= key) {
  55001. // Continue searching to the right.
  55002. start = middle;
  55003. }
  55004. else {
  55005. // Continue searching to the left.
  55006. end = middle - 1;
  55007. }
  55008. }
  55009. if (sortedArray[start] === key) {
  55010. return start;
  55011. }
  55012. // Key could not be found.
  55013. return !indirectSearch ? -1 : start;
  55014. }
  55015. /**
  55016. * Get the ordinal positions for the entire data set. This is necessary
  55017. * in chart panning because we need to find out what points or data
  55018. * groups are available outside the visible range. When a panning
  55019. * operation starts, if an index for the given grouping does not exists,
  55020. * it is created and cached. This index is deleted on updated data, so
  55021. * it will be regenerated the next time a panning operation starts.
  55022. * @private
  55023. */
  55024. getExtendedPositions() {
  55025. const ordinal = this, axis = ordinal.axis, axisProto = axis.constructor.prototype, chart = axis.chart, grouping = axis.series[0].currentDataGrouping, key = grouping ?
  55026. grouping.count + grouping.unitName :
  55027. 'raw', overscroll = axis.options.overscroll, extremes = axis.getExtremes();
  55028. let fakeAxis, fakeSeries = void 0, ordinalIndex = ordinal.index;
  55029. // If this is the first time, or the ordinal index is deleted by
  55030. // updatedData,
  55031. // create it.
  55032. if (!ordinalIndex) {
  55033. ordinalIndex = ordinal.index = {};
  55034. }
  55035. if (!ordinalIndex[key]) {
  55036. // Create a fake axis object where the extended ordinal
  55037. // positions are emulated
  55038. fakeAxis = {
  55039. series: [],
  55040. chart: chart,
  55041. forceOrdinal: false,
  55042. getExtremes: function () {
  55043. return {
  55044. min: extremes.dataMin,
  55045. max: extremes.dataMax + overscroll
  55046. };
  55047. },
  55048. getGroupPixelWidth: axisProto.getGroupPixelWidth,
  55049. getTimeTicks: axisProto.getTimeTicks,
  55050. options: {
  55051. ordinal: true
  55052. },
  55053. ordinal: {
  55054. getGroupIntervalFactor: this.getGroupIntervalFactor
  55055. },
  55056. ordinal2lin: axisProto.ordinal2lin,
  55057. getIndexOfPoint: axisProto.getIndexOfPoint,
  55058. val2lin: axisProto.val2lin // #2590
  55059. };
  55060. fakeAxis.ordinal.axis = fakeAxis;
  55061. // Add the fake series to hold the full data, then apply
  55062. // processData to it
  55063. axis.series.forEach(function (series) {
  55064. fakeSeries = {
  55065. xAxis: fakeAxis,
  55066. xData: series.xData.slice(),
  55067. chart: chart,
  55068. destroyGroupedData: H.noop,
  55069. getProcessedData: Series.prototype.getProcessedData,
  55070. applyGrouping: Series.prototype.applyGrouping
  55071. };
  55072. fakeSeries.xData = fakeSeries.xData.concat(ordinal.getOverscrollPositions());
  55073. fakeSeries.options = {
  55074. dataGrouping: grouping ? {
  55075. firstAnchor: 'firstPoint',
  55076. anchor: 'middle',
  55077. lastAnchor: 'lastPoint',
  55078. enabled: true,
  55079. forced: true,
  55080. // doesn't matter which, use the fastest
  55081. approximation: 'open',
  55082. units: [[
  55083. grouping.unitName,
  55084. [grouping.count]
  55085. ]]
  55086. } : {
  55087. enabled: false
  55088. }
  55089. };
  55090. fakeAxis.series.push(fakeSeries);
  55091. series.processData.apply(fakeSeries);
  55092. });
  55093. // Force to use the ordinal when points are evenly spaced (e.g.
  55094. // weeks), #3825.
  55095. if ((fakeSeries.closestPointRange !==
  55096. fakeSeries.basePointRange) &&
  55097. fakeSeries.currentDataGrouping) {
  55098. fakeAxis.forceOrdinal = true;
  55099. }
  55100. // Run beforeSetTickPositions to compute the ordinalPositions
  55101. axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
  55102. // Cache it
  55103. ordinalIndex[key] = fakeAxis.ordinal.positions;
  55104. }
  55105. return ordinalIndex[key];
  55106. }
  55107. /**
  55108. * Find the factor to estimate how wide the plot area would have been if
  55109. * ordinal gaps were included. This value is used to compute an imagined
  55110. * plot width in order to establish the data grouping interval.
  55111. *
  55112. * A real world case is the intraday-candlestick example. Without this
  55113. * logic, it would show the correct data grouping when viewing a range
  55114. * within each day, but once moving the range to include the gap between
  55115. * two days, the interval would include the cut-away night hours and the
  55116. * data grouping would be wrong. So the below method tries to compensate
  55117. * by identifying the most common point interval, in this case days.
  55118. *
  55119. * An opposite case is presented in issue #718. We have a long array of
  55120. * daily data, then one point is appended one hour after the last point.
  55121. * We expect the data grouping not to change.
  55122. *
  55123. * In the future, if we find cases where this estimation doesn't work
  55124. * optimally, we might need to add a second pass to the data grouping
  55125. * logic, where we do another run with a greater interval if the number
  55126. * of data groups is more than a certain fraction of the desired group
  55127. * count.
  55128. * @private
  55129. */
  55130. getGroupIntervalFactor(xMin, xMax, series) {
  55131. const ordinal = this, axis = ordinal.axis, processedXData = series.processedXData, len = processedXData.length, distances = [];
  55132. let median, i, groupIntervalFactor = ordinal.groupIntervalFactor;
  55133. // Only do this computation for the first series, let the other
  55134. // inherit it (#2416)
  55135. if (!groupIntervalFactor) {
  55136. // Register all the distances in an array
  55137. for (i = 0; i < len - 1; i++) {
  55138. distances[i] = (processedXData[i + 1] -
  55139. processedXData[i]);
  55140. }
  55141. // Sort them and find the median
  55142. distances.sort(function (a, b) {
  55143. return a - b;
  55144. });
  55145. median = distances[Math.floor(len / 2)];
  55146. // Compensate for series that don't extend through the entire
  55147. // axis extent. #1675.
  55148. xMin = Math.max(xMin, processedXData[0]);
  55149. xMax = Math.min(xMax, processedXData[len - 1]);
  55150. ordinal.groupIntervalFactor = groupIntervalFactor =
  55151. (len * median) / (xMax - xMin);
  55152. }
  55153. // Return the factor needed for data grouping
  55154. return groupIntervalFactor;
  55155. }
  55156. /**
  55157. * Get index of point inside the ordinal positions array.
  55158. *
  55159. * @private
  55160. * @param {number} val
  55161. * The pixel value of a point.
  55162. *
  55163. * @param {Array<number>} [ordinallArray]
  55164. * An array of all points available on the axis for the given data set.
  55165. * Either ordinalPositions if the value is inside the plotArea or
  55166. * extendedOrdinalPositions if not.
  55167. */
  55168. getIndexOfPoint(val, ordinalArray) {
  55169. const ordinal = this, axis = ordinal.axis, firstPointVal = ordinal.positions ? ordinal.positions[0] : 0;
  55170. // Check whether the series has at least one point inside the chart
  55171. const hasPointsInside = function (series) {
  55172. return series.points.some((point) => !!point.isInside);
  55173. };
  55174. let firstPointX;
  55175. // When more series assign to axis, find the smallest one, #15987.
  55176. axis.series.forEach((series) => {
  55177. var _a;
  55178. const firstPoint = (_a = series.points) === null || _a === void 0 ? void 0 : _a[0];
  55179. if (defined(firstPoint === null || firstPoint === void 0 ? void 0 : firstPoint.plotX) &&
  55180. (firstPoint.plotX < firstPointX ||
  55181. !defined(firstPointX)) &&
  55182. hasPointsInside(series)) {
  55183. firstPointX = firstPoint.plotX;
  55184. }
  55185. });
  55186. // If undefined, give a default value
  55187. firstPointX !== null && firstPointX !== void 0 ? firstPointX : (firstPointX = axis.minPixelPadding);
  55188. // Distance in pixels between two points on the ordinal axis in the
  55189. // current zoom.
  55190. const ordinalPointPixelInterval = axis.translationSlope * (ordinal.slope ||
  55191. axis.closestPointRange ||
  55192. ordinal.overscrollPointsRange),
  55193. // toValue for the first point.
  55194. shiftIndex = correctFloat((val - firstPointX) / ordinalPointPixelInterval);
  55195. return Additions.findIndexOf(ordinalArray, firstPointVal, true) + shiftIndex;
  55196. }
  55197. /**
  55198. * Get ticks for an ordinal axis within a range where points don't
  55199. * exist. It is required when overscroll is enabled. We can't base on
  55200. * points, because we may not have any, so we use approximated
  55201. * pointRange and generate these ticks between Axis.dataMax,
  55202. * Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
  55203. * navigator scrolling.
  55204. * @private
  55205. */
  55206. getOverscrollPositions() {
  55207. const ordinal = this, axis = ordinal.axis, extraRange = axis.options.overscroll, distance = ordinal.overscrollPointsRange, positions = [];
  55208. let max = axis.dataMax;
  55209. if (defined(distance)) {
  55210. // Max + pointRange because we need to scroll to the last
  55211. while (max <= axis.dataMax + extraRange) {
  55212. max += distance;
  55213. positions.push(max);
  55214. }
  55215. }
  55216. return positions;
  55217. }
  55218. /**
  55219. * Make the tick intervals closer because the ordinal gaps make the
  55220. * ticks spread out or cluster.
  55221. * @private
  55222. */
  55223. postProcessTickInterval(tickInterval) {
  55224. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/. This is a case
  55225. // where this algorithm doesn't work optimally. In this case, the
  55226. // tick labels are spread out per week, but all the gaps reside
  55227. // within weeks. So we have a situation where the labels are courser
  55228. // than the ordinal gaps, and thus the tick interval should not be
  55229. // altered.
  55230. const ordinal = this, axis = ordinal.axis, ordinalSlope = ordinal.slope;
  55231. let ret;
  55232. if (ordinalSlope) {
  55233. if (!axis.options.breaks) {
  55234. ret = (tickInterval /
  55235. (ordinalSlope / axis.closestPointRange));
  55236. }
  55237. else {
  55238. ret = axis.closestPointRange || tickInterval; // #7275
  55239. }
  55240. }
  55241. else {
  55242. ret = tickInterval;
  55243. }
  55244. return ret;
  55245. }
  55246. }
  55247. OrdinalAxis.Additions = Additions;
  55248. })(OrdinalAxis || (OrdinalAxis = {}));
  55249. /* *
  55250. *
  55251. * Default Export
  55252. *
  55253. * */
  55254. return OrdinalAxis;
  55255. });
  55256. _registerModule(_modules, 'Series/HLC/HLCPoint.js', [_modules['Core/Series/SeriesRegistry.js']], function (SeriesRegistry) {
  55257. /* *
  55258. *
  55259. * (c) 2010-2021 Pawel Lysy
  55260. *
  55261. * License: www.highcharts.com/license
  55262. *
  55263. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55264. *
  55265. * */
  55266. const { column: { prototype: { pointClass: ColumnPoint } } } = SeriesRegistry.seriesTypes;
  55267. /* *
  55268. *
  55269. * Class
  55270. *
  55271. * */
  55272. class HLCPoint extends ColumnPoint {
  55273. constructor() {
  55274. /* *
  55275. *
  55276. * Properties
  55277. *
  55278. * */
  55279. super(...arguments);
  55280. this.close = void 0;
  55281. this.high = void 0;
  55282. this.low = void 0;
  55283. this.options = void 0;
  55284. this.plotClose = void 0;
  55285. this.series = void 0;
  55286. }
  55287. }
  55288. /* *
  55289. *
  55290. * Default Export
  55291. *
  55292. * */
  55293. return HLCPoint;
  55294. });
  55295. _registerModule(_modules, 'Series/HLC/HLCSeriesDefaults.js', [], function () {
  55296. /* *
  55297. *
  55298. * (c) 2010-2021 Pawel Lysy
  55299. *
  55300. * License: www.highcharts.com/license
  55301. *
  55302. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55303. *
  55304. * */
  55305. /* *
  55306. *
  55307. * API Options
  55308. *
  55309. * */
  55310. /**
  55311. * An HLC chart is a style of financial chart used to describe price
  55312. * movements over time. It displays high, low and close values per
  55313. * data point.
  55314. *
  55315. * @sample stock/demo/hlc/
  55316. * HLC chart
  55317. *
  55318. * @extends plotOptions.column
  55319. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  55320. * stack
  55321. * @product highstock
  55322. * @optionparent plotOptions.hlc
  55323. */
  55324. const HLCSeriesDefaults = {
  55325. /**
  55326. * The approximate pixel width of each group. If for example a series
  55327. * with 30 points is displayed over a 600 pixel wide plot area, no
  55328. * grouping is performed. If however the series contains so many points
  55329. * that the spacing is less than the groupPixelWidth, Highcharts will
  55330. * try to group it into appropriate groups so that each is more or less
  55331. * two pixels wide. Defaults to `5`.
  55332. *
  55333. * @type {number}
  55334. * @default 5
  55335. * @product highstock
  55336. * @apioption plotOptions.hlc.dataGrouping.groupPixelWidth
  55337. */
  55338. /**
  55339. * @type {Highcharts.DataGroupingApproximationValue|Function}
  55340. * @default hlc
  55341. * @product highstock
  55342. * @apioption plotOptions.hlc.dataGrouping.approximation
  55343. */
  55344. /**
  55345. * @default close
  55346. * @apioption plotOptions.hlc.colorKey
  55347. */
  55348. /**
  55349. * The pixel width of the line/border. Defaults to `1`.
  55350. *
  55351. * @sample {highstock} stock/plotoptions/hlc-linewidth/
  55352. * A greater line width
  55353. *
  55354. * @type {number}
  55355. * @default 1
  55356. * @product highstock
  55357. *
  55358. * @public
  55359. */
  55360. lineWidth: 1,
  55361. tooltip: {
  55362. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  55363. '<b> {series.name}</b><br/>' +
  55364. 'High: {point.high}<br/>' +
  55365. 'Low: {point.low}<br/>' +
  55366. 'Close: {point.close}<br/>'
  55367. },
  55368. /**
  55369. * @type {number|null}
  55370. */
  55371. threshold: null,
  55372. states: {
  55373. /**
  55374. * @extends plotOptions.column.states.hover
  55375. * @product highstock
  55376. */
  55377. hover: {
  55378. /**
  55379. * The pixel width of the line representing the HLC point.
  55380. *
  55381. * @type {number}
  55382. * @default 3
  55383. * @product highstock
  55384. */
  55385. lineWidth: 3
  55386. }
  55387. },
  55388. /**
  55389. * Determines which one of `high`, `low`, `close` values should
  55390. * be represented as `point.y`, which is later used to set dataLabel
  55391. * position and [compare](#plotOptions.series.compare).
  55392. *
  55393. * @sample {highstock} stock/plotoptions/hlc-pointvalkey/
  55394. * Possible values
  55395. *
  55396. * @declare Highcharts.OptionsHLCPointValKeyValue
  55397. * @type {string}
  55398. * @default close
  55399. * @validvalue ["high", "low", "close"]
  55400. * @product highstock
  55401. * @apioption plotOptions.hlc.pointValKey
  55402. */
  55403. /**
  55404. * @default close
  55405. * @apioption plotOptions.hlc.colorKey
  55406. */
  55407. stickyTracking: true
  55408. };
  55409. /**
  55410. * A `hlc` series. If the [type](#series.hlc.type) option is not
  55411. * specified, it is inherited from [chart.type](#chart.type).
  55412. *
  55413. * @extends series,plotOptions.hlc
  55414. * @excluding dataParser, dataURL
  55415. * @product highstock
  55416. * @apioption series.hlc
  55417. */
  55418. /**
  55419. * An array of data points for the series. For the `hlc` series type,
  55420. * points can be given in the following ways:
  55421. *
  55422. * 1. An array of arrays with 4 or 3 values. In this case, the values correspond
  55423. * to `x,high,low,close`. If the first value is a string, it is applied
  55424. * as the name of the point, and the `x` value is inferred. The `x` value can
  55425. * also be omitted, in which case the inner arrays should be of length of 3\.
  55426. * Then the `x` value is automatically calculated, either starting at 0 and
  55427. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  55428. * series options.
  55429. * ```js
  55430. * data: [
  55431. * [0, 5, 6, 7],
  55432. * [1, 4, 8, 2],
  55433. * [2, 3, 4, 10]
  55434. * ]
  55435. * ```
  55436. *
  55437. * 2. An array of objects with named values. The following snippet shows only a
  55438. * few settings, see the complete options set below. If the total number of
  55439. * data points exceeds the series'
  55440. * [turboThreshold](#series.hlc.turboThreshold), this option is not
  55441. * available.
  55442. * ```js
  55443. * data: [{
  55444. * x: 1,
  55445. * high: 4,
  55446. * low: 5,
  55447. * close: 2,
  55448. * name: "Point2",
  55449. * color: "#00FF00"
  55450. * }, {
  55451. * x: 1,
  55452. * high: 3,
  55453. * low: 6,
  55454. * close: 7,
  55455. * name: "Point1",
  55456. * color: "#FF00FF"
  55457. * }]
  55458. * ```
  55459. *
  55460. * @type {Array<Array<(number|string),number,number>|Array<(number|string),number,number,number>|*>}
  55461. * @extends series.arearange.data
  55462. * @excluding y, marker
  55463. * @product highstock
  55464. * @apioption series.hlc.data
  55465. */
  55466. /**
  55467. * The closing value of each data point.
  55468. *
  55469. * @type {number}
  55470. * @product highstock
  55471. * @apioption series.hlc.data.close
  55472. */
  55473. (''); // keeps doclets above in JS file
  55474. /* *
  55475. *
  55476. * Default Export
  55477. *
  55478. * */
  55479. return HLCSeriesDefaults;
  55480. });
  55481. _registerModule(_modules, 'Series/HLC/HLCSeries.js', [_modules['Series/HLC/HLCPoint.js'], _modules['Series/HLC/HLCSeriesDefaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (HLCPoint, HLCSeriesDefaults, SeriesRegistry, U) {
  55482. /* *
  55483. *
  55484. * (c) 2010-2021 Pawel Lysy
  55485. *
  55486. * License: www.highcharts.com/license
  55487. *
  55488. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55489. *
  55490. * */
  55491. const { column: ColumnSeries } = SeriesRegistry.seriesTypes;
  55492. const { extend, merge } = U;
  55493. /* *
  55494. *
  55495. * Class
  55496. *
  55497. * */
  55498. /**
  55499. * The hlc series type.
  55500. *
  55501. * @private
  55502. * @class
  55503. * @name Highcharts.seriesTypes.hlc
  55504. *
  55505. * @augments Highcharts.Series
  55506. */
  55507. class HLCSeries extends ColumnSeries {
  55508. constructor() {
  55509. /* *
  55510. *
  55511. * Static Properties
  55512. *
  55513. * */
  55514. super(...arguments);
  55515. /* *
  55516. *
  55517. * Properties
  55518. *
  55519. * */
  55520. this.data = void 0;
  55521. this.options = void 0;
  55522. this.points = void 0;
  55523. this.yData = void 0;
  55524. }
  55525. /* *
  55526. *
  55527. * Functions
  55528. *
  55529. * */
  55530. /**
  55531. * Extend the path if close is not between high and low.
  55532. *
  55533. * @param {SVGPath} path the path array of the point
  55534. * @param {number} halfStrokeWidth
  55535. * @param {number} value value of the point to which the stem should be extended
  55536. */
  55537. extendStem(path, halfStrokeWidth, value) {
  55538. const start = path[0];
  55539. const end = path[1];
  55540. // We don't need to worry about crisp - close value
  55541. // is already crisped and halfStrokeWidth should remove it.
  55542. if (typeof start[2] === 'number') {
  55543. start[2] = Math.max(value + halfStrokeWidth, start[2]);
  55544. }
  55545. if (typeof end[2] === 'number') {
  55546. end[2] = Math.min(value - halfStrokeWidth, end[2]);
  55547. }
  55548. }
  55549. /**
  55550. * Function to create SVGPath of the point based on the
  55551. * plot positions of this point.
  55552. * @private
  55553. */
  55554. getPointPath(point, graphic) {
  55555. // crisp vector coordinates
  55556. const strokeWidth = graphic.strokeWidth(), series = point.series, crispCorr = (strokeWidth % 2) / 2,
  55557. // #2596:
  55558. crispX = Math.round(point.plotX) - crispCorr, halfWidth = Math.round(point.shapeArgs.width / 2);
  55559. let plotClose = point.plotClose;
  55560. // the vertical stem
  55561. const path = [
  55562. ['M', crispX, Math.round(point.yBottom)],
  55563. ['L', crispX, Math.round(point.plotHigh)]
  55564. ];
  55565. // close
  55566. if (point.close !== null) {
  55567. plotClose = Math.round(point.plotClose) + crispCorr;
  55568. path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
  55569. series.extendStem(path, strokeWidth / 2, plotClose);
  55570. }
  55571. return path;
  55572. }
  55573. /**
  55574. * Draw single point
  55575. * @private
  55576. */
  55577. drawSinglePoint(point) {
  55578. const series = point.series, chart = series.chart;
  55579. let path, graphic = point.graphic;
  55580. if (typeof point.plotY !== 'undefined') {
  55581. // Create and/or update the graphic
  55582. if (!graphic) {
  55583. point.graphic = graphic = chart.renderer.path()
  55584. .add(series.group);
  55585. }
  55586. if (!chart.styledMode) {
  55587. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  55588. }
  55589. // crisp vector coordinates
  55590. path = series.getPointPath(point, graphic);
  55591. graphic[!graphic ? 'attr' : 'animate']({ d: path })
  55592. .addClass(point.getClassName(), true);
  55593. }
  55594. }
  55595. /**
  55596. * Draw the data points
  55597. * @private
  55598. */
  55599. drawPoints() {
  55600. this.points.forEach(this.drawSinglePoint);
  55601. }
  55602. /**
  55603. * @private
  55604. * @function Highcharts.seriesTypes.hlc#init
  55605. */
  55606. init() {
  55607. super.init.apply(this, arguments);
  55608. this.options.stacking = void 0; // #8817
  55609. }
  55610. /**
  55611. * Postprocess mapping between options and SVG attributes
  55612. * @private
  55613. */
  55614. pointAttribs(point, state) {
  55615. const attribs = super.pointAttribs.call(this, point, state);
  55616. delete attribs.fill;
  55617. return attribs;
  55618. }
  55619. toYData(point) {
  55620. // return a plain array for speedy calculation
  55621. return [point.high, point.low, point.close];
  55622. }
  55623. /**
  55624. * Translate data points from raw values x and y to plotX and plotY
  55625. *
  55626. * @private
  55627. * @function Highcharts.seriesTypes.hlc#translate
  55628. */
  55629. translate() {
  55630. const series = this, yAxis = series.yAxis, names = (this.pointArrayMap && this.pointArrayMap.slice()) || [], translated = names.map((name) => `plot${name.charAt(0).toUpperCase() + name.slice(1)}`);
  55631. translated.push('yBottom');
  55632. names.push('low');
  55633. super.translate.apply(series);
  55634. // Do the translation
  55635. series.points.forEach(function (point) {
  55636. names.forEach(function (name, i) {
  55637. let value = point[name];
  55638. if (value !== null) {
  55639. if (series.dataModify) {
  55640. value = series.dataModify.modifyValue(value);
  55641. }
  55642. point[translated[i]] =
  55643. yAxis.toPixels(value, true);
  55644. }
  55645. });
  55646. // Align the tooltip to the high value to avoid covering the
  55647. // point
  55648. point.tooltipPos[1] =
  55649. point.plotHigh + yAxis.pos - series.chart.plotTop;
  55650. });
  55651. }
  55652. }
  55653. HLCSeries.defaultOptions = merge(ColumnSeries.defaultOptions, HLCSeriesDefaults);
  55654. extend(HLCSeries.prototype, {
  55655. pointClass: HLCPoint,
  55656. animate: null,
  55657. directTouch: false,
  55658. pointArrayMap: ['high', 'low', 'close'],
  55659. pointAttrToOptions: {
  55660. stroke: 'color',
  55661. 'stroke-width': 'lineWidth'
  55662. },
  55663. pointValKey: 'close'
  55664. });
  55665. SeriesRegistry.registerSeriesType('hlc', HLCSeries);
  55666. /* *
  55667. *
  55668. * Default Export
  55669. *
  55670. * */
  55671. return HLCSeries;
  55672. });
  55673. _registerModule(_modules, 'Series/OHLC/OHLCPoint.js', [_modules['Core/Series/SeriesRegistry.js']], function (SeriesRegistry) {
  55674. /* *
  55675. *
  55676. * (c) 2010-2021 Torstein Honsi
  55677. *
  55678. * License: www.highcharts.com/license
  55679. *
  55680. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55681. *
  55682. * */
  55683. const { seriesTypes: { hlc: HLCSeries } } = SeriesRegistry;
  55684. /* *
  55685. *
  55686. * Class
  55687. *
  55688. * */
  55689. class OHLCPoint extends HLCSeries.prototype.pointClass {
  55690. constructor() {
  55691. /* *
  55692. *
  55693. * Properties
  55694. *
  55695. * */
  55696. super(...arguments);
  55697. this.open = void 0;
  55698. this.options = void 0;
  55699. this.plotOpen = void 0;
  55700. this.series = void 0;
  55701. }
  55702. /* *
  55703. *
  55704. * Functions
  55705. *
  55706. * */
  55707. /**
  55708. * Extend the parent method by adding up or down to the class name.
  55709. * @private
  55710. * @function Highcharts.seriesTypes.ohlc#getClassName
  55711. */
  55712. getClassName() {
  55713. return super.getClassName.call(this) +
  55714. (this.open < this.close ?
  55715. ' highcharts-point-up' :
  55716. ' highcharts-point-down');
  55717. }
  55718. /**
  55719. * Save upColor as point color (#14826).
  55720. * @private
  55721. * @function Highcharts.seriesTypes.ohlc#resolveUpColor
  55722. */
  55723. resolveUpColor() {
  55724. if (this.open < this.close &&
  55725. !this.options.color &&
  55726. this.series.options.upColor) {
  55727. this.color = this.series.options.upColor;
  55728. }
  55729. }
  55730. /**
  55731. * Extend the parent method by saving upColor.
  55732. * @private
  55733. * @function Highcharts.seriesTypes.ohlc#resolveColor
  55734. */
  55735. resolveColor() {
  55736. super.resolveColor();
  55737. this.resolveUpColor();
  55738. }
  55739. /**
  55740. * Extend the parent method by saving upColor.
  55741. * @private
  55742. * @function Highcharts.seriesTypes.ohlc#getZone
  55743. *
  55744. * @return {Highcharts.SeriesZonesOptionsObject}
  55745. * The zone item.
  55746. */
  55747. getZone() {
  55748. const zone = super.getZone();
  55749. this.resolveUpColor();
  55750. return zone;
  55751. }
  55752. /**
  55753. * Extend the parent method by resolving up/down colors (#15849)
  55754. * @private
  55755. **/
  55756. applyOptions() {
  55757. super.applyOptions.apply(this, arguments);
  55758. if (this.resolveColor) {
  55759. this.resolveColor();
  55760. }
  55761. return this;
  55762. }
  55763. }
  55764. /* *
  55765. *
  55766. * Default Export
  55767. *
  55768. * */
  55769. return OHLCPoint;
  55770. });
  55771. _registerModule(_modules, 'Series/OHLC/OHLCSeriesDefaults.js', [], function () {
  55772. /* *
  55773. *
  55774. * (c) 2010-2021 Torstein Honsi
  55775. *
  55776. * License: www.highcharts.com/license
  55777. *
  55778. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55779. *
  55780. * */
  55781. /* *
  55782. *
  55783. * API Options
  55784. *
  55785. * */
  55786. /**
  55787. * An OHLC chart is a style of financial chart used to describe price
  55788. * movements over time. It displays open, high, low and close values per
  55789. * data point.
  55790. *
  55791. * @sample stock/demo/ohlc
  55792. * OHLC chart
  55793. *
  55794. * @extends plotOptions.hlc
  55795. * @product highstock
  55796. * @optionparent plotOptions.ohlc
  55797. */
  55798. const OHLCSeriesDefaults = {
  55799. /**
  55800. * @type {Highcharts.DataGroupingApproximationValue|Function}
  55801. * @default ohlc
  55802. * @product highstock
  55803. * @apioption plotOptions.ohlc.dataGrouping.approximation
  55804. */
  55805. /**
  55806. * Determines which one of `open`, `high`, `low`, `close` values should
  55807. * be represented as `point.y`, which is later used to set dataLabel
  55808. * position and [compare](#plotOptions.series.compare).
  55809. *
  55810. * @declare Highcharts.OptionsPointValKeyValue
  55811. * @default close
  55812. * @validvalue ["open", "high", "low", "close"]
  55813. * @product highstock
  55814. * @apioption plotOptions.ohlc.pointValKey
  55815. */
  55816. /**
  55817. * Line color for up points.
  55818. *
  55819. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  55820. * @product highstock
  55821. * @apioption plotOptions.ohlc.upColor
  55822. */
  55823. tooltip: {
  55824. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  55825. '<b> {series.name}</b><br/>' +
  55826. 'Open: {point.open}<br/>' +
  55827. 'High: {point.high}<br/>' +
  55828. 'Low: {point.low}<br/>' +
  55829. 'Close: {point.close}<br/>'
  55830. }
  55831. };
  55832. /**
  55833. * The parameter allows setting line series type and use OHLC indicators.
  55834. * Data in OHLC format is required.
  55835. *
  55836. * @sample {highstock} stock/indicators/use-ohlc-data
  55837. * Use OHLC data format to plot line chart
  55838. *
  55839. * @type {boolean}
  55840. * @product highstock
  55841. * @apioption plotOptions.line.useOhlcData
  55842. */
  55843. /**
  55844. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  55845. * specified, it is inherited from [chart.type](#chart.type).
  55846. *
  55847. * @extends series,plotOptions.ohlc
  55848. * @excluding dataParser, dataURL
  55849. * @product highstock
  55850. * @apioption series.ohlc
  55851. */
  55852. /**
  55853. * An array of data points for the series. For the `ohlc` series type,
  55854. * points can be given in the following ways:
  55855. *
  55856. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  55857. * to `x,open,high,low,close`. If the first value is a string, it is applied
  55858. * as the name of the point, and the `x` value is inferred. The `x` value can
  55859. * also be omitted, in which case the inner arrays should be of length 4\.
  55860. * Then the `x` value is automatically calculated, either starting at 0 and
  55861. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  55862. * series options.
  55863. * ```js
  55864. * data: [
  55865. * [0, 6, 5, 6, 7],
  55866. * [1, 9, 4, 8, 2],
  55867. * [2, 6, 3, 4, 10]
  55868. * ]
  55869. * ```
  55870. *
  55871. * 2. An array of objects with named values. The following snippet shows only a
  55872. * few settings, see the complete options set below. If the total number of
  55873. * data points exceeds the series'
  55874. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  55875. * available.
  55876. * ```js
  55877. * data: [{
  55878. * x: 1,
  55879. * open: 3,
  55880. * high: 4,
  55881. * low: 5,
  55882. * close: 2,
  55883. * name: "Point2",
  55884. * color: "#00FF00"
  55885. * }, {
  55886. * x: 1,
  55887. * open: 4,
  55888. * high: 3,
  55889. * low: 6,
  55890. * close: 7,
  55891. * name: "Point1",
  55892. * color: "#FF00FF"
  55893. * }]
  55894. * ```
  55895. *
  55896. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  55897. * @extends series.arearange.data
  55898. * @excluding y, marker
  55899. * @product highstock
  55900. * @apioption series.ohlc.data
  55901. */
  55902. /**
  55903. * The closing value of each data point.
  55904. *
  55905. * @type {number}
  55906. * @product highstock
  55907. * @apioption series.ohlc.data.close
  55908. */
  55909. /**
  55910. * The opening value of each data point.
  55911. *
  55912. * @type {number}
  55913. * @product highstock
  55914. * @apioption series.ohlc.data.open
  55915. */
  55916. ''; // adds doclets above to transpilat
  55917. /* *
  55918. *
  55919. * Default Export
  55920. *
  55921. * */
  55922. return OHLCSeriesDefaults;
  55923. });
  55924. _registerModule(_modules, 'Series/OHLC/OHLCSeries.js', [_modules['Series/OHLC/OHLCPoint.js'], _modules['Series/OHLC/OHLCSeriesDefaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (OHLCPoint, OHLCSeriesDefaults, SeriesRegistry, U) {
  55925. /* *
  55926. *
  55927. * (c) 2010-2021 Torstein Honsi
  55928. *
  55929. * License: www.highcharts.com/license
  55930. *
  55931. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55932. *
  55933. * */
  55934. const { seriesTypes: { hlc: HLCSeries } } = SeriesRegistry;
  55935. const { addEvent, extend, merge } = U;
  55936. /* *
  55937. *
  55938. * Constants
  55939. *
  55940. * */
  55941. const composedMembers = [];
  55942. /* *
  55943. *
  55944. * Functions
  55945. *
  55946. * */
  55947. /**
  55948. * @private
  55949. */
  55950. function onSeriesAfterSetOptions(e) {
  55951. const options = e.options, dataGrouping = options.dataGrouping;
  55952. if (dataGrouping &&
  55953. options.useOhlcData &&
  55954. options.id !== 'highcharts-navigator-series') {
  55955. dataGrouping.approximation = 'ohlc';
  55956. }
  55957. }
  55958. /**
  55959. * Add useOhlcData option
  55960. * @private
  55961. */
  55962. function onSeriesInit(eventOptions) {
  55963. // eslint-disable-next-line no-invalid-this
  55964. const series = this, options = eventOptions.options;
  55965. if (options.useOhlcData &&
  55966. options.id !== 'highcharts-navigator-series') {
  55967. extend(series, {
  55968. pointValKey: OHLCSeries.prototype.pointValKey,
  55969. // keys: ohlcProto.keys, // @todo potentially nonsense
  55970. pointArrayMap: OHLCSeries.prototype.pointArrayMap,
  55971. toYData: OHLCSeries.prototype.toYData
  55972. });
  55973. }
  55974. }
  55975. /* *
  55976. *
  55977. * Class
  55978. *
  55979. * */
  55980. /**
  55981. * The ohlc series type.
  55982. *
  55983. * @private
  55984. * @class
  55985. * @name Highcharts.seriesTypes.ohlc
  55986. *
  55987. * @augments Highcharts.Series
  55988. */
  55989. class OHLCSeries extends HLCSeries {
  55990. constructor() {
  55991. /* *
  55992. *
  55993. * Static Properties
  55994. *
  55995. * */
  55996. super(...arguments);
  55997. /* *
  55998. *
  55999. * Properties
  56000. *
  56001. * */
  56002. this.data = void 0;
  56003. this.options = void 0;
  56004. this.points = void 0;
  56005. }
  56006. /* *
  56007. *
  56008. * Static Functions
  56009. *
  56010. * */
  56011. static compose(SeriesClass, ..._args) {
  56012. if (U.pushUnique(composedMembers, SeriesClass)) {
  56013. addEvent(SeriesClass, 'afterSetOptions', onSeriesAfterSetOptions);
  56014. addEvent(SeriesClass, 'init', onSeriesInit);
  56015. }
  56016. }
  56017. /* *
  56018. *
  56019. * Functions
  56020. *
  56021. * */
  56022. getPointPath(point, graphic) {
  56023. const path = super.getPointPath(point, graphic), strokeWidth = graphic.strokeWidth(), crispCorr = (strokeWidth % 2) / 2, crispX = Math.round(point.plotX) - crispCorr, halfWidth = Math.round(point.shapeArgs.width / 2);
  56024. let plotOpen = point.plotOpen;
  56025. // crisp vector coordinates
  56026. if (point.open !== null) {
  56027. plotOpen = Math.round(point.plotOpen) + crispCorr;
  56028. path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
  56029. super.extendStem(path, strokeWidth / 2, plotOpen);
  56030. }
  56031. return path;
  56032. }
  56033. /**
  56034. * Postprocess mapping between options and SVG attributes
  56035. * @private
  56036. */
  56037. pointAttribs(point, state) {
  56038. const attribs = super.pointAttribs.call(this, point, state), options = this.options;
  56039. delete attribs.fill;
  56040. if (!point.options.color &&
  56041. options.upColor &&
  56042. point.open < point.close) {
  56043. attribs.stroke = options.upColor;
  56044. }
  56045. return attribs;
  56046. }
  56047. toYData(point) {
  56048. // return a plain array for speedy calculation
  56049. return [point.open, point.high, point.low, point.close];
  56050. }
  56051. }
  56052. OHLCSeries.defaultOptions = merge(HLCSeries.defaultOptions, OHLCSeriesDefaults);
  56053. extend(OHLCSeries.prototype, {
  56054. pointClass: OHLCPoint,
  56055. pointArrayMap: ['open', 'high', 'low', 'close']
  56056. });
  56057. SeriesRegistry.registerSeriesType('ohlc', OHLCSeries);
  56058. /* *
  56059. *
  56060. * Default Export
  56061. *
  56062. * */
  56063. return OHLCSeries;
  56064. });
  56065. _registerModule(_modules, 'Series/Candlestick/CandlestickSeriesDefaults.js', [_modules['Core/Defaults.js'], _modules['Core/Utilities.js']], function (D, U) {
  56066. /* *
  56067. *
  56068. * (c) 2010-2021 Torstein Honsi
  56069. *
  56070. * License: www.highcharts.com/license
  56071. *
  56072. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56073. *
  56074. * */
  56075. const { defaultOptions } = D;
  56076. const { merge } = U;
  56077. /* *
  56078. *
  56079. * API Options
  56080. *
  56081. * */
  56082. /**
  56083. * A candlestick chart is a style of financial chart used to describe
  56084. * price movements over time.
  56085. *
  56086. * @sample stock/demo/candlestick/
  56087. * Candlestick chart
  56088. *
  56089. * @extends plotOptions.ohlc
  56090. * @excluding borderColor,borderRadius,borderWidth
  56091. * @product highstock
  56092. * @optionparent plotOptions.candlestick
  56093. */
  56094. const CandlestickSeriesDefaults = {
  56095. /**
  56096. * The specific line color for up candle sticks. The default is to
  56097. * inherit the general `lineColor` setting.
  56098. *
  56099. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  56100. * Candlestick line colors
  56101. *
  56102. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56103. * @since 1.3.6
  56104. * @product highstock
  56105. * @apioption plotOptions.candlestick.upLineColor
  56106. */
  56107. states: {
  56108. /**
  56109. * @extends plotOptions.column.states.hover
  56110. * @product highstock
  56111. */
  56112. hover: {
  56113. /**
  56114. * The pixel width of the line/border around the
  56115. * candlestick.
  56116. *
  56117. * @product highstock
  56118. */
  56119. lineWidth: 2
  56120. }
  56121. },
  56122. /**
  56123. * @type {number|null}
  56124. * @product highstock
  56125. */
  56126. threshold: null,
  56127. /**
  56128. * The color of the line/border of the candlestick.
  56129. *
  56130. * In styled mode, the line stroke can be set with the
  56131. * `.highcharts-candlestick-series .highcahrts-point` rule.
  56132. *
  56133. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  56134. *
  56135. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  56136. * Candlestick line colors
  56137. *
  56138. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56139. * @default #000000
  56140. * @product highstock
  56141. */
  56142. lineColor: "#000000" /* Palette.neutralColor100 */,
  56143. /**
  56144. * The pixel width of the candlestick line/border. Defaults to `1`.
  56145. *
  56146. *
  56147. * In styled mode, the line stroke width can be set with the
  56148. * `.highcharts-candlestick-series .highcahrts-point` rule.
  56149. *
  56150. * @product highstock
  56151. */
  56152. lineWidth: 1,
  56153. /**
  56154. * The fill color of the candlestick when values are rising.
  56155. *
  56156. * In styled mode, the up color can be set with the
  56157. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  56158. *
  56159. * @sample {highstock} stock/plotoptions/candlestick-color/
  56160. * Custom colors
  56161. * @sample {highstock} highcharts/css/candlestick/
  56162. * Colors in styled mode
  56163. *
  56164. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56165. * @default #ffffff
  56166. * @product highstock
  56167. */
  56168. upColor: "#ffffff" /* Palette.backgroundColor */,
  56169. /**
  56170. * @product highstock
  56171. */
  56172. stickyTracking: true
  56173. };
  56174. /**
  56175. * A `candlestick` series. If the [type](#series.candlestick.type)
  56176. * option is not specified, it is inherited from [chart.type](
  56177. * #chart.type).
  56178. *
  56179. * @type {*}
  56180. * @extends series,plotOptions.candlestick
  56181. * @excluding dataParser, dataURL, marker
  56182. * @product highstock
  56183. * @apioption series.candlestick
  56184. */
  56185. /**
  56186. * An array of data points for the series. For the `candlestick` series
  56187. * type, points can be given in the following ways:
  56188. *
  56189. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  56190. * to `x,open,high,low,close`. If the first value is a string, it is applied
  56191. * as the name of the point, and the `x` value is inferred. The `x` value can
  56192. * also be omitted, in which case the inner arrays should be of length 4.
  56193. * Then the `x` value is automatically calculated, either starting at 0 and
  56194. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  56195. * series options.
  56196. * ```js
  56197. * data: [
  56198. * [0, 7, 2, 0, 4],
  56199. * [1, 1, 4, 2, 8],
  56200. * [2, 3, 3, 9, 3]
  56201. * ]
  56202. * ```
  56203. *
  56204. * 2. An array of objects with named values. The following snippet shows only a
  56205. * few settings, see the complete options set below. If the total number of
  56206. * data points exceeds the series'
  56207. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  56208. * available.
  56209. * ```js
  56210. * data: [{
  56211. * x: 1,
  56212. * open: 9,
  56213. * high: 2,
  56214. * low: 4,
  56215. * close: 6,
  56216. * name: "Point2",
  56217. * color: "#00FF00"
  56218. * }, {
  56219. * x: 1,
  56220. * open: 1,
  56221. * high: 4,
  56222. * low: 7,
  56223. * close: 7,
  56224. * name: "Point1",
  56225. * color: "#FF00FF"
  56226. * }]
  56227. * ```
  56228. *
  56229. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  56230. * @extends series.ohlc.data
  56231. * @excluding y
  56232. * @product highstock
  56233. * @apioption series.candlestick.data
  56234. */
  56235. ''; // adds doclets above to transpilat
  56236. /* *
  56237. *
  56238. * Default Export
  56239. *
  56240. * */
  56241. return CandlestickSeriesDefaults;
  56242. });
  56243. _registerModule(_modules, 'Series/Candlestick/CandlestickSeries.js', [_modules['Series/Candlestick/CandlestickSeriesDefaults.js'], _modules['Core/Defaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (CandlestickSeriesDefaults, D, SeriesRegistry, U) {
  56244. /* *
  56245. *
  56246. * (c) 2010-2021 Torstein Honsi
  56247. *
  56248. * License: www.highcharts.com/license
  56249. *
  56250. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56251. *
  56252. * */
  56253. const { defaultOptions } = D;
  56254. const { column: ColumnSeries, ohlc: OHLCSeries } = SeriesRegistry.seriesTypes;
  56255. const { merge } = U;
  56256. /* *
  56257. *
  56258. * Class
  56259. *
  56260. * */
  56261. /**
  56262. * The candlestick series type.
  56263. *
  56264. * @private
  56265. * @class
  56266. * @name Highcharts.seriesTypes.candlestick
  56267. *
  56268. * @augments Highcharts.seriesTypes.ohlc
  56269. */
  56270. class CandlestickSeries extends OHLCSeries {
  56271. constructor() {
  56272. /* *
  56273. *
  56274. * Static Properties
  56275. *
  56276. * */
  56277. super(...arguments);
  56278. /* *
  56279. *
  56280. * Properties
  56281. *
  56282. * */
  56283. this.data = void 0;
  56284. this.options = void 0;
  56285. this.points = void 0;
  56286. }
  56287. /* *
  56288. *
  56289. * Functions
  56290. *
  56291. * */
  56292. /**
  56293. * Postprocess mapping between options and SVG attributes
  56294. *
  56295. * @private
  56296. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  56297. */
  56298. pointAttribs(point, state) {
  56299. const attribs = ColumnSeries.prototype.pointAttribs.call(this, point, state), options = this.options, isUp = point.open < point.close, stroke = options.lineColor || this.color, color = point.color || this.color; // (#14826)
  56300. attribs['stroke-width'] = options.lineWidth;
  56301. attribs.fill = point.options.color ||
  56302. (isUp ? (options.upColor || color) : color);
  56303. attribs.stroke = point.options.lineColor ||
  56304. (isUp ? (options.upLineColor || stroke) : stroke);
  56305. // Select or hover states
  56306. if (state) {
  56307. const stateOptions = options.states[state];
  56308. attribs.fill = stateOptions.color || attribs.fill;
  56309. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  56310. attribs['stroke-width'] =
  56311. stateOptions.lineWidth || attribs['stroke-width'];
  56312. }
  56313. return attribs;
  56314. }
  56315. /**
  56316. * Draw the data points.
  56317. *
  56318. * @private
  56319. * @function Highcharts.seriesTypes.candlestick#drawPoints
  56320. */
  56321. drawPoints() {
  56322. const series = this, points = series.points, chart = series.chart, reversedYAxis = series.yAxis.reversed;
  56323. for (const point of points) {
  56324. let graphic = point.graphic, plotOpen, plotClose, topBox, bottomBox, hasTopWhisker, hasBottomWhisker, crispCorr, crispX, path, halfWidth;
  56325. const isNew = !graphic;
  56326. if (typeof point.plotY !== 'undefined') {
  56327. if (!graphic) {
  56328. point.graphic = graphic = chart.renderer.path()
  56329. .add(series.group);
  56330. }
  56331. if (!series.chart.styledMode) {
  56332. graphic
  56333. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  56334. .shadow(series.options.shadow);
  56335. }
  56336. // Crisp vector coordinates
  56337. crispCorr = (graphic.strokeWidth() % 2) / 2;
  56338. // #2596:
  56339. crispX = Math.round(point.plotX) - crispCorr;
  56340. plotOpen = point.plotOpen;
  56341. plotClose = point.plotClose;
  56342. topBox = Math.min(plotOpen, plotClose);
  56343. bottomBox = Math.max(plotOpen, plotClose);
  56344. halfWidth = Math.round(point.shapeArgs.width / 2);
  56345. hasTopWhisker = reversedYAxis ?
  56346. bottomBox !== point.yBottom :
  56347. Math.round(topBox) !==
  56348. Math.round(point.plotHigh);
  56349. hasBottomWhisker = reversedYAxis ?
  56350. Math.round(topBox) !==
  56351. Math.round(point.plotHigh) :
  56352. bottomBox !== point.yBottom;
  56353. topBox = Math.round(topBox) + crispCorr;
  56354. bottomBox = Math.round(bottomBox) + crispCorr;
  56355. // Create the path. Due to a bug in Chrome 49, the path is
  56356. // first instanciated with no values, then the values
  56357. // pushed. For unknown reasons, instanciating the path array
  56358. // with all the values would lead to a crash when updating
  56359. // frequently (#5193).
  56360. path = [];
  56361. path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
  56362. ['M', crispX, topBox], [
  56363. 'L',
  56364. // #460, #2094
  56365. crispX,
  56366. hasTopWhisker ?
  56367. Math.round(reversedYAxis ?
  56368. point.yBottom :
  56369. point.plotHigh) :
  56370. topBox
  56371. ], ['M', crispX, bottomBox], [
  56372. 'L',
  56373. // #460, #2094
  56374. crispX,
  56375. hasBottomWhisker ?
  56376. Math.round(reversedYAxis ?
  56377. point.plotHigh :
  56378. point.yBottom) :
  56379. bottomBox
  56380. ]);
  56381. graphic[isNew ? 'attr' : 'animate']({ d: path })
  56382. .addClass(point.getClassName(), true);
  56383. }
  56384. }
  56385. }
  56386. }
  56387. CandlestickSeries.defaultOptions = merge(OHLCSeries.defaultOptions, defaultOptions.plotOptions, { tooltip: OHLCSeries.defaultOptions.tooltip }, CandlestickSeriesDefaults);
  56388. SeriesRegistry.registerSeriesType('candlestick', CandlestickSeries);
  56389. /* *
  56390. *
  56391. * Default Export
  56392. *
  56393. * */
  56394. return CandlestickSeries;
  56395. });
  56396. _registerModule(_modules, 'Series/Flags/FlagsPoint.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  56397. /* *
  56398. *
  56399. * (c) 2010-2021 Torstein Honsi
  56400. *
  56401. * License: www.highcharts.com/license
  56402. *
  56403. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56404. *
  56405. * */
  56406. const { column: { prototype: { pointClass: ColumnPoint } } } = SeriesRegistry.seriesTypes;
  56407. const { isNumber } = U;
  56408. /* *
  56409. *
  56410. * Class
  56411. *
  56412. * */
  56413. class FlagsPoint extends ColumnPoint {
  56414. constructor() {
  56415. /* *
  56416. *
  56417. * Properties
  56418. *
  56419. * */
  56420. super(...arguments);
  56421. this.options = void 0;
  56422. this.series = void 0;
  56423. this.ttBelow = false;
  56424. }
  56425. /* *
  56426. *
  56427. * Functions
  56428. *
  56429. * */
  56430. /**
  56431. * @private
  56432. */
  56433. isValid() {
  56434. // #9233 - Prevent from treating flags as null points (even if
  56435. // they have no y values defined).
  56436. return isNumber(this.y) || typeof this.y === 'undefined';
  56437. }
  56438. /**
  56439. * @private
  56440. */
  56441. hasNewShapeType() {
  56442. const shape = this.options.shape || this.series.options.shape;
  56443. return this.graphic && shape && shape !== this.graphic.symbolKey;
  56444. }
  56445. }
  56446. /* *
  56447. *
  56448. * Default Export
  56449. *
  56450. * */
  56451. return FlagsPoint;
  56452. });
  56453. _registerModule(_modules, 'Series/Flags/FlagsSeriesDefaults.js', [], function () {
  56454. /* *
  56455. *
  56456. * (c) 2010-2021 Torstein Honsi
  56457. *
  56458. * License: www.highcharts.com/license
  56459. *
  56460. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56461. *
  56462. * */
  56463. /* *
  56464. *
  56465. * API Options
  56466. *
  56467. * */
  56468. /**
  56469. * Flags are used to mark events in stock charts. They can be added on the
  56470. * timeline, or attached to a specific series.
  56471. *
  56472. * @sample stock/demo/flags-general/
  56473. * Flags on a line series
  56474. *
  56475. * @extends plotOptions.column
  56476. * @excluding animation, borderColor, borderRadius, borderWidth,
  56477. * colorByPoint, cropThreshold, dataGrouping, pointPadding,
  56478. * pointWidth, turboThreshold
  56479. * @product highstock
  56480. * @optionparent plotOptions.flags
  56481. */
  56482. const FlagsSeriesDefaults = {
  56483. /**
  56484. * In case the flag is placed on a series, on what point key to place
  56485. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  56486. * however, the flag can optionally be placed on the `open`, `high`,
  56487. * `low` or `close` key.
  56488. *
  56489. * @sample {highstock} stock/plotoptions/flags-onkey/
  56490. * Range series, flag on high
  56491. *
  56492. * @type {string}
  56493. * @default y
  56494. * @since 4.2.2
  56495. * @product highstock
  56496. * @validvalue ["y", "open", "high", "low", "close"]
  56497. * @apioption plotOptions.flags.onKey
  56498. */
  56499. /**
  56500. * The id of the series that the flags should be drawn on. If no id
  56501. * is given, the flags are drawn on the x axis.
  56502. *
  56503. * @sample {highstock} stock/plotoptions/flags/
  56504. * Flags on series and on x axis
  56505. *
  56506. * @type {string}
  56507. * @product highstock
  56508. * @apioption plotOptions.flags.onSeries
  56509. */
  56510. pointRange: 0,
  56511. /**
  56512. * Whether the flags are allowed to overlap sideways. If `false`, the
  56513. * flags are moved sideways using an algorithm that seeks to place every
  56514. * flag as close as possible to its original position.
  56515. *
  56516. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  56517. * Allow sideways overlap
  56518. *
  56519. * @since 6.0.4
  56520. */
  56521. allowOverlapX: false,
  56522. /**
  56523. * The shape of the marker. Can be one of "flag", "circlepin",
  56524. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  56525. * Individual shapes can also be set for each point.
  56526. *
  56527. * @sample {highstock} stock/plotoptions/flags/
  56528. * Different shapes
  56529. *
  56530. * @type {Highcharts.FlagsShapeValue}
  56531. * @product highstock
  56532. */
  56533. shape: 'flag',
  56534. /**
  56535. * When multiple flags in the same series fall on the same value, this
  56536. * number determines the vertical offset between them.
  56537. *
  56538. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  56539. * A greater stack distance
  56540. *
  56541. * @product highstock
  56542. */
  56543. stackDistance: 12,
  56544. /**
  56545. * Text alignment for the text inside the flag.
  56546. *
  56547. * @since 5.0.0
  56548. * @product highstock
  56549. * @validvalue ["left", "center", "right"]
  56550. */
  56551. textAlign: 'center',
  56552. /**
  56553. * Specific tooltip options for flag series. Flag series tooltips are
  56554. * different from most other types in that a flag doesn't have a data
  56555. * value, so the tooltip rather displays the `text` option for each
  56556. * point.
  56557. *
  56558. * @extends plotOptions.series.tooltip
  56559. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  56560. * @product highstock
  56561. */
  56562. tooltip: {
  56563. pointFormat: '{point.text}'
  56564. },
  56565. /**
  56566. * @type {number|null}
  56567. */
  56568. threshold: null,
  56569. /**
  56570. * The text to display on each flag. This can be defined on series
  56571. * level, or individually for each point. Defaults to `"A"`.
  56572. *
  56573. * @type {string}
  56574. * @default A
  56575. * @product highstock
  56576. * @apioption plotOptions.flags.title
  56577. */
  56578. /**
  56579. * The y position of the top left corner of the flag relative to either
  56580. * the series (if onSeries is defined), or the x axis. Defaults to
  56581. * `-30`.
  56582. *
  56583. * @product highstock
  56584. */
  56585. y: -30,
  56586. /**
  56587. * Whether to use HTML to render the flag texts. Using HTML allows for
  56588. * advanced formatting, images and reliable bi-directional text
  56589. * rendering. Note that exported images won't respect the HTML, and that
  56590. * HTML won't respect Z-index settings.
  56591. *
  56592. * @type {boolean}
  56593. * @default false
  56594. * @since 1.3
  56595. * @product highstock
  56596. * @apioption plotOptions.flags.useHTML
  56597. */
  56598. /**
  56599. * Fixed width of the flag's shape. By default, width is autocalculated
  56600. * according to the flag's title.
  56601. *
  56602. * @sample {highstock} stock/demo/flags-shapes/
  56603. * Flags with fixed width
  56604. *
  56605. * @type {number}
  56606. * @product highstock
  56607. * @apioption plotOptions.flags.width
  56608. */
  56609. /**
  56610. * Fixed height of the flag's shape. By default, height is
  56611. * autocalculated according to the flag's title.
  56612. *
  56613. * @type {number}
  56614. * @product highstock
  56615. * @apioption plotOptions.flags.height
  56616. */
  56617. /**
  56618. * The fill color for the flags.
  56619. *
  56620. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56621. * @product highstock
  56622. */
  56623. fillColor: "#ffffff" /* Palette.backgroundColor */,
  56624. /**
  56625. * The color of the line/border of the flag.
  56626. *
  56627. * In styled mode, the stroke is set in the
  56628. * `.highcharts-flag-series.highcharts-point` rule.
  56629. *
  56630. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56631. * @default #000000
  56632. * @product highstock
  56633. * @apioption plotOptions.flags.lineColor
  56634. */
  56635. /**
  56636. * The pixel width of the flag's line/border.
  56637. *
  56638. * @product highstock
  56639. */
  56640. lineWidth: 1,
  56641. states: {
  56642. /**
  56643. * @extends plotOptions.column.states.hover
  56644. * @product highstock
  56645. */
  56646. hover: {
  56647. /**
  56648. * The color of the line/border of the flag.
  56649. *
  56650. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56651. * @product highstock
  56652. */
  56653. lineColor: "#000000" /* Palette.neutralColor100 */,
  56654. /**
  56655. * The fill or background color of the flag.
  56656. *
  56657. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56658. * @product highstock
  56659. */
  56660. fillColor: "#ccd3ff" /* Palette.highlightColor20 */
  56661. }
  56662. },
  56663. /**
  56664. * The text styles of the flag.
  56665. *
  56666. * In styled mode, the styles are set in the
  56667. * `.highcharts-flag-series .highcharts-point` rule.
  56668. *
  56669. * @type {Highcharts.CSSObject}
  56670. * @default {"fontSize": "11px", "fontWeight": "bold"}
  56671. * @product highstock
  56672. */
  56673. style: {
  56674. /** @ignore-option */
  56675. fontSize: '0.7em',
  56676. /** @ignore-option */
  56677. fontWeight: 'bold'
  56678. }
  56679. };
  56680. /**
  56681. * A `flags` series. If the [type](#series.flags.type) option is not
  56682. * specified, it is inherited from [chart.type](#chart.type).
  56683. *
  56684. * @extends series,plotOptions.flags
  56685. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  56686. * connectNulls, cropThreshold, dashStyle, dataGrouping, dataParser,
  56687. * dataURL, gapSize, gapUnit, linecap, lineWidth, marker,
  56688. * pointPadding, pointWidth, step, turboThreshold, useOhlcData
  56689. * @product highstock
  56690. * @apioption series.flags
  56691. */
  56692. /**
  56693. * An array of data points for the series. For the `flags` series type,
  56694. * points can be given in the following ways:
  56695. *
  56696. * 1. An array of objects with named values. The following snippet shows only a
  56697. * few settings, see the complete options set below. If the total number of
  56698. * data points exceeds the series'
  56699. * [turboThreshold](#series.flags.turboThreshold), this option is not
  56700. * available.
  56701. * ```js
  56702. * data: [{
  56703. * x: 1,
  56704. * title: "A",
  56705. * text: "First event"
  56706. * }, {
  56707. * x: 1,
  56708. * title: "B",
  56709. * text: "Second event"
  56710. * }]
  56711. * ```
  56712. *
  56713. * @type {Array<*>}
  56714. * @extends series.line.data
  56715. * @excluding dataLabels, marker, name, y
  56716. * @product highstock
  56717. * @apioption series.flags.data
  56718. */
  56719. /**
  56720. * The fill color of an individual flag. By default it inherits from
  56721. * the series color.
  56722. *
  56723. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56724. * @product highstock
  56725. * @apioption series.flags.data.fillColor
  56726. */
  56727. /**
  56728. * The longer text to be shown in the flag's tooltip.
  56729. *
  56730. * @type {string}
  56731. * @product highstock
  56732. * @apioption series.flags.data.text
  56733. */
  56734. /**
  56735. * The short text to be shown on the flag.
  56736. *
  56737. * @type {string}
  56738. * @product highstock
  56739. * @apioption series.flags.data.title
  56740. */
  56741. ''; // keeps doclets above in transpiled file
  56742. /* *
  56743. *
  56744. * Default Export
  56745. *
  56746. * */
  56747. return FlagsSeriesDefaults;
  56748. });
  56749. _registerModule(_modules, 'Series/Flags/FlagsSymbols.js', [_modules['Core/Renderer/RendererRegistry.js']], function (RendererRegistry) {
  56750. /* *
  56751. *
  56752. * Imports
  56753. *
  56754. * */
  56755. /* *
  56756. *
  56757. * Composition
  56758. *
  56759. * */
  56760. var FlagsSymbols;
  56761. (function (FlagsSymbols) {
  56762. /* *
  56763. *
  56764. * Constants
  56765. *
  56766. * */
  56767. const modifiedMembers = [];
  56768. /* *
  56769. *
  56770. * Functions
  56771. *
  56772. * */
  56773. /* eslint-disable valid-jsdoc */
  56774. /**
  56775. * @private
  56776. */
  56777. function compose(SVGRendererClass) {
  56778. if (modifiedMembers.indexOf(SVGRendererClass) === -1) {
  56779. modifiedMembers.push(SVGRendererClass);
  56780. const symbols = SVGRendererClass.prototype.symbols;
  56781. symbols.flag = flag;
  56782. createPinSymbol(symbols, 'circle');
  56783. createPinSymbol(symbols, 'square');
  56784. }
  56785. const RendererClass = RendererRegistry.getRendererType();
  56786. // The symbol callbacks are generated on the SVGRenderer object in all
  56787. // browsers.
  56788. if (modifiedMembers.indexOf(RendererClass)) {
  56789. modifiedMembers.push(RendererClass);
  56790. }
  56791. }
  56792. FlagsSymbols.compose = compose;
  56793. /**
  56794. * Create the flag icon with anchor.
  56795. * @private
  56796. */
  56797. function flag(x, y, w, h, options) {
  56798. const anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
  56799. // To do: unwanted any cast because symbols.circle has wrong type, it
  56800. // actually returns an SVGPathArray
  56801. const path = this.circle(anchorX - 1, anchorY - 1, 2, 2);
  56802. path.push(['M', anchorX, anchorY], ['L', x, y + h], ['L', x, y], ['L', x + w, y], ['L', x + w, y + h], ['L', x, y + h], ['Z']);
  56803. return path;
  56804. }
  56805. /**
  56806. * Create the circlepin and squarepin icons with anchor.
  56807. * @private
  56808. */
  56809. function createPinSymbol(symbols, shape) {
  56810. symbols[(shape + 'pin')] = function (x, y, w, h, options) {
  56811. const anchorX = options && options.anchorX, anchorY = options && options.anchorY;
  56812. let path;
  56813. // For single-letter flags, make sure circular flags are not taller
  56814. // than their width
  56815. if (shape === 'circle' && h > w) {
  56816. x -= Math.round((h - w) / 2);
  56817. w = h;
  56818. }
  56819. path = (symbols[shape])(x, y, w, h);
  56820. if (anchorX && anchorY) {
  56821. /**
  56822. * If the label is below the anchor, draw the connecting line
  56823. * from the top edge of the label, otherwise start drawing from
  56824. * the bottom edge
  56825. */
  56826. let labelX = anchorX;
  56827. if (shape === 'circle') {
  56828. labelX = x + w / 2;
  56829. }
  56830. else {
  56831. const startSeg = path[0];
  56832. const endSeg = path[1];
  56833. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  56834. labelX = (startSeg[1] + endSeg[1]) / 2;
  56835. }
  56836. }
  56837. const labelY = (y > anchorY) ? y : y + h;
  56838. path.push([
  56839. 'M',
  56840. labelX,
  56841. labelY
  56842. ], [
  56843. 'L',
  56844. anchorX,
  56845. anchorY
  56846. ]);
  56847. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  56848. }
  56849. return path;
  56850. };
  56851. }
  56852. })(FlagsSymbols || (FlagsSymbols = {}));
  56853. /* *
  56854. *
  56855. * Default Export
  56856. *
  56857. * */
  56858. return FlagsSymbols;
  56859. });
  56860. _registerModule(_modules, 'Series/OnSeriesComposition.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (ColumnSeries, Series, U) {
  56861. /* *
  56862. *
  56863. * (c) 2010-2021 Torstein Honsi
  56864. *
  56865. * License: www.highcharts.com/license
  56866. *
  56867. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56868. *
  56869. * */
  56870. const { prototype: columnProto } = ColumnSeries;
  56871. const { prototype: seriesProto } = Series;
  56872. const { defined, stableSort } = U;
  56873. /* *
  56874. *
  56875. * Composition
  56876. *
  56877. * */
  56878. var OnSeriesComposition;
  56879. (function (OnSeriesComposition) {
  56880. /* *
  56881. *
  56882. * Declarations
  56883. *
  56884. * */
  56885. /* *
  56886. *
  56887. * Properties
  56888. *
  56889. * */
  56890. const composedMembers = [];
  56891. /* *
  56892. *
  56893. * Functions
  56894. *
  56895. * */
  56896. /* eslint-disable valid-jsdoc */
  56897. /**
  56898. * @private
  56899. */
  56900. function compose(SeriesClass) {
  56901. if (U.pushUnique(composedMembers, SeriesClass)) {
  56902. const seriesProto = SeriesClass.prototype;
  56903. seriesProto.getPlotBox = getPlotBox;
  56904. seriesProto.translate = translate;
  56905. }
  56906. return SeriesClass;
  56907. }
  56908. OnSeriesComposition.compose = compose;
  56909. /**
  56910. * Override getPlotBox. If the onSeries option is valid, return the plot box
  56911. * of the onSeries, otherwise proceed as usual.
  56912. *
  56913. * @private
  56914. */
  56915. function getPlotBox(name) {
  56916. return seriesProto.getPlotBox.call((this.options.onSeries &&
  56917. this.chart.get(this.options.onSeries)) || this, name);
  56918. }
  56919. OnSeriesComposition.getPlotBox = getPlotBox;
  56920. /**
  56921. * Extend the translate method by placing the point on the related series
  56922. *
  56923. * @private
  56924. */
  56925. function translate() {
  56926. columnProto.translate.apply(this);
  56927. const series = this, options = series.options, chart = series.chart, points = series.points, optionsOnSeries = options.onSeries, onSeries = (optionsOnSeries &&
  56928. chart.get(optionsOnSeries)), step = onSeries && onSeries.options.step, onData = (onSeries && onSeries.points), inverted = chart.inverted, xAxis = series.xAxis, yAxis = series.yAxis;
  56929. let cursor = points.length - 1, point, lastPoint, onKey = options.onKey || 'y', i = onData && onData.length, xOffset = 0, leftPoint, lastX, rightPoint, currentDataGrouping, distanceRatio;
  56930. // relate to a master series
  56931. if (onSeries && onSeries.visible && i) {
  56932. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  56933. currentDataGrouping = onSeries.currentDataGrouping;
  56934. lastX = (onData[i - 1].x +
  56935. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  56936. // sort the data points
  56937. stableSort(points, (a, b) => (a.x - b.x));
  56938. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  56939. while (i-- && points[cursor]) {
  56940. leftPoint = onData[i];
  56941. point = points[cursor];
  56942. point.y = leftPoint.y;
  56943. if (leftPoint.x <= point.x &&
  56944. typeof leftPoint[onKey] !== 'undefined') {
  56945. if (point.x <= lastX) { // #803
  56946. point.plotY = leftPoint[onKey];
  56947. // interpolate between points, #666
  56948. if (leftPoint.x < point.x &&
  56949. !step) {
  56950. rightPoint = onData[i + 1];
  56951. if (rightPoint &&
  56952. typeof rightPoint[onKey] !== 'undefined') {
  56953. // the distance ratio, between 0 and 1
  56954. distanceRatio =
  56955. (point.x - leftPoint.x) /
  56956. (rightPoint.x - leftPoint.x);
  56957. point.plotY +=
  56958. distanceRatio *
  56959. // the plotY distance
  56960. (rightPoint[onKey] - leftPoint[onKey]);
  56961. point.y +=
  56962. distanceRatio *
  56963. (rightPoint.y - leftPoint.y);
  56964. }
  56965. }
  56966. }
  56967. cursor--;
  56968. i++; // check again for points in the same x position
  56969. if (cursor < 0) {
  56970. break;
  56971. }
  56972. }
  56973. }
  56974. }
  56975. // Add plotY position and handle stacking
  56976. points.forEach((point, i) => {
  56977. let stackIndex;
  56978. point.plotX += xOffset; // #2049
  56979. // Undefined plotY means the point is either on axis, outside series
  56980. // range or hidden series. If the series is outside the range of the
  56981. // x axis it should fall through with an undefined plotY, but then
  56982. // we must remove the shapeArgs (#847). For inverted charts, we need
  56983. // to calculate position anyway, because series.invertGroups is not
  56984. // defined
  56985. if (typeof point.plotY === 'undefined' || inverted) {
  56986. if (point.plotX >= 0 &&
  56987. point.plotX <= xAxis.len) {
  56988. // We're inside xAxis range
  56989. if (inverted) {
  56990. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  56991. point.plotX = defined(point.y) ?
  56992. yAxis.translate(point.y, 0, 0, 0, 1) :
  56993. 0;
  56994. }
  56995. else {
  56996. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  56997. xAxis.offset; // For the windbarb demo
  56998. }
  56999. }
  57000. else {
  57001. point.shapeArgs = {}; // 847
  57002. }
  57003. }
  57004. // if multiple flags appear at the same x, order them into a stack
  57005. lastPoint = points[i - 1];
  57006. if (lastPoint && lastPoint.plotX === point.plotX) {
  57007. if (typeof lastPoint.stackIndex === 'undefined') {
  57008. lastPoint.stackIndex = 0;
  57009. }
  57010. stackIndex = lastPoint.stackIndex + 1;
  57011. }
  57012. point.stackIndex = stackIndex; // #3639
  57013. });
  57014. this.onSeries = onSeries;
  57015. }
  57016. OnSeriesComposition.translate = translate;
  57017. })(OnSeriesComposition || (OnSeriesComposition = {}));
  57018. /* *
  57019. *
  57020. * Default Export
  57021. *
  57022. * */
  57023. return OnSeriesComposition;
  57024. });
  57025. _registerModule(_modules, 'Series/Flags/FlagsSeries.js', [_modules['Series/Flags/FlagsPoint.js'], _modules['Series/Flags/FlagsSeriesDefaults.js'], _modules['Series/Flags/FlagsSymbols.js'], _modules['Core/Globals.js'], _modules['Series/OnSeriesComposition.js'], _modules['Core/Renderer/RendererUtilities.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Renderer/SVG/SVGElement.js'], _modules['Core/Utilities.js']], function (FlagsPoint, FlagsSeriesDefaults, FlagsSymbols, H, OnSeriesComposition, R, SeriesRegistry, SVGElement, U) {
  57026. /* *
  57027. *
  57028. * (c) 2010-2021 Torstein Honsi
  57029. *
  57030. * License: www.highcharts.com/license
  57031. *
  57032. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  57033. *
  57034. * */
  57035. const { noop } = H;
  57036. const { distribute } = R;
  57037. const { series: Series, seriesTypes: { column: ColumnSeries } } = SeriesRegistry;
  57038. const { addEvent, defined, extend, merge, objectEach, wrap } = U;
  57039. /* *
  57040. *
  57041. * Classes
  57042. *
  57043. * */
  57044. /**
  57045. * The Flags series.
  57046. *
  57047. * @private
  57048. * @class
  57049. * @name Highcharts.seriesTypes.flags
  57050. *
  57051. * @augments Highcharts.Series
  57052. */
  57053. class FlagsSeries extends ColumnSeries {
  57054. constructor() {
  57055. /* *
  57056. *
  57057. * Static Properties
  57058. *
  57059. * */
  57060. super(...arguments);
  57061. /* *
  57062. *
  57063. * Properties
  57064. *
  57065. * */
  57066. this.data = void 0;
  57067. this.options = void 0;
  57068. this.points = void 0;
  57069. }
  57070. /* *
  57071. *
  57072. * Functions
  57073. *
  57074. * */
  57075. /**
  57076. * Disable animation, but keep clipping (#8546).
  57077. * @private
  57078. */
  57079. animate(init) {
  57080. if (init) {
  57081. this.setClip();
  57082. }
  57083. }
  57084. /**
  57085. * Draw the markers.
  57086. * @private
  57087. */
  57088. drawPoints() {
  57089. const series = this, points = series.points, chart = series.chart, renderer = chart.renderer, inverted = chart.inverted, options = series.options, optionsY = options.y, yAxis = series.yAxis, boxesMap = {}, boxes = [];
  57090. let plotX, plotY, shape, i, point, graphic, stackIndex, anchorY, attribs, outsideRight, centered;
  57091. i = points.length;
  57092. while (i--) {
  57093. point = points[i];
  57094. outsideRight =
  57095. (inverted ? point.plotY : point.plotX) >
  57096. series.xAxis.len;
  57097. plotX = point.plotX;
  57098. stackIndex = point.stackIndex;
  57099. shape = point.options.shape || options.shape;
  57100. plotY = point.plotY;
  57101. if (typeof plotY !== 'undefined') {
  57102. plotY = point.plotY + optionsY -
  57103. (typeof stackIndex !== 'undefined' &&
  57104. (stackIndex * options.stackDistance));
  57105. }
  57106. // skip connectors for higher level stacked points
  57107. point.anchorX = stackIndex ? void 0 : point.plotX;
  57108. anchorY = stackIndex ? void 0 : point.plotY;
  57109. centered = shape !== 'flag';
  57110. graphic = point.graphic;
  57111. // Only draw the point if y is defined and the flag is within
  57112. // the visible area
  57113. if (typeof plotY !== 'undefined' &&
  57114. plotX >= 0 &&
  57115. !outsideRight) {
  57116. // #15384
  57117. if (graphic && point.hasNewShapeType()) {
  57118. graphic = graphic.destroy();
  57119. }
  57120. // Create the flag
  57121. if (!graphic) {
  57122. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML)
  57123. .addClass('highcharts-point')
  57124. .add(series.markerGroup);
  57125. // Add reference to the point for tracker (#6303)
  57126. if (point.graphic.div) {
  57127. point.graphic.div.point = point;
  57128. }
  57129. graphic.isNew = true;
  57130. }
  57131. graphic.attr({
  57132. align: centered ? 'center' : 'left',
  57133. width: options.width,
  57134. height: options.height,
  57135. 'text-align': options.textAlign
  57136. });
  57137. if (!chart.styledMode) {
  57138. graphic
  57139. .attr(series.pointAttribs(point))
  57140. .css(merge(options.style, point.style))
  57141. .shadow(options.shadow);
  57142. }
  57143. if (plotX > 0) { // #3119
  57144. plotX -= graphic.strokeWidth() % 2; // #4285
  57145. }
  57146. // Plant the flag
  57147. attribs = {
  57148. y: plotY,
  57149. anchorY: anchorY
  57150. };
  57151. if (options.allowOverlapX) {
  57152. attribs.x = plotX;
  57153. attribs.anchorX = point.anchorX;
  57154. }
  57155. graphic.attr({
  57156. text: point.options.title || options.title || 'A'
  57157. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  57158. // Rig for the distribute function
  57159. if (!options.allowOverlapX) {
  57160. if (!boxesMap[point.plotX]) {
  57161. boxesMap[point.plotX] = {
  57162. align: centered ? 0.5 : 0,
  57163. size: graphic.width,
  57164. target: plotX,
  57165. anchorX: plotX
  57166. };
  57167. }
  57168. else {
  57169. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  57170. }
  57171. }
  57172. // Set the tooltip anchor position
  57173. point.tooltipPos = [
  57174. plotX,
  57175. plotY + yAxis.pos - chart.plotTop
  57176. ]; // #6327
  57177. }
  57178. else if (graphic) {
  57179. point.graphic = graphic.destroy();
  57180. }
  57181. }
  57182. // Handle X-dimension overlapping
  57183. if (!options.allowOverlapX) {
  57184. let maxDistance = 100;
  57185. objectEach(boxesMap, function (box) {
  57186. box.plotX = box.anchorX;
  57187. boxes.push(box);
  57188. maxDistance = Math.max(box.size, maxDistance);
  57189. });
  57190. // If necessary (for overlapping or long labels) distribute it
  57191. // depending on the label width or a hardcoded value, #16041.
  57192. distribute(boxes, inverted ? yAxis.len : this.xAxis.len, maxDistance);
  57193. for (const point of points) {
  57194. const plotX = point.plotX, graphic = point.graphic, box = graphic && boxesMap[plotX];
  57195. if (box && graphic) {
  57196. // Hide flag when its box position is not specified
  57197. // (#8573, #9299)
  57198. if (!defined(box.pos)) {
  57199. graphic.hide().isNew = true;
  57200. }
  57201. else {
  57202. graphic[graphic.isNew ? 'attr' : 'animate']({
  57203. x: box.pos + (box.align || 0) * box.size,
  57204. anchorX: point.anchorX
  57205. }).show().isNew = false;
  57206. }
  57207. }
  57208. }
  57209. }
  57210. // Can be a mix of SVG and HTML and we need events for both (#6303)
  57211. if (options.useHTML && series.markerGroup) {
  57212. wrap(series.markerGroup, 'on', function (proceed) {
  57213. return SVGElement.prototype.on.apply(
  57214. // for HTML
  57215. // eslint-disable-next-line no-invalid-this
  57216. proceed.apply(this, [].slice.call(arguments, 1)),
  57217. // and for SVG
  57218. [].slice.call(arguments, 1));
  57219. });
  57220. }
  57221. }
  57222. /**
  57223. * Extend the column trackers with listeners to expand and contract
  57224. * stacks.
  57225. * @private
  57226. */
  57227. drawTracker() {
  57228. const series = this, points = series.points;
  57229. super.drawTracker();
  57230. /* *
  57231. * Bring each stacked flag up on mouse over, this allows readability
  57232. * of vertically stacked elements as well as tight points on the x
  57233. * axis. #1924.
  57234. */
  57235. for (const point of points) {
  57236. const graphic = point.graphic;
  57237. if (graphic) {
  57238. if (point.unbindMouseOver) {
  57239. point.unbindMouseOver();
  57240. }
  57241. point.unbindMouseOver = addEvent(graphic.element, 'mouseover', function () {
  57242. // Raise this point
  57243. if (point.stackIndex > 0 &&
  57244. !point.raised) {
  57245. point._y = graphic.y;
  57246. graphic.attr({
  57247. y: point._y - 8
  57248. });
  57249. point.raised = true;
  57250. }
  57251. // Revert other raised points
  57252. for (const otherPoint of points) {
  57253. if (otherPoint !== point &&
  57254. otherPoint.raised &&
  57255. otherPoint.graphic) {
  57256. otherPoint.graphic.attr({
  57257. y: otherPoint._y
  57258. });
  57259. otherPoint.raised = false;
  57260. }
  57261. }
  57262. });
  57263. }
  57264. }
  57265. }
  57266. /**
  57267. * Get presentational attributes
  57268. * @private
  57269. */
  57270. pointAttribs(point, state) {
  57271. const options = this.options, color = (point && point.color) || this.color;
  57272. let lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
  57273. if (state) {
  57274. fill = options.states[state].fillColor;
  57275. lineColor = options.states[state].lineColor;
  57276. lineWidth = options.states[state].lineWidth;
  57277. }
  57278. return {
  57279. fill: fill || color,
  57280. stroke: lineColor || color,
  57281. 'stroke-width': lineWidth || options.lineWidth || 0
  57282. };
  57283. }
  57284. /**
  57285. * @private
  57286. */
  57287. setClip() {
  57288. Series.prototype.setClip.apply(this, arguments);
  57289. if (this.options.clip !== false &&
  57290. this.sharedClipKey &&
  57291. this.markerGroup) {
  57292. this.markerGroup.clip(this.chart.sharedClips[this.sharedClipKey]);
  57293. }
  57294. }
  57295. }
  57296. FlagsSeries.compose = FlagsSymbols.compose;
  57297. FlagsSeries.defaultOptions = merge(ColumnSeries.defaultOptions, FlagsSeriesDefaults);
  57298. OnSeriesComposition.compose(FlagsSeries);
  57299. extend(FlagsSeries.prototype, {
  57300. allowDG: false,
  57301. forceCrop: true,
  57302. invertible: false,
  57303. noSharedTooltip: true,
  57304. pointClass: FlagsPoint,
  57305. sorted: false,
  57306. takeOrdinalPosition: false,
  57307. trackerGroups: ['markerGroup'],
  57308. buildKDTree: noop,
  57309. /**
  57310. * Inherit the initialization from base Series.
  57311. * @private
  57312. */
  57313. init: Series.prototype.init
  57314. });
  57315. SeriesRegistry.registerSeriesType('flags', FlagsSeries);
  57316. /* *
  57317. *
  57318. * Default Export
  57319. *
  57320. * */
  57321. /* *
  57322. *
  57323. * API Declarations
  57324. *
  57325. * */
  57326. /**
  57327. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  57328. */
  57329. ''; // detach doclets above
  57330. return FlagsSeries;
  57331. });
  57332. _registerModule(_modules, 'Core/Chart/StockChart.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Templating.js'], _modules['Core/Defaults.js'], _modules['Stock/Navigator/NavigatorDefaults.js'], _modules['Stock/RangeSelector/RangeSelectorDefaults.js'], _modules['Stock/Scrollbar/ScrollbarDefaults.js'], _modules['Core/Series/Series.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (Axis, Chart, F, D, NavigatorDefaults, RangeSelectorDefaults, ScrollbarDefaults, Series, SVGRenderer, U) {
  57333. /* *
  57334. *
  57335. * (c) 2010-2021 Torstein Honsi
  57336. *
  57337. * License: www.highcharts.com/license
  57338. *
  57339. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  57340. *
  57341. * */
  57342. const { format } = F;
  57343. const { getOptions } = D;
  57344. const { addEvent, clamp, defined, extend, find, isNumber, isString, merge, pick, splat } = U;
  57345. /* *
  57346. *
  57347. * Class
  57348. *
  57349. * */
  57350. /**
  57351. * Stock-optimized chart. Use {@link Highcharts.Chart|Chart} for common charts.
  57352. *
  57353. * @requires modules/stock
  57354. *
  57355. * @class
  57356. * @name Highcharts.StockChart
  57357. * @extends Highcharts.Chart
  57358. */
  57359. class StockChart extends Chart {
  57360. /**
  57361. * Initializes the chart. The constructor's arguments are passed on
  57362. * directly.
  57363. *
  57364. * @function Highcharts.StockChart#init
  57365. *
  57366. * @param {Highcharts.Options} userOptions
  57367. * Custom options.
  57368. *
  57369. * @param {Function} [callback]
  57370. * Function to run when the chart has loaded and and all external
  57371. * images are loaded.
  57372. *
  57373. *
  57374. * @emits Highcharts.StockChart#event:init
  57375. * @emits Highcharts.StockChart#event:afterInit
  57376. */
  57377. init(userOptions, callback) {
  57378. const defaultOptions = getOptions(), xAxisOptions = userOptions.xAxis, yAxisOptions = userOptions.yAxis,
  57379. // Always disable startOnTick:true on the main axis when the
  57380. // navigator is enabled (#1090)
  57381. navigatorEnabled = pick(userOptions.navigator && userOptions.navigator.enabled, NavigatorDefaults.enabled, true);
  57382. // Avoid doing these twice
  57383. userOptions.xAxis = userOptions.yAxis = void 0;
  57384. const options = merge({
  57385. chart: {
  57386. panning: {
  57387. enabled: true,
  57388. type: 'x'
  57389. },
  57390. zooming: {
  57391. pinchType: 'x'
  57392. }
  57393. },
  57394. navigator: {
  57395. enabled: navigatorEnabled
  57396. },
  57397. scrollbar: {
  57398. // #4988 - check if setOptions was called
  57399. enabled: pick(ScrollbarDefaults.enabled, true)
  57400. },
  57401. rangeSelector: {
  57402. // #4988 - check if setOptions was called
  57403. enabled: pick(RangeSelectorDefaults.rangeSelector.enabled, true)
  57404. },
  57405. title: {
  57406. text: null
  57407. },
  57408. tooltip: {
  57409. split: pick(defaultOptions.tooltip.split, true),
  57410. crosshairs: true
  57411. },
  57412. legend: {
  57413. enabled: false
  57414. }
  57415. }, userOptions, // user's options
  57416. {
  57417. isStock: true // internal flag
  57418. });
  57419. userOptions.xAxis = xAxisOptions;
  57420. userOptions.yAxis = yAxisOptions;
  57421. // apply X axis options to both single and multi y axes
  57422. options.xAxis = splat(userOptions.xAxis || {}).map(function (xAxisOptions, i) {
  57423. return merge(getDefaultAxisOptions('xAxis', xAxisOptions), defaultOptions.xAxis, // #3802
  57424. // #7690
  57425. defaultOptions.xAxis && defaultOptions.xAxis[i], xAxisOptions, // user options
  57426. getForcedAxisOptions('xAxis', userOptions));
  57427. });
  57428. // apply Y axis options to both single and multi y axes
  57429. options.yAxis = splat(userOptions.yAxis || {}).map(function (yAxisOptions, i) {
  57430. return merge(getDefaultAxisOptions('yAxis', yAxisOptions), defaultOptions.yAxis, // #3802
  57431. // #7690
  57432. defaultOptions.yAxis && defaultOptions.yAxis[i], yAxisOptions // user options
  57433. );
  57434. });
  57435. super.init(options, callback);
  57436. }
  57437. /**
  57438. * Factory for creating different axis types.
  57439. * Extended to add stock defaults.
  57440. *
  57441. * @private
  57442. * @function Highcharts.StockChart#createAxis
  57443. * @param {string} coll
  57444. * An axis type.
  57445. * @param {Chart.CreateAxisOptionsObject} options
  57446. * The axis creation options.
  57447. */
  57448. createAxis(coll, options) {
  57449. options.axis = merge(getDefaultAxisOptions(coll, options.axis), options.axis, getForcedAxisOptions(coll, this.userOptions));
  57450. return super.createAxis(coll, options);
  57451. }
  57452. }
  57453. /* eslint-disable no-invalid-this, valid-jsdoc */
  57454. (function (StockChart) {
  57455. /**
  57456. * Factory function for creating new stock charts. Creates a new
  57457. * {@link Highcharts.StockChart|StockChart} object with different default
  57458. * options than the basic Chart.
  57459. *
  57460. * @example
  57461. * let chart = Highcharts.stockChart('container', {
  57462. * series: [{
  57463. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  57464. * pointInterval: 24 * 60 * 60 * 1000
  57465. * }]
  57466. * });
  57467. *
  57468. * @function Highcharts.stockChart
  57469. *
  57470. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  57471. * The DOM element to render to, or its id.
  57472. *
  57473. * @param {Highcharts.Options} options
  57474. * The chart options structure as described in the
  57475. * [options reference](https://api.highcharts.com/highstock).
  57476. *
  57477. * @param {Highcharts.ChartCallbackFunction} [callback]
  57478. * A function to execute when the chart object is finished
  57479. * rendering and all external image files (`chart.backgroundImage`,
  57480. * `chart.plotBackgroundImage` etc) are loaded. Defining a
  57481. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  57482. * handler is equivalent.
  57483. *
  57484. * @return {Highcharts.StockChart}
  57485. * The chart object.
  57486. */
  57487. function stockChart(a, b, c) {
  57488. return new StockChart(a, b, c);
  57489. }
  57490. StockChart.stockChart = stockChart;
  57491. })(StockChart || (StockChart = {}));
  57492. /**
  57493. * Get stock-specific default axis options.
  57494. *
  57495. * @private
  57496. * @function getDefaultAxisOptions
  57497. */
  57498. function getDefaultAxisOptions(type, options) {
  57499. if (type === 'xAxis') {
  57500. return {
  57501. minPadding: 0,
  57502. maxPadding: 0,
  57503. overscroll: 0,
  57504. ordinal: true,
  57505. title: {
  57506. text: null
  57507. },
  57508. labels: {
  57509. overflow: 'justify'
  57510. },
  57511. showLastLabel: true
  57512. };
  57513. }
  57514. if (type === 'yAxis') {
  57515. return {
  57516. labels: {
  57517. y: -2
  57518. },
  57519. opposite: pick(options.opposite, true),
  57520. showLastLabel: !!(
  57521. // #6104, show last label by default for category axes
  57522. options.categories ||
  57523. options.type === 'category'),
  57524. title: {
  57525. text: null
  57526. }
  57527. };
  57528. }
  57529. return {};
  57530. }
  57531. /**
  57532. * Get stock-specific forced axis options.
  57533. *
  57534. * @private
  57535. * @function getForcedAxisOptions
  57536. */
  57537. function getForcedAxisOptions(type, chartOptions) {
  57538. if (type === 'xAxis') {
  57539. // Always disable startOnTick:true on the main axis when the navigator
  57540. // is enabled (#1090)
  57541. const navigatorEnabled = pick(chartOptions.navigator && chartOptions.navigator.enabled, NavigatorDefaults.enabled, true);
  57542. const axisOptions = {
  57543. type: 'datetime',
  57544. categories: void 0
  57545. };
  57546. if (navigatorEnabled) {
  57547. axisOptions.startOnTick = false;
  57548. axisOptions.endOnTick = false;
  57549. }
  57550. return axisOptions;
  57551. }
  57552. return {};
  57553. }
  57554. /* *
  57555. *
  57556. * Compositions
  57557. *
  57558. * */
  57559. // Handle som Stock-specific series defaults, override the plotOptions before
  57560. // series options are handled.
  57561. addEvent(Series, 'setOptions', function (e) {
  57562. let overrides;
  57563. if (this.chart.options.isStock) {
  57564. if (this.is('column') || this.is('columnrange')) {
  57565. overrides = {
  57566. borderWidth: 0,
  57567. shadow: false
  57568. };
  57569. }
  57570. else if (!this.is('scatter') && !this.is('sma')) {
  57571. overrides = {
  57572. marker: {
  57573. enabled: false,
  57574. radius: 2
  57575. }
  57576. };
  57577. }
  57578. if (overrides) {
  57579. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  57580. }
  57581. }
  57582. });
  57583. // Override the automatic label alignment so that the first Y axis' labels are
  57584. // drawn on top of the grid line, and subsequent axes are drawn outside
  57585. addEvent(Axis, 'autoLabelAlign', function (e) {
  57586. const { chart, options } = this, panes = chart._labelPanes = chart._labelPanes || {}, labelOptions = options.labels;
  57587. if (chart.options.isStock && this.coll === 'yAxis') {
  57588. const key = options.top + ',' + options.height;
  57589. // Do it only for the first Y axis of each pane
  57590. if (!panes[key] && labelOptions.enabled) {
  57591. if (labelOptions.distance === 15 && // default
  57592. this.side === 1) {
  57593. labelOptions.distance = 0;
  57594. }
  57595. if (typeof labelOptions.align === 'undefined') {
  57596. labelOptions.align = 'right';
  57597. }
  57598. panes[key] = this;
  57599. e.align = 'right';
  57600. e.preventDefault();
  57601. }
  57602. }
  57603. });
  57604. // Clear axis from label panes (#6071)
  57605. addEvent(Axis, 'destroy', function () {
  57606. const chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  57607. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  57608. delete chart._labelPanes[key];
  57609. }
  57610. });
  57611. // Override getPlotLinePath to allow for multipane charts
  57612. addEvent(Axis, 'getPlotLinePath', function (e) {
  57613. let axis = this, series = (this.isLinked && !this.series ?
  57614. this.linkedParent.series :
  57615. this.series), chart = axis.chart, renderer = chart.renderer, axisLeft = axis.left, axisTop = axis.top, x1, y1, x2, y2, result = [], axes = [], // #3416 need a default array
  57616. axes2, uniqueAxes, translatedValue = e.translatedValue, value = e.value, force = e.force, transVal;
  57617. /**
  57618. * Return the other axis based on either the axis option or on related
  57619. * series.
  57620. * @private
  57621. */
  57622. function getAxis(coll) {
  57623. const otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis', opt = axis.options[otherColl];
  57624. // Other axis indexed by number
  57625. if (isNumber(opt)) {
  57626. return [chart[otherColl][opt]];
  57627. }
  57628. // Other axis indexed by id (like navigator)
  57629. if (isString(opt)) {
  57630. return [chart.get(opt)];
  57631. }
  57632. // Auto detect based on existing series
  57633. return series.map(function (s) {
  57634. return s[otherColl];
  57635. });
  57636. }
  57637. if ( // For stock chart, by default render paths across the panes
  57638. // except the case when `acrossPanes` is disabled by user (#6644)
  57639. (chart.options.isStock && e.acrossPanes !== false) &&
  57640. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  57641. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  57642. e.preventDefault();
  57643. // Get the related axes based on series
  57644. axes = getAxis(axis.coll);
  57645. // Get the related axes based options.*Axis setting #2810
  57646. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  57647. axes2.forEach(function (A) {
  57648. if (defined(A.options.id) ?
  57649. A.options.id.indexOf('navigator') === -1 :
  57650. true) {
  57651. const a = (A.isXAxis ? 'yAxis' : 'xAxis'), rax = (defined(A.options[a]) ?
  57652. chart[a][A.options[a]] :
  57653. chart[a][0]);
  57654. if (axis === rax) {
  57655. axes.push(A);
  57656. }
  57657. }
  57658. });
  57659. // Remove duplicates in the axes array. If there are no axes in the axes
  57660. // array, we are adding an axis without data, so we need to populate
  57661. // this with grid lines (#2796).
  57662. uniqueAxes = axes.length ?
  57663. [] :
  57664. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  57665. axes.forEach(function (axis2) {
  57666. if (uniqueAxes.indexOf(axis2) === -1 &&
  57667. // Do not draw on axis which overlap completely. #5424
  57668. !find(uniqueAxes, function (unique) {
  57669. return unique.pos === axis2.pos && unique.len === axis2.len;
  57670. })) {
  57671. uniqueAxes.push(axis2);
  57672. }
  57673. });
  57674. transVal = pick(translatedValue, axis.translate(value, void 0, void 0, e.old));
  57675. if (isNumber(transVal)) {
  57676. if (axis.horiz) {
  57677. uniqueAxes.forEach(function (axis2) {
  57678. let skip;
  57679. y1 = axis2.pos;
  57680. y2 = y1 + axis2.len;
  57681. x1 = x2 = Math.round(transVal + axis.transB);
  57682. // outside plot area
  57683. if (force !== 'pass' &&
  57684. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  57685. if (force) {
  57686. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  57687. }
  57688. else {
  57689. skip = true;
  57690. }
  57691. }
  57692. if (!skip) {
  57693. result.push(['M', x1, y1], ['L', x2, y2]);
  57694. }
  57695. });
  57696. }
  57697. else {
  57698. uniqueAxes.forEach(function (axis2) {
  57699. let skip;
  57700. x1 = axis2.pos;
  57701. x2 = x1 + axis2.len;
  57702. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  57703. // outside plot area
  57704. if (force !== 'pass' &&
  57705. (y1 < axisTop || y1 > axisTop + axis.height)) {
  57706. if (force) {
  57707. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  57708. }
  57709. else {
  57710. skip = true;
  57711. }
  57712. }
  57713. if (!skip) {
  57714. result.push(['M', x1, y1], ['L', x2, y2]);
  57715. }
  57716. });
  57717. }
  57718. }
  57719. e.path = result.length > 0 ?
  57720. renderer.crispPolyLine(result, e.lineWidth || 1) :
  57721. // #3557 getPlotLinePath in regular Highcharts also returns null
  57722. null;
  57723. }
  57724. });
  57725. /**
  57726. * Function to crisp a line with multiple segments
  57727. *
  57728. * @private
  57729. * @function Highcharts.SVGRenderer#crispPolyLine
  57730. */
  57731. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  57732. // points format: [['M', 0, 0], ['L', 100, 0]]
  57733. // normalize to a crisp line
  57734. for (let i = 0; i < points.length; i = i + 2) {
  57735. const start = points[i], end = points[i + 1];
  57736. if (start[1] === end[1]) {
  57737. // Substract due to #1129. Now bottom and left axis gridlines behave
  57738. // the same.
  57739. start[1] = end[1] =
  57740. Math.round(start[1]) - (width % 2 / 2);
  57741. }
  57742. if (start[2] === end[2]) {
  57743. start[2] = end[2] =
  57744. Math.round(start[2]) + (width % 2 / 2);
  57745. }
  57746. }
  57747. return points;
  57748. };
  57749. // Wrapper to hide the label
  57750. addEvent(Axis, 'afterHideCrosshair', function () {
  57751. if (this.crossLabel) {
  57752. this.crossLabel = this.crossLabel.hide();
  57753. }
  57754. });
  57755. // Extend crosshairs to also draw the label
  57756. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  57757. // Check if the label has to be drawn
  57758. if (!this.crosshair ||
  57759. !this.crosshair.label ||
  57760. !this.crosshair.label.enabled ||
  57761. !this.cross ||
  57762. !isNumber(this.min) ||
  57763. !isNumber(this.max)) {
  57764. return;
  57765. }
  57766. let chart = this.chart, log = this.logarithmic, options = this.crosshair.label, // the label's options
  57767. horiz = this.horiz, // axis orientation
  57768. opposite = this.opposite, // axis position
  57769. left = this.left, // left position
  57770. top = this.top, // top position
  57771. width = this.width, crossLabel = this.crossLabel, // the svgElement
  57772. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, offset = 0,
  57773. // Use last available event (#5287)
  57774. e = event.e || (this.cross && this.cross.e), point = event.point, min = this.min, max = this.max;
  57775. if (log) {
  57776. min = log.lin2log(min);
  57777. max = log.lin2log(max);
  57778. }
  57779. align = (horiz ? 'center' : opposite ?
  57780. (this.labelAlign === 'right' ? 'right' : 'left') :
  57781. (this.labelAlign === 'left' ? 'left' : 'center'));
  57782. // If the label does not exist yet, create it.
  57783. if (!crossLabel) {
  57784. crossLabel = this.crossLabel = chart.renderer
  57785. .label('', 0, void 0, options.shape || 'callout')
  57786. .addClass('highcharts-crosshair-label highcharts-color-' + (point && point.series ?
  57787. point.series.colorIndex :
  57788. this.series[0] && this.series[0].colorIndex))
  57789. .attr({
  57790. align: options.align || align,
  57791. padding: pick(options.padding, 8),
  57792. r: pick(options.borderRadius, 3),
  57793. zIndex: 2
  57794. })
  57795. .add(this.labelGroup);
  57796. // Presentational
  57797. if (!chart.styledMode) {
  57798. crossLabel
  57799. .attr({
  57800. fill: options.backgroundColor ||
  57801. point && point.series && point.series.color || // #14888
  57802. "#666666" /* Palette.neutralColor60 */,
  57803. stroke: options.borderColor || '',
  57804. 'stroke-width': options.borderWidth || 0
  57805. })
  57806. .css(extend({
  57807. color: "#ffffff" /* Palette.backgroundColor */,
  57808. fontWeight: 'normal',
  57809. fontSize: '0.7em',
  57810. textAlign: 'center'
  57811. }, options.style || {}));
  57812. }
  57813. }
  57814. if (horiz) {
  57815. posx = snap ? (point.plotX || 0) + left : e.chartX;
  57816. posy = top + (opposite ? 0 : this.height);
  57817. }
  57818. else {
  57819. posx = left + this.offset + (opposite ? width : 0);
  57820. posy = snap ? (point.plotY || 0) + top : e.chartY;
  57821. }
  57822. if (!formatOption && !options.formatter) {
  57823. if (this.dateTime) {
  57824. formatFormat = '%b %d, %Y';
  57825. }
  57826. formatOption =
  57827. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  57828. }
  57829. // Show the label
  57830. const value = snap ?
  57831. (this.isXAxis ? point.x : point.y) :
  57832. this.toValue(horiz ? e.chartX : e.chartY);
  57833. // Crosshair should be rendered within Axis range (#7219). Also, the point
  57834. // of currentPriceIndicator should be inside the plot area, #14879.
  57835. const isInside = point && point.series ?
  57836. point.series.isPointInside(point) :
  57837. (isNumber(value) && value > min && value < max);
  57838. let text = '';
  57839. if (formatOption) {
  57840. text = format(formatOption, { value }, chart);
  57841. }
  57842. else if (options.formatter && isNumber(value)) {
  57843. text = options.formatter.call(this, value);
  57844. }
  57845. crossLabel.attr({
  57846. text,
  57847. x: posx,
  57848. y: posy,
  57849. visibility: isInside ? 'inherit' : 'hidden'
  57850. });
  57851. crossBox = crossLabel.getBBox();
  57852. // now it is placed we can correct its position
  57853. if (isNumber(crossLabel.x) && !horiz && !opposite) {
  57854. posx = crossLabel.x - (crossBox.width / 2);
  57855. }
  57856. if (isNumber(crossLabel.y)) {
  57857. if (horiz) {
  57858. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  57859. posy = crossLabel.y - crossBox.height;
  57860. }
  57861. }
  57862. else {
  57863. posy = crossLabel.y - (crossBox.height / 2);
  57864. }
  57865. }
  57866. // check the edges
  57867. if (horiz) {
  57868. limit = {
  57869. left: left - crossBox.x,
  57870. right: left + this.width - crossBox.x
  57871. };
  57872. }
  57873. else {
  57874. limit = {
  57875. left: this.labelAlign === 'left' ? left : 0,
  57876. right: this.labelAlign === 'right' ?
  57877. left + this.width :
  57878. chart.chartWidth
  57879. };
  57880. }
  57881. // left edge
  57882. if (crossLabel.translateX < limit.left) {
  57883. offset = limit.left - crossLabel.translateX;
  57884. }
  57885. // right edge
  57886. if (crossLabel.translateX + crossBox.width >= limit.right) {
  57887. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  57888. }
  57889. // show the crosslabel
  57890. crossLabel.attr({
  57891. x: posx + offset,
  57892. y: posy,
  57893. // First set x and y, then anchorX and anchorY, when box is actually
  57894. // calculated, #5702
  57895. anchorX: horiz ?
  57896. posx :
  57897. (this.opposite ? 0 : chart.chartWidth),
  57898. anchorY: horiz ?
  57899. (this.opposite ? chart.chartHeight : 0) :
  57900. posy + crossBox.height / 2
  57901. });
  57902. });
  57903. /**
  57904. * Based on the data grouping options decides whether
  57905. * the data should be cropped while processing.
  57906. *
  57907. * @ignore
  57908. * @function Highcharts.Series#forceCropping
  57909. */
  57910. Series.prototype.forceCropping = function () {
  57911. const chart = this.chart, options = this.options, dataGroupingOptions = options.dataGrouping, groupingEnabled = this.allowDG !== false && dataGroupingOptions &&
  57912. pick(dataGroupingOptions.enabled, chart.options.isStock);
  57913. return groupingEnabled;
  57914. };
  57915. addEvent(Chart, 'update', function (e) {
  57916. const options = e.options;
  57917. // Use case: enabling scrollbar from a disabled state.
  57918. // Scrollbar needs to be initialized from a controller, Navigator in this
  57919. // case (#6615)
  57920. if ('scrollbar' in options && this.navigator) {
  57921. merge(true, this.options.scrollbar, options.scrollbar);
  57922. this.navigator.update({});
  57923. delete options.scrollbar;
  57924. }
  57925. });
  57926. /* *
  57927. *
  57928. * Default Export
  57929. *
  57930. * */
  57931. return StockChart;
  57932. });
  57933. _registerModule(_modules, 'masters/modules/stock.src.js', [_modules['Core/Globals.js'], _modules['Series/DataModifyComposition.js'], _modules['Stock/Navigator/Navigator.js'], _modules['Stock/RangeSelector/RangeSelector.js'], _modules['Stock/Scrollbar/Scrollbar.js'], _modules['Core/Axis/OrdinalAxis.js'], _modules['Series/OHLC/OHLCSeries.js'], _modules['Series/Flags/FlagsSeries.js'], _modules['Core/Chart/StockChart.js']], function (Highcharts, DataModifyComposition, Navigator, RangeSelector, Scrollbar, OrdinalAxis, OHLCSeries, FlagsSeries, StockChart) {
  57934. const G = Highcharts;
  57935. // Classes
  57936. G.Navigator = Navigator;
  57937. G.RangeSelector = RangeSelector;
  57938. G.Scrollbar = Scrollbar;
  57939. G.StockChart = G.stockChart = StockChart.stockChart;
  57940. // Compositions
  57941. DataModifyComposition.compose(G.Series, G.Axis, G.Point);
  57942. FlagsSeries.compose(G.Renderer);
  57943. Navigator.compose(G.Axis, G.Chart, G.Series);
  57944. OHLCSeries.compose(G.Series);
  57945. OrdinalAxis.compose(G.Axis, G.Series, G.Chart);
  57946. RangeSelector.compose(G.Axis, G.Chart);
  57947. Scrollbar.compose(G.Axis);
  57948. });
  57949. _registerModule(_modules, 'masters/highstock.src.js', [_modules['masters/highcharts.src.js']], function (Highcharts) {
  57950. Highcharts.product = 'Highstock';
  57951. return Highcharts;
  57952. });
  57953. _modules['masters/highstock.src.js']._modules = _modules;
  57954. return _modules['masters/highstock.src.js'];
  57955. }));