highstock.src.js 2.5 MB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613216142161521616216172161821619216202162121622216232162421625216262162721628216292163021631216322163321634216352163621637216382163921640216412164221643216442164521646216472164821649216502165121652216532165421655216562165721658216592166021661216622166321664216652166621667216682166921670216712167221673216742167521676216772167821679216802168121682216832168421685216862168721688216892169021691216922169321694216952169621697216982169921700217012170221703217042170521706217072170821709217102171121712217132171421715217162171721718217192172021721217222172321724217252172621727217282172921730217312173221733217342173521736217372173821739217402174121742217432174421745217462174721748217492175021751217522175321754217552175621757217582175921760217612176221763217642176521766217672176821769217702177121772217732177421775217762177721778217792178021781217822178321784217852178621787217882178921790217912179221793217942179521796217972179821799218002180121802218032180421805218062180721808218092181021811218122181321814218152181621817218182181921820218212182221823218242182521826218272182821829218302183121832218332183421835218362183721838218392184021841218422184321844218452184621847218482184921850218512185221853218542185521856218572185821859218602186121862218632186421865218662186721868218692187021871218722187321874218752187621877218782187921880218812188221883218842188521886218872188821889218902189121892218932189421895218962189721898218992190021901219022190321904219052190621907219082190921910219112191221913219142191521916219172191821919219202192121922219232192421925219262192721928219292193021931219322193321934219352193621937219382193921940219412194221943219442194521946219472194821949219502195121952219532195421955219562195721958219592196021961219622196321964219652196621967219682196921970219712197221973219742197521976219772197821979219802198121982219832198421985219862198721988219892199021991219922199321994219952199621997219982199922000220012200222003220042200522006220072200822009220102201122012220132201422015220162201722018220192202022021220222202322024220252202622027220282202922030220312203222033220342203522036220372203822039220402204122042220432204422045220462204722048220492205022051220522205322054220552205622057220582205922060220612206222063220642206522066220672206822069220702207122072220732207422075220762207722078220792208022081220822208322084220852208622087220882208922090220912209222093220942209522096220972209822099221002210122102221032210422105221062210722108221092211022111221122211322114221152211622117221182211922120221212212222123221242212522126221272212822129221302213122132221332213422135221362213722138221392214022141221422214322144221452214622147221482214922150221512215222153221542215522156221572215822159221602216122162221632216422165221662216722168221692217022171221722217322174221752217622177221782217922180221812218222183221842218522186221872218822189221902219122192221932219422195221962219722198221992220022201222022220322204222052220622207222082220922210222112221222213222142221522216222172221822219222202222122222222232222422225222262222722228222292223022231222322223322234222352223622237222382223922240222412224222243222442224522246222472224822249222502225122252222532225422255222562225722258222592226022261222622226322264222652226622267222682226922270222712227222273222742227522276222772227822279222802228122282222832228422285222862228722288222892229022291222922229322294222952229622297222982229922300223012230222303223042230522306223072230822309223102231122312223132231422315223162231722318223192232022321223222232322324223252232622327223282232922330223312233222333223342233522336223372233822339223402234122342223432234422345223462234722348223492235022351223522235322354223552235622357223582235922360223612236222363223642236522366223672236822369223702237122372223732237422375223762237722378223792238022381223822238322384223852238622387223882238922390223912239222393223942239522396223972239822399224002240122402224032240422405224062240722408224092241022411224122241322414224152241622417224182241922420224212242222423224242242522426224272242822429224302243122432224332243422435224362243722438224392244022441224422244322444224452244622447224482244922450224512245222453224542245522456224572245822459224602246122462224632246422465224662246722468224692247022471224722247322474224752247622477224782247922480224812248222483224842248522486224872248822489224902249122492224932249422495224962249722498224992250022501225022250322504225052250622507225082250922510225112251222513225142251522516225172251822519225202252122522225232252422525225262252722528225292253022531225322253322534225352253622537225382253922540225412254222543225442254522546225472254822549225502255122552225532255422555225562255722558225592256022561225622256322564225652256622567225682256922570225712257222573225742257522576225772257822579225802258122582225832258422585225862258722588225892259022591225922259322594225952259622597225982259922600226012260222603226042260522606226072260822609226102261122612226132261422615226162261722618226192262022621226222262322624226252262622627226282262922630226312263222633226342263522636226372263822639226402264122642226432264422645226462264722648226492265022651226522265322654226552265622657226582265922660226612266222663226642266522666226672266822669226702267122672226732267422675226762267722678226792268022681226822268322684226852268622687226882268922690226912269222693226942269522696226972269822699227002270122702227032270422705227062270722708227092271022711227122271322714227152271622717227182271922720227212272222723227242272522726227272272822729227302273122732227332273422735227362273722738227392274022741227422274322744227452274622747227482274922750227512275222753227542275522756227572275822759227602276122762227632276422765227662276722768227692277022771227722277322774227752277622777227782277922780227812278222783227842278522786227872278822789227902279122792227932279422795227962279722798227992280022801228022280322804228052280622807228082280922810228112281222813228142281522816228172281822819228202282122822228232282422825228262282722828228292283022831228322283322834228352283622837228382283922840228412284222843228442284522846228472284822849228502285122852228532285422855228562285722858228592286022861228622286322864228652286622867228682286922870228712287222873228742287522876228772287822879228802288122882228832288422885228862288722888228892289022891228922289322894228952289622897228982289922900229012290222903229042290522906229072290822909229102291122912229132291422915229162291722918229192292022921229222292322924229252292622927229282292922930229312293222933229342293522936229372293822939229402294122942229432294422945229462294722948229492295022951229522295322954229552295622957229582295922960229612296222963229642296522966229672296822969229702297122972229732297422975229762297722978229792298022981229822298322984229852298622987229882298922990229912299222993229942299522996229972299822999230002300123002230032300423005230062300723008230092301023011230122301323014230152301623017230182301923020230212302223023230242302523026230272302823029230302303123032230332303423035230362303723038230392304023041230422304323044230452304623047230482304923050230512305223053230542305523056230572305823059230602306123062230632306423065230662306723068230692307023071230722307323074230752307623077230782307923080230812308223083230842308523086230872308823089230902309123092230932309423095230962309723098230992310023101231022310323104231052310623107231082310923110231112311223113231142311523116231172311823119231202312123122231232312423125231262312723128231292313023131231322313323134231352313623137231382313923140231412314223143231442314523146231472314823149231502315123152231532315423155231562315723158231592316023161231622316323164231652316623167231682316923170231712317223173231742317523176231772317823179231802318123182231832318423185231862318723188231892319023191231922319323194231952319623197231982319923200232012320223203232042320523206232072320823209232102321123212232132321423215232162321723218232192322023221232222322323224232252322623227232282322923230232312323223233232342323523236232372323823239232402324123242232432324423245232462324723248232492325023251232522325323254232552325623257232582325923260232612326223263232642326523266232672326823269232702327123272232732327423275232762327723278232792328023281232822328323284232852328623287232882328923290232912329223293232942329523296232972329823299233002330123302233032330423305233062330723308233092331023311233122331323314233152331623317233182331923320233212332223323233242332523326233272332823329233302333123332233332333423335233362333723338233392334023341233422334323344233452334623347233482334923350233512335223353233542335523356233572335823359233602336123362233632336423365233662336723368233692337023371233722337323374233752337623377233782337923380233812338223383233842338523386233872338823389233902339123392233932339423395233962339723398233992340023401234022340323404234052340623407234082340923410234112341223413234142341523416234172341823419234202342123422234232342423425234262342723428234292343023431234322343323434234352343623437234382343923440234412344223443234442344523446234472344823449234502345123452234532345423455234562345723458234592346023461234622346323464234652346623467234682346923470234712347223473234742347523476234772347823479234802348123482234832348423485234862348723488234892349023491234922349323494234952349623497234982349923500235012350223503235042350523506235072350823509235102351123512235132351423515235162351723518235192352023521235222352323524235252352623527235282352923530235312353223533235342353523536235372353823539235402354123542235432354423545235462354723548235492355023551235522355323554235552355623557235582355923560235612356223563235642356523566235672356823569235702357123572235732357423575235762357723578235792358023581235822358323584235852358623587235882358923590235912359223593235942359523596235972359823599236002360123602236032360423605236062360723608236092361023611236122361323614236152361623617236182361923620236212362223623236242362523626236272362823629236302363123632236332363423635236362363723638236392364023641236422364323644236452364623647236482364923650236512365223653236542365523656236572365823659236602366123662236632366423665236662366723668236692367023671236722367323674236752367623677236782367923680236812368223683236842368523686236872368823689236902369123692236932369423695236962369723698236992370023701237022370323704237052370623707237082370923710237112371223713237142371523716237172371823719237202372123722237232372423725237262372723728237292373023731237322373323734237352373623737237382373923740237412374223743237442374523746237472374823749237502375123752237532375423755237562375723758237592376023761237622376323764237652376623767237682376923770237712377223773237742377523776237772377823779237802378123782237832378423785237862378723788237892379023791237922379323794237952379623797237982379923800238012380223803238042380523806238072380823809238102381123812238132381423815238162381723818238192382023821238222382323824238252382623827238282382923830238312383223833238342383523836238372383823839238402384123842238432384423845238462384723848238492385023851238522385323854238552385623857238582385923860238612386223863238642386523866238672386823869238702387123872238732387423875238762387723878238792388023881238822388323884238852388623887238882388923890238912389223893238942389523896238972389823899239002390123902239032390423905239062390723908239092391023911239122391323914239152391623917239182391923920239212392223923239242392523926239272392823929239302393123932239332393423935239362393723938239392394023941239422394323944239452394623947239482394923950239512395223953239542395523956239572395823959239602396123962239632396423965239662396723968239692397023971239722397323974239752397623977239782397923980239812398223983239842398523986239872398823989239902399123992239932399423995239962399723998239992400024001240022400324004240052400624007240082400924010240112401224013240142401524016240172401824019240202402124022240232402424025240262402724028240292403024031240322403324034240352403624037240382403924040240412404224043240442404524046240472404824049240502405124052240532405424055240562405724058240592406024061240622406324064240652406624067240682406924070240712407224073240742407524076240772407824079240802408124082240832408424085240862408724088240892409024091240922409324094240952409624097240982409924100241012410224103241042410524106241072410824109241102411124112241132411424115241162411724118241192412024121241222412324124241252412624127241282412924130241312413224133241342413524136241372413824139241402414124142241432414424145241462414724148241492415024151241522415324154241552415624157241582415924160241612416224163241642416524166241672416824169241702417124172241732417424175241762417724178241792418024181241822418324184241852418624187241882418924190241912419224193241942419524196241972419824199242002420124202242032420424205242062420724208242092421024211242122421324214242152421624217242182421924220242212422224223242242422524226242272422824229242302423124232242332423424235242362423724238242392424024241242422424324244242452424624247242482424924250242512425224253242542425524256242572425824259242602426124262242632426424265242662426724268242692427024271242722427324274242752427624277242782427924280242812428224283242842428524286242872428824289242902429124292242932429424295242962429724298242992430024301243022430324304243052430624307243082430924310243112431224313243142431524316243172431824319243202432124322243232432424325243262432724328243292433024331243322433324334243352433624337243382433924340243412434224343243442434524346243472434824349243502435124352243532435424355243562435724358243592436024361243622436324364243652436624367243682436924370243712437224373243742437524376243772437824379243802438124382243832438424385243862438724388243892439024391243922439324394243952439624397243982439924400244012440224403244042440524406244072440824409244102441124412244132441424415244162441724418244192442024421244222442324424244252442624427244282442924430244312443224433244342443524436244372443824439244402444124442244432444424445244462444724448244492445024451244522445324454244552445624457244582445924460244612446224463244642446524466244672446824469244702447124472244732447424475244762447724478244792448024481244822448324484244852448624487244882448924490244912449224493244942449524496244972449824499245002450124502245032450424505245062450724508245092451024511245122451324514245152451624517245182451924520245212452224523245242452524526245272452824529245302453124532245332453424535245362453724538245392454024541245422454324544245452454624547245482454924550245512455224553245542455524556245572455824559245602456124562245632456424565245662456724568245692457024571245722457324574245752457624577245782457924580245812458224583245842458524586245872458824589245902459124592245932459424595245962459724598245992460024601246022460324604246052460624607246082460924610246112461224613246142461524616246172461824619246202462124622246232462424625246262462724628246292463024631246322463324634246352463624637246382463924640246412464224643246442464524646246472464824649246502465124652246532465424655246562465724658246592466024661246622466324664246652466624667246682466924670246712467224673246742467524676246772467824679246802468124682246832468424685246862468724688246892469024691246922469324694246952469624697246982469924700247012470224703247042470524706247072470824709247102471124712247132471424715247162471724718247192472024721247222472324724247252472624727247282472924730247312473224733247342473524736247372473824739247402474124742247432474424745247462474724748247492475024751247522475324754247552475624757247582475924760247612476224763247642476524766247672476824769247702477124772247732477424775247762477724778247792478024781247822478324784247852478624787247882478924790247912479224793247942479524796247972479824799248002480124802248032480424805248062480724808248092481024811248122481324814248152481624817248182481924820248212482224823248242482524826248272482824829248302483124832248332483424835248362483724838248392484024841248422484324844248452484624847248482484924850248512485224853248542485524856248572485824859248602486124862248632486424865248662486724868248692487024871248722487324874248752487624877248782487924880248812488224883248842488524886248872488824889248902489124892248932489424895248962489724898248992490024901249022490324904249052490624907249082490924910249112491224913249142491524916249172491824919249202492124922249232492424925249262492724928249292493024931249322493324934249352493624937249382493924940249412494224943249442494524946249472494824949249502495124952249532495424955249562495724958249592496024961249622496324964249652496624967249682496924970249712497224973249742497524976249772497824979249802498124982249832498424985249862498724988249892499024991249922499324994249952499624997249982499925000250012500225003250042500525006250072500825009250102501125012250132501425015250162501725018250192502025021250222502325024250252502625027250282502925030250312503225033250342503525036250372503825039250402504125042250432504425045250462504725048250492505025051250522505325054250552505625057250582505925060250612506225063250642506525066250672506825069250702507125072250732507425075250762507725078250792508025081250822508325084250852508625087250882508925090250912509225093250942509525096250972509825099251002510125102251032510425105251062510725108251092511025111251122511325114251152511625117251182511925120251212512225123251242512525126251272512825129251302513125132251332513425135251362513725138251392514025141251422514325144251452514625147251482514925150251512515225153251542515525156251572515825159251602516125162251632516425165251662516725168251692517025171251722517325174251752517625177251782517925180251812518225183251842518525186251872518825189251902519125192251932519425195251962519725198251992520025201252022520325204252052520625207252082520925210252112521225213252142521525216252172521825219252202522125222252232522425225252262522725228252292523025231252322523325234252352523625237252382523925240252412524225243252442524525246252472524825249252502525125252252532525425255252562525725258252592526025261252622526325264252652526625267252682526925270252712527225273252742527525276252772527825279252802528125282252832528425285252862528725288252892529025291252922529325294252952529625297252982529925300253012530225303253042530525306253072530825309253102531125312253132531425315253162531725318253192532025321253222532325324253252532625327253282532925330253312533225333253342533525336253372533825339253402534125342253432534425345253462534725348253492535025351253522535325354253552535625357253582535925360253612536225363253642536525366253672536825369253702537125372253732537425375253762537725378253792538025381253822538325384253852538625387253882538925390253912539225393253942539525396253972539825399254002540125402254032540425405254062540725408254092541025411254122541325414254152541625417254182541925420254212542225423254242542525426254272542825429254302543125432254332543425435254362543725438254392544025441254422544325444254452544625447254482544925450254512545225453254542545525456254572545825459254602546125462254632546425465254662546725468254692547025471254722547325474254752547625477254782547925480254812548225483254842548525486254872548825489254902549125492254932549425495254962549725498254992550025501255022550325504255052550625507255082550925510255112551225513255142551525516255172551825519255202552125522255232552425525255262552725528255292553025531255322553325534255352553625537255382553925540255412554225543255442554525546255472554825549255502555125552255532555425555255562555725558255592556025561255622556325564255652556625567255682556925570255712557225573255742557525576255772557825579255802558125582255832558425585255862558725588255892559025591255922559325594255952559625597255982559925600256012560225603256042560525606256072560825609256102561125612256132561425615256162561725618256192562025621256222562325624256252562625627256282562925630256312563225633256342563525636256372563825639256402564125642256432564425645256462564725648256492565025651256522565325654256552565625657256582565925660256612566225663256642566525666256672566825669256702567125672256732567425675256762567725678256792568025681256822568325684256852568625687256882568925690256912569225693256942569525696256972569825699257002570125702257032570425705257062570725708257092571025711257122571325714257152571625717257182571925720257212572225723257242572525726257272572825729257302573125732257332573425735257362573725738257392574025741257422574325744257452574625747257482574925750257512575225753257542575525756257572575825759257602576125762257632576425765257662576725768257692577025771257722577325774257752577625777257782577925780257812578225783257842578525786257872578825789257902579125792257932579425795257962579725798257992580025801258022580325804258052580625807258082580925810258112581225813258142581525816258172581825819258202582125822258232582425825258262582725828258292583025831258322583325834258352583625837258382583925840258412584225843258442584525846258472584825849258502585125852258532585425855258562585725858258592586025861258622586325864258652586625867258682586925870258712587225873258742587525876258772587825879258802588125882258832588425885258862588725888258892589025891258922589325894258952589625897258982589925900259012590225903259042590525906259072590825909259102591125912259132591425915259162591725918259192592025921259222592325924259252592625927259282592925930259312593225933259342593525936259372593825939259402594125942259432594425945259462594725948259492595025951259522595325954259552595625957259582595925960259612596225963259642596525966259672596825969259702597125972259732597425975259762597725978259792598025981259822598325984259852598625987259882598925990259912599225993259942599525996259972599825999260002600126002260032600426005260062600726008260092601026011260122601326014260152601626017260182601926020260212602226023260242602526026260272602826029260302603126032260332603426035260362603726038260392604026041260422604326044260452604626047260482604926050260512605226053260542605526056260572605826059260602606126062260632606426065260662606726068260692607026071260722607326074260752607626077260782607926080260812608226083260842608526086260872608826089260902609126092260932609426095260962609726098260992610026101261022610326104261052610626107261082610926110261112611226113261142611526116261172611826119261202612126122261232612426125261262612726128261292613026131261322613326134261352613626137261382613926140261412614226143261442614526146261472614826149261502615126152261532615426155261562615726158261592616026161261622616326164261652616626167261682616926170261712617226173261742617526176261772617826179261802618126182261832618426185261862618726188261892619026191261922619326194261952619626197261982619926200262012620226203262042620526206262072620826209262102621126212262132621426215262162621726218262192622026221262222622326224262252622626227262282622926230262312623226233262342623526236262372623826239262402624126242262432624426245262462624726248262492625026251262522625326254262552625626257262582625926260262612626226263262642626526266262672626826269262702627126272262732627426275262762627726278262792628026281262822628326284262852628626287262882628926290262912629226293262942629526296262972629826299263002630126302263032630426305263062630726308263092631026311263122631326314263152631626317263182631926320263212632226323263242632526326263272632826329263302633126332263332633426335263362633726338263392634026341263422634326344263452634626347263482634926350263512635226353263542635526356263572635826359263602636126362263632636426365263662636726368263692637026371263722637326374263752637626377263782637926380263812638226383263842638526386263872638826389263902639126392263932639426395263962639726398263992640026401264022640326404264052640626407264082640926410264112641226413264142641526416264172641826419264202642126422264232642426425264262642726428264292643026431264322643326434264352643626437264382643926440264412644226443264442644526446264472644826449264502645126452264532645426455264562645726458264592646026461264622646326464264652646626467264682646926470264712647226473264742647526476264772647826479264802648126482264832648426485264862648726488264892649026491264922649326494264952649626497264982649926500265012650226503265042650526506265072650826509265102651126512265132651426515265162651726518265192652026521265222652326524265252652626527265282652926530265312653226533265342653526536265372653826539265402654126542265432654426545265462654726548265492655026551265522655326554265552655626557265582655926560265612656226563265642656526566265672656826569265702657126572265732657426575265762657726578265792658026581265822658326584265852658626587265882658926590265912659226593265942659526596265972659826599266002660126602266032660426605266062660726608266092661026611266122661326614266152661626617266182661926620266212662226623266242662526626266272662826629266302663126632266332663426635266362663726638266392664026641266422664326644266452664626647266482664926650266512665226653266542665526656266572665826659266602666126662266632666426665266662666726668266692667026671266722667326674266752667626677266782667926680266812668226683266842668526686266872668826689266902669126692266932669426695266962669726698266992670026701267022670326704267052670626707267082670926710267112671226713267142671526716267172671826719267202672126722267232672426725267262672726728267292673026731267322673326734267352673626737267382673926740267412674226743267442674526746267472674826749267502675126752267532675426755267562675726758267592676026761267622676326764267652676626767267682676926770267712677226773267742677526776267772677826779267802678126782267832678426785267862678726788267892679026791267922679326794267952679626797267982679926800268012680226803268042680526806268072680826809268102681126812268132681426815268162681726818268192682026821268222682326824268252682626827268282682926830268312683226833268342683526836268372683826839268402684126842268432684426845268462684726848268492685026851268522685326854268552685626857268582685926860268612686226863268642686526866268672686826869268702687126872268732687426875268762687726878268792688026881268822688326884268852688626887268882688926890268912689226893268942689526896268972689826899269002690126902269032690426905269062690726908269092691026911269122691326914269152691626917269182691926920269212692226923269242692526926269272692826929269302693126932269332693426935269362693726938269392694026941269422694326944269452694626947269482694926950269512695226953269542695526956269572695826959269602696126962269632696426965269662696726968269692697026971269722697326974269752697626977269782697926980269812698226983269842698526986269872698826989269902699126992269932699426995269962699726998269992700027001270022700327004270052700627007270082700927010270112701227013270142701527016270172701827019270202702127022270232702427025270262702727028270292703027031270322703327034270352703627037270382703927040270412704227043270442704527046270472704827049270502705127052270532705427055270562705727058270592706027061270622706327064270652706627067270682706927070270712707227073270742707527076270772707827079270802708127082270832708427085270862708727088270892709027091270922709327094270952709627097270982709927100271012710227103271042710527106271072710827109271102711127112271132711427115271162711727118271192712027121271222712327124271252712627127271282712927130271312713227133271342713527136271372713827139271402714127142271432714427145271462714727148271492715027151271522715327154271552715627157271582715927160271612716227163271642716527166271672716827169271702717127172271732717427175271762717727178271792718027181271822718327184271852718627187271882718927190271912719227193271942719527196271972719827199272002720127202272032720427205272062720727208272092721027211272122721327214272152721627217272182721927220272212722227223272242722527226272272722827229272302723127232272332723427235272362723727238272392724027241272422724327244272452724627247272482724927250272512725227253272542725527256272572725827259272602726127262272632726427265272662726727268272692727027271272722727327274272752727627277272782727927280272812728227283272842728527286272872728827289272902729127292272932729427295272962729727298272992730027301273022730327304273052730627307273082730927310273112731227313273142731527316273172731827319273202732127322273232732427325273262732727328273292733027331273322733327334273352733627337273382733927340273412734227343273442734527346273472734827349273502735127352273532735427355273562735727358273592736027361273622736327364273652736627367273682736927370273712737227373273742737527376273772737827379273802738127382273832738427385273862738727388273892739027391273922739327394273952739627397273982739927400274012740227403274042740527406274072740827409274102741127412274132741427415274162741727418274192742027421274222742327424274252742627427274282742927430274312743227433274342743527436274372743827439274402744127442274432744427445274462744727448274492745027451274522745327454274552745627457274582745927460274612746227463274642746527466274672746827469274702747127472274732747427475274762747727478274792748027481274822748327484274852748627487274882748927490274912749227493274942749527496274972749827499275002750127502275032750427505275062750727508275092751027511275122751327514275152751627517275182751927520275212752227523275242752527526275272752827529275302753127532275332753427535275362753727538275392754027541275422754327544275452754627547275482754927550275512755227553275542755527556275572755827559275602756127562275632756427565275662756727568275692757027571275722757327574275752757627577275782757927580275812758227583275842758527586275872758827589275902759127592275932759427595275962759727598275992760027601276022760327604276052760627607276082760927610276112761227613276142761527616276172761827619276202762127622276232762427625276262762727628276292763027631276322763327634276352763627637276382763927640276412764227643276442764527646276472764827649276502765127652276532765427655276562765727658276592766027661276622766327664276652766627667276682766927670276712767227673276742767527676276772767827679276802768127682276832768427685276862768727688276892769027691276922769327694276952769627697276982769927700277012770227703277042770527706277072770827709277102771127712277132771427715277162771727718277192772027721277222772327724277252772627727277282772927730277312773227733277342773527736277372773827739277402774127742277432774427745277462774727748277492775027751277522775327754277552775627757277582775927760277612776227763277642776527766277672776827769277702777127772277732777427775277762777727778277792778027781277822778327784277852778627787277882778927790277912779227793277942779527796277972779827799278002780127802278032780427805278062780727808278092781027811278122781327814278152781627817278182781927820278212782227823278242782527826278272782827829278302783127832278332783427835278362783727838278392784027841278422784327844278452784627847278482784927850278512785227853278542785527856278572785827859278602786127862278632786427865278662786727868278692787027871278722787327874278752787627877278782787927880278812788227883278842788527886278872788827889278902789127892278932789427895278962789727898278992790027901279022790327904279052790627907279082790927910279112791227913279142791527916279172791827919279202792127922279232792427925279262792727928279292793027931279322793327934279352793627937279382793927940279412794227943279442794527946279472794827949279502795127952279532795427955279562795727958279592796027961279622796327964279652796627967279682796927970279712797227973279742797527976279772797827979279802798127982279832798427985279862798727988279892799027991279922799327994279952799627997279982799928000280012800228003280042800528006280072800828009280102801128012280132801428015280162801728018280192802028021280222802328024280252802628027280282802928030280312803228033280342803528036280372803828039280402804128042280432804428045280462804728048280492805028051280522805328054280552805628057280582805928060280612806228063280642806528066280672806828069280702807128072280732807428075280762807728078280792808028081280822808328084280852808628087280882808928090280912809228093280942809528096280972809828099281002810128102281032810428105281062810728108281092811028111281122811328114281152811628117281182811928120281212812228123281242812528126281272812828129281302813128132281332813428135281362813728138281392814028141281422814328144281452814628147281482814928150281512815228153281542815528156281572815828159281602816128162281632816428165281662816728168281692817028171281722817328174281752817628177281782817928180281812818228183281842818528186281872818828189281902819128192281932819428195281962819728198281992820028201282022820328204282052820628207282082820928210282112821228213282142821528216282172821828219282202822128222282232822428225282262822728228282292823028231282322823328234282352823628237282382823928240282412824228243282442824528246282472824828249282502825128252282532825428255282562825728258282592826028261282622826328264282652826628267282682826928270282712827228273282742827528276282772827828279282802828128282282832828428285282862828728288282892829028291282922829328294282952829628297282982829928300283012830228303283042830528306283072830828309283102831128312283132831428315283162831728318283192832028321283222832328324283252832628327283282832928330283312833228333283342833528336283372833828339283402834128342283432834428345283462834728348283492835028351283522835328354283552835628357283582835928360283612836228363283642836528366283672836828369283702837128372283732837428375283762837728378283792838028381283822838328384283852838628387283882838928390283912839228393283942839528396283972839828399284002840128402284032840428405284062840728408284092841028411284122841328414284152841628417284182841928420284212842228423284242842528426284272842828429284302843128432284332843428435284362843728438284392844028441284422844328444284452844628447284482844928450284512845228453284542845528456284572845828459284602846128462284632846428465284662846728468284692847028471284722847328474284752847628477284782847928480284812848228483284842848528486284872848828489284902849128492284932849428495284962849728498284992850028501285022850328504285052850628507285082850928510285112851228513285142851528516285172851828519285202852128522285232852428525285262852728528285292853028531285322853328534285352853628537285382853928540285412854228543285442854528546285472854828549285502855128552285532855428555285562855728558285592856028561285622856328564285652856628567285682856928570285712857228573285742857528576285772857828579285802858128582285832858428585285862858728588285892859028591285922859328594285952859628597285982859928600286012860228603286042860528606286072860828609286102861128612286132861428615286162861728618286192862028621286222862328624286252862628627286282862928630286312863228633286342863528636286372863828639286402864128642286432864428645286462864728648286492865028651286522865328654286552865628657286582865928660286612866228663286642866528666286672866828669286702867128672286732867428675286762867728678286792868028681286822868328684286852868628687286882868928690286912869228693286942869528696286972869828699287002870128702287032870428705287062870728708287092871028711287122871328714287152871628717287182871928720287212872228723287242872528726287272872828729287302873128732287332873428735287362873728738287392874028741287422874328744287452874628747287482874928750287512875228753287542875528756287572875828759287602876128762287632876428765287662876728768287692877028771287722877328774287752877628777287782877928780287812878228783287842878528786287872878828789287902879128792287932879428795287962879728798287992880028801288022880328804288052880628807288082880928810288112881228813288142881528816288172881828819288202882128822288232882428825288262882728828288292883028831288322883328834288352883628837288382883928840288412884228843288442884528846288472884828849288502885128852288532885428855288562885728858288592886028861288622886328864288652886628867288682886928870288712887228873288742887528876288772887828879288802888128882288832888428885288862888728888288892889028891288922889328894288952889628897288982889928900289012890228903289042890528906289072890828909289102891128912289132891428915289162891728918289192892028921289222892328924289252892628927289282892928930289312893228933289342893528936289372893828939289402894128942289432894428945289462894728948289492895028951289522895328954289552895628957289582895928960289612896228963289642896528966289672896828969289702897128972289732897428975289762897728978289792898028981289822898328984289852898628987289882898928990289912899228993289942899528996289972899828999290002900129002290032900429005290062900729008290092901029011290122901329014290152901629017290182901929020290212902229023290242902529026290272902829029290302903129032290332903429035290362903729038290392904029041290422904329044290452904629047290482904929050290512905229053290542905529056290572905829059290602906129062290632906429065290662906729068290692907029071290722907329074290752907629077290782907929080290812908229083290842908529086290872908829089290902909129092290932909429095290962909729098290992910029101291022910329104291052910629107291082910929110291112911229113291142911529116291172911829119291202912129122291232912429125291262912729128291292913029131291322913329134291352913629137291382913929140291412914229143291442914529146291472914829149291502915129152291532915429155291562915729158291592916029161291622916329164291652916629167291682916929170291712917229173291742917529176291772917829179291802918129182291832918429185291862918729188291892919029191291922919329194291952919629197291982919929200292012920229203292042920529206292072920829209292102921129212292132921429215292162921729218292192922029221292222922329224292252922629227292282922929230292312923229233292342923529236292372923829239292402924129242292432924429245292462924729248292492925029251292522925329254292552925629257292582925929260292612926229263292642926529266292672926829269292702927129272292732927429275292762927729278292792928029281292822928329284292852928629287292882928929290292912929229293292942929529296292972929829299293002930129302293032930429305293062930729308293092931029311293122931329314293152931629317293182931929320293212932229323293242932529326293272932829329293302933129332293332933429335293362933729338293392934029341293422934329344293452934629347293482934929350293512935229353293542935529356293572935829359293602936129362293632936429365293662936729368293692937029371293722937329374293752937629377293782937929380293812938229383293842938529386293872938829389293902939129392293932939429395293962939729398293992940029401294022940329404294052940629407294082940929410294112941229413294142941529416294172941829419294202942129422294232942429425294262942729428294292943029431294322943329434294352943629437294382943929440294412944229443294442944529446294472944829449294502945129452294532945429455294562945729458294592946029461294622946329464294652946629467294682946929470294712947229473294742947529476294772947829479294802948129482294832948429485294862948729488294892949029491294922949329494294952949629497294982949929500295012950229503295042950529506295072950829509295102951129512295132951429515295162951729518295192952029521295222952329524295252952629527295282952929530295312953229533295342953529536295372953829539295402954129542295432954429545295462954729548295492955029551295522955329554295552955629557295582955929560295612956229563295642956529566295672956829569295702957129572295732957429575295762957729578295792958029581295822958329584295852958629587295882958929590295912959229593295942959529596295972959829599296002960129602296032960429605296062960729608296092961029611296122961329614296152961629617296182961929620296212962229623296242962529626296272962829629296302963129632296332963429635296362963729638296392964029641296422964329644296452964629647296482964929650296512965229653296542965529656296572965829659296602966129662296632966429665296662966729668296692967029671296722967329674296752967629677296782967929680296812968229683296842968529686296872968829689296902969129692296932969429695296962969729698296992970029701297022970329704297052970629707297082970929710297112971229713297142971529716297172971829719297202972129722297232972429725297262972729728297292973029731297322973329734297352973629737297382973929740297412974229743297442974529746297472974829749297502975129752297532975429755297562975729758297592976029761297622976329764297652976629767297682976929770297712977229773297742977529776297772977829779297802978129782297832978429785297862978729788297892979029791297922979329794297952979629797297982979929800298012980229803298042980529806298072980829809298102981129812298132981429815298162981729818298192982029821298222982329824298252982629827298282982929830298312983229833298342983529836298372983829839298402984129842298432984429845298462984729848298492985029851298522985329854298552985629857298582985929860298612986229863298642986529866298672986829869298702987129872298732987429875298762987729878298792988029881298822988329884298852988629887298882988929890298912989229893298942989529896298972989829899299002990129902299032990429905299062990729908299092991029911299122991329914299152991629917299182991929920299212992229923299242992529926299272992829929299302993129932299332993429935299362993729938299392994029941299422994329944299452994629947299482994929950299512995229953299542995529956299572995829959299602996129962299632996429965299662996729968299692997029971299722997329974299752997629977299782997929980299812998229983299842998529986299872998829989299902999129992299932999429995299962999729998299993000030001300023000330004300053000630007300083000930010300113001230013300143001530016300173001830019300203002130022300233002430025300263002730028300293003030031300323003330034300353003630037300383003930040300413004230043300443004530046300473004830049300503005130052300533005430055300563005730058300593006030061300623006330064300653006630067300683006930070300713007230073300743007530076300773007830079300803008130082300833008430085300863008730088300893009030091300923009330094300953009630097300983009930100301013010230103301043010530106301073010830109301103011130112301133011430115301163011730118301193012030121301223012330124301253012630127301283012930130301313013230133301343013530136301373013830139301403014130142301433014430145301463014730148301493015030151301523015330154301553015630157301583015930160301613016230163301643016530166301673016830169301703017130172301733017430175301763017730178301793018030181301823018330184301853018630187301883018930190301913019230193301943019530196301973019830199302003020130202302033020430205302063020730208302093021030211302123021330214302153021630217302183021930220302213022230223302243022530226302273022830229302303023130232302333023430235302363023730238302393024030241302423024330244302453024630247302483024930250302513025230253302543025530256302573025830259302603026130262302633026430265302663026730268302693027030271302723027330274302753027630277302783027930280302813028230283302843028530286302873028830289302903029130292302933029430295302963029730298302993030030301303023030330304303053030630307303083030930310303113031230313303143031530316303173031830319303203032130322303233032430325303263032730328303293033030331303323033330334303353033630337303383033930340303413034230343303443034530346303473034830349303503035130352303533035430355303563035730358303593036030361303623036330364303653036630367303683036930370303713037230373303743037530376303773037830379303803038130382303833038430385303863038730388303893039030391303923039330394303953039630397303983039930400304013040230403304043040530406304073040830409304103041130412304133041430415304163041730418304193042030421304223042330424304253042630427304283042930430304313043230433304343043530436304373043830439304403044130442304433044430445304463044730448304493045030451304523045330454304553045630457304583045930460304613046230463304643046530466304673046830469304703047130472304733047430475304763047730478304793048030481304823048330484304853048630487304883048930490304913049230493304943049530496304973049830499305003050130502305033050430505305063050730508305093051030511305123051330514305153051630517305183051930520305213052230523305243052530526305273052830529305303053130532305333053430535305363053730538305393054030541305423054330544305453054630547305483054930550305513055230553305543055530556305573055830559305603056130562305633056430565305663056730568305693057030571305723057330574305753057630577305783057930580305813058230583305843058530586305873058830589305903059130592305933059430595305963059730598305993060030601306023060330604306053060630607306083060930610306113061230613306143061530616306173061830619306203062130622306233062430625306263062730628306293063030631306323063330634306353063630637306383063930640306413064230643306443064530646306473064830649306503065130652306533065430655306563065730658306593066030661306623066330664306653066630667306683066930670306713067230673306743067530676306773067830679306803068130682306833068430685306863068730688306893069030691306923069330694306953069630697306983069930700307013070230703307043070530706307073070830709307103071130712307133071430715307163071730718307193072030721307223072330724307253072630727307283072930730307313073230733307343073530736307373073830739307403074130742307433074430745307463074730748307493075030751307523075330754307553075630757307583075930760307613076230763307643076530766307673076830769307703077130772307733077430775307763077730778307793078030781307823078330784307853078630787307883078930790307913079230793307943079530796307973079830799308003080130802308033080430805308063080730808308093081030811308123081330814308153081630817308183081930820308213082230823308243082530826308273082830829308303083130832308333083430835308363083730838308393084030841308423084330844308453084630847308483084930850308513085230853308543085530856308573085830859308603086130862308633086430865308663086730868308693087030871308723087330874308753087630877308783087930880308813088230883308843088530886308873088830889308903089130892308933089430895308963089730898308993090030901309023090330904309053090630907309083090930910309113091230913309143091530916309173091830919309203092130922309233092430925309263092730928309293093030931309323093330934309353093630937309383093930940309413094230943309443094530946309473094830949309503095130952309533095430955309563095730958309593096030961309623096330964309653096630967309683096930970309713097230973309743097530976309773097830979309803098130982309833098430985309863098730988309893099030991309923099330994309953099630997309983099931000310013100231003310043100531006310073100831009310103101131012310133101431015310163101731018310193102031021310223102331024310253102631027310283102931030310313103231033310343103531036310373103831039310403104131042310433104431045310463104731048310493105031051310523105331054310553105631057310583105931060310613106231063310643106531066310673106831069310703107131072310733107431075310763107731078310793108031081310823108331084310853108631087310883108931090310913109231093310943109531096310973109831099311003110131102311033110431105311063110731108311093111031111311123111331114311153111631117311183111931120311213112231123311243112531126311273112831129311303113131132311333113431135311363113731138311393114031141311423114331144311453114631147311483114931150311513115231153311543115531156311573115831159311603116131162311633116431165311663116731168311693117031171311723117331174311753117631177311783117931180311813118231183311843118531186311873118831189311903119131192311933119431195311963119731198311993120031201312023120331204312053120631207312083120931210312113121231213312143121531216312173121831219312203122131222312233122431225312263122731228312293123031231312323123331234312353123631237312383123931240312413124231243312443124531246312473124831249312503125131252312533125431255312563125731258312593126031261312623126331264312653126631267312683126931270312713127231273312743127531276312773127831279312803128131282312833128431285312863128731288312893129031291312923129331294312953129631297312983129931300313013130231303313043130531306313073130831309313103131131312313133131431315313163131731318313193132031321313223132331324313253132631327313283132931330313313133231333313343133531336313373133831339313403134131342313433134431345313463134731348313493135031351313523135331354313553135631357313583135931360313613136231363313643136531366313673136831369313703137131372313733137431375313763137731378313793138031381313823138331384313853138631387313883138931390313913139231393313943139531396313973139831399314003140131402314033140431405314063140731408314093141031411314123141331414314153141631417314183141931420314213142231423314243142531426314273142831429314303143131432314333143431435314363143731438314393144031441314423144331444314453144631447314483144931450314513145231453314543145531456314573145831459314603146131462314633146431465314663146731468314693147031471314723147331474314753147631477314783147931480314813148231483314843148531486314873148831489314903149131492314933149431495314963149731498314993150031501315023150331504315053150631507315083150931510315113151231513315143151531516315173151831519315203152131522315233152431525315263152731528315293153031531315323153331534315353153631537315383153931540315413154231543315443154531546315473154831549315503155131552315533155431555315563155731558315593156031561315623156331564315653156631567315683156931570315713157231573315743157531576315773157831579315803158131582315833158431585315863158731588315893159031591315923159331594315953159631597315983159931600316013160231603316043160531606316073160831609316103161131612316133161431615316163161731618316193162031621316223162331624316253162631627316283162931630316313163231633316343163531636316373163831639316403164131642316433164431645316463164731648316493165031651316523165331654316553165631657316583165931660316613166231663316643166531666316673166831669316703167131672316733167431675316763167731678316793168031681316823168331684316853168631687316883168931690316913169231693316943169531696316973169831699317003170131702317033170431705317063170731708317093171031711317123171331714317153171631717317183171931720317213172231723317243172531726317273172831729317303173131732317333173431735317363173731738317393174031741317423174331744317453174631747317483174931750317513175231753317543175531756317573175831759317603176131762317633176431765317663176731768317693177031771317723177331774317753177631777317783177931780317813178231783317843178531786317873178831789317903179131792317933179431795317963179731798317993180031801318023180331804318053180631807318083180931810318113181231813318143181531816318173181831819318203182131822318233182431825318263182731828318293183031831318323183331834318353183631837318383183931840318413184231843318443184531846318473184831849318503185131852318533185431855318563185731858318593186031861318623186331864318653186631867318683186931870318713187231873318743187531876318773187831879318803188131882318833188431885318863188731888318893189031891318923189331894318953189631897318983189931900319013190231903319043190531906319073190831909319103191131912319133191431915319163191731918319193192031921319223192331924319253192631927319283192931930319313193231933319343193531936319373193831939319403194131942319433194431945319463194731948319493195031951319523195331954319553195631957319583195931960319613196231963319643196531966319673196831969319703197131972319733197431975319763197731978319793198031981319823198331984319853198631987319883198931990319913199231993319943199531996319973199831999320003200132002320033200432005320063200732008320093201032011320123201332014320153201632017320183201932020320213202232023320243202532026320273202832029320303203132032320333203432035320363203732038320393204032041320423204332044320453204632047320483204932050320513205232053320543205532056320573205832059320603206132062320633206432065320663206732068320693207032071320723207332074320753207632077320783207932080320813208232083320843208532086320873208832089320903209132092320933209432095320963209732098320993210032101321023210332104321053210632107321083210932110321113211232113321143211532116321173211832119321203212132122321233212432125321263212732128321293213032131321323213332134321353213632137321383213932140321413214232143321443214532146321473214832149321503215132152321533215432155321563215732158321593216032161321623216332164321653216632167321683216932170321713217232173321743217532176321773217832179321803218132182321833218432185321863218732188321893219032191321923219332194321953219632197321983219932200322013220232203322043220532206322073220832209322103221132212322133221432215322163221732218322193222032221322223222332224322253222632227322283222932230322313223232233322343223532236322373223832239322403224132242322433224432245322463224732248322493225032251322523225332254322553225632257322583225932260322613226232263322643226532266322673226832269322703227132272322733227432275322763227732278322793228032281322823228332284322853228632287322883228932290322913229232293322943229532296322973229832299323003230132302323033230432305323063230732308323093231032311323123231332314323153231632317323183231932320323213232232323323243232532326323273232832329323303233132332323333233432335323363233732338323393234032341323423234332344323453234632347323483234932350323513235232353323543235532356323573235832359323603236132362323633236432365323663236732368323693237032371323723237332374323753237632377323783237932380323813238232383323843238532386323873238832389323903239132392323933239432395323963239732398323993240032401324023240332404324053240632407324083240932410324113241232413324143241532416324173241832419324203242132422324233242432425324263242732428324293243032431324323243332434324353243632437324383243932440324413244232443324443244532446324473244832449324503245132452324533245432455324563245732458324593246032461324623246332464324653246632467324683246932470324713247232473324743247532476324773247832479324803248132482324833248432485324863248732488324893249032491324923249332494324953249632497324983249932500325013250232503325043250532506325073250832509325103251132512325133251432515325163251732518325193252032521325223252332524325253252632527325283252932530325313253232533325343253532536325373253832539325403254132542325433254432545325463254732548325493255032551325523255332554325553255632557325583255932560325613256232563325643256532566325673256832569325703257132572325733257432575325763257732578325793258032581325823258332584325853258632587325883258932590325913259232593325943259532596325973259832599326003260132602326033260432605326063260732608326093261032611326123261332614326153261632617326183261932620326213262232623326243262532626326273262832629326303263132632326333263432635326363263732638326393264032641326423264332644326453264632647326483264932650326513265232653326543265532656326573265832659326603266132662326633266432665326663266732668326693267032671326723267332674326753267632677326783267932680326813268232683326843268532686326873268832689326903269132692326933269432695326963269732698326993270032701327023270332704327053270632707327083270932710327113271232713327143271532716327173271832719327203272132722327233272432725327263272732728327293273032731327323273332734327353273632737327383273932740327413274232743327443274532746327473274832749327503275132752327533275432755327563275732758327593276032761327623276332764327653276632767327683276932770327713277232773327743277532776327773277832779327803278132782327833278432785327863278732788327893279032791327923279332794327953279632797327983279932800328013280232803328043280532806328073280832809328103281132812328133281432815328163281732818328193282032821328223282332824328253282632827328283282932830328313283232833328343283532836328373283832839328403284132842328433284432845328463284732848328493285032851328523285332854328553285632857328583285932860328613286232863328643286532866328673286832869328703287132872328733287432875328763287732878328793288032881328823288332884328853288632887328883288932890328913289232893328943289532896328973289832899329003290132902329033290432905329063290732908329093291032911329123291332914329153291632917329183291932920329213292232923329243292532926329273292832929329303293132932329333293432935329363293732938329393294032941329423294332944329453294632947329483294932950329513295232953329543295532956329573295832959329603296132962329633296432965329663296732968329693297032971329723297332974329753297632977329783297932980329813298232983329843298532986329873298832989329903299132992329933299432995329963299732998329993300033001330023300333004330053300633007330083300933010330113301233013330143301533016330173301833019330203302133022330233302433025330263302733028330293303033031330323303333034330353303633037330383303933040330413304233043330443304533046330473304833049330503305133052330533305433055330563305733058330593306033061330623306333064330653306633067330683306933070330713307233073330743307533076330773307833079330803308133082330833308433085330863308733088330893309033091330923309333094330953309633097330983309933100331013310233103331043310533106331073310833109331103311133112331133311433115331163311733118331193312033121331223312333124331253312633127331283312933130331313313233133331343313533136331373313833139331403314133142331433314433145331463314733148331493315033151331523315333154331553315633157331583315933160331613316233163331643316533166331673316833169331703317133172331733317433175331763317733178331793318033181331823318333184331853318633187331883318933190331913319233193331943319533196331973319833199332003320133202332033320433205332063320733208332093321033211332123321333214332153321633217332183321933220332213322233223332243322533226332273322833229332303323133232332333323433235332363323733238332393324033241332423324333244332453324633247332483324933250332513325233253332543325533256332573325833259332603326133262332633326433265332663326733268332693327033271332723327333274332753327633277332783327933280332813328233283332843328533286332873328833289332903329133292332933329433295332963329733298332993330033301333023330333304333053330633307333083330933310333113331233313333143331533316333173331833319333203332133322333233332433325333263332733328333293333033331333323333333334333353333633337333383333933340333413334233343333443334533346333473334833349333503335133352333533335433355333563335733358333593336033361333623336333364333653336633367333683336933370333713337233373333743337533376333773337833379333803338133382333833338433385333863338733388333893339033391333923339333394333953339633397333983339933400334013340233403334043340533406334073340833409334103341133412334133341433415334163341733418334193342033421334223342333424334253342633427334283342933430334313343233433334343343533436334373343833439334403344133442334433344433445334463344733448334493345033451334523345333454334553345633457334583345933460334613346233463334643346533466334673346833469334703347133472334733347433475334763347733478334793348033481334823348333484334853348633487334883348933490334913349233493334943349533496334973349833499335003350133502335033350433505335063350733508335093351033511335123351333514335153351633517335183351933520335213352233523335243352533526335273352833529335303353133532335333353433535335363353733538335393354033541335423354333544335453354633547335483354933550335513355233553335543355533556335573355833559335603356133562335633356433565335663356733568335693357033571335723357333574335753357633577335783357933580335813358233583335843358533586335873358833589335903359133592335933359433595335963359733598335993360033601336023360333604336053360633607336083360933610336113361233613336143361533616336173361833619336203362133622336233362433625336263362733628336293363033631336323363333634336353363633637336383363933640336413364233643336443364533646336473364833649336503365133652336533365433655336563365733658336593366033661336623366333664336653366633667336683366933670336713367233673336743367533676336773367833679336803368133682336833368433685336863368733688336893369033691336923369333694336953369633697336983369933700337013370233703337043370533706337073370833709337103371133712337133371433715337163371733718337193372033721337223372333724337253372633727337283372933730337313373233733337343373533736337373373833739337403374133742337433374433745337463374733748337493375033751337523375333754337553375633757337583375933760337613376233763337643376533766337673376833769337703377133772337733377433775337763377733778337793378033781337823378333784337853378633787337883378933790337913379233793337943379533796337973379833799338003380133802338033380433805338063380733808338093381033811338123381333814338153381633817338183381933820338213382233823338243382533826338273382833829338303383133832338333383433835338363383733838338393384033841338423384333844338453384633847338483384933850338513385233853338543385533856338573385833859338603386133862338633386433865338663386733868338693387033871338723387333874338753387633877338783387933880338813388233883338843388533886338873388833889338903389133892338933389433895338963389733898338993390033901339023390333904339053390633907339083390933910339113391233913339143391533916339173391833919339203392133922339233392433925339263392733928339293393033931339323393333934339353393633937339383393933940339413394233943339443394533946339473394833949339503395133952339533395433955339563395733958339593396033961339623396333964339653396633967339683396933970339713397233973339743397533976339773397833979339803398133982339833398433985339863398733988339893399033991339923399333994339953399633997339983399934000340013400234003340043400534006340073400834009340103401134012340133401434015340163401734018340193402034021340223402334024340253402634027340283402934030340313403234033340343403534036340373403834039340403404134042340433404434045340463404734048340493405034051340523405334054340553405634057340583405934060340613406234063340643406534066340673406834069340703407134072340733407434075340763407734078340793408034081340823408334084340853408634087340883408934090340913409234093340943409534096340973409834099341003410134102341033410434105341063410734108341093411034111341123411334114341153411634117341183411934120341213412234123341243412534126341273412834129341303413134132341333413434135341363413734138341393414034141341423414334144341453414634147341483414934150341513415234153341543415534156341573415834159341603416134162341633416434165341663416734168341693417034171341723417334174341753417634177341783417934180341813418234183341843418534186341873418834189341903419134192341933419434195341963419734198341993420034201342023420334204342053420634207342083420934210342113421234213342143421534216342173421834219342203422134222342233422434225342263422734228342293423034231342323423334234342353423634237342383423934240342413424234243342443424534246342473424834249342503425134252342533425434255342563425734258342593426034261342623426334264342653426634267342683426934270342713427234273342743427534276342773427834279342803428134282342833428434285342863428734288342893429034291342923429334294342953429634297342983429934300343013430234303343043430534306343073430834309343103431134312343133431434315343163431734318343193432034321343223432334324343253432634327343283432934330343313433234333343343433534336343373433834339343403434134342343433434434345343463434734348343493435034351343523435334354343553435634357343583435934360343613436234363343643436534366343673436834369343703437134372343733437434375343763437734378343793438034381343823438334384343853438634387343883438934390343913439234393343943439534396343973439834399344003440134402344033440434405344063440734408344093441034411344123441334414344153441634417344183441934420344213442234423344243442534426344273442834429344303443134432344333443434435344363443734438344393444034441344423444334444344453444634447344483444934450344513445234453344543445534456344573445834459344603446134462344633446434465344663446734468344693447034471344723447334474344753447634477344783447934480344813448234483344843448534486344873448834489344903449134492344933449434495344963449734498344993450034501345023450334504345053450634507345083450934510345113451234513345143451534516345173451834519345203452134522345233452434525345263452734528345293453034531345323453334534345353453634537345383453934540345413454234543345443454534546345473454834549345503455134552345533455434555345563455734558345593456034561345623456334564345653456634567345683456934570345713457234573345743457534576345773457834579345803458134582345833458434585345863458734588345893459034591345923459334594345953459634597345983459934600346013460234603346043460534606346073460834609346103461134612346133461434615346163461734618346193462034621346223462334624346253462634627346283462934630346313463234633346343463534636346373463834639346403464134642346433464434645346463464734648346493465034651346523465334654346553465634657346583465934660346613466234663346643466534666346673466834669346703467134672346733467434675346763467734678346793468034681346823468334684346853468634687346883468934690346913469234693346943469534696346973469834699347003470134702347033470434705347063470734708347093471034711347123471334714347153471634717347183471934720347213472234723347243472534726347273472834729347303473134732347333473434735347363473734738347393474034741347423474334744347453474634747347483474934750347513475234753347543475534756347573475834759347603476134762347633476434765347663476734768347693477034771347723477334774347753477634777347783477934780347813478234783347843478534786347873478834789347903479134792347933479434795347963479734798347993480034801348023480334804348053480634807348083480934810348113481234813348143481534816348173481834819348203482134822348233482434825348263482734828348293483034831348323483334834348353483634837348383483934840348413484234843348443484534846348473484834849348503485134852348533485434855348563485734858348593486034861348623486334864348653486634867348683486934870348713487234873348743487534876348773487834879348803488134882348833488434885348863488734888348893489034891348923489334894348953489634897348983489934900349013490234903349043490534906349073490834909349103491134912349133491434915349163491734918349193492034921349223492334924349253492634927349283492934930349313493234933349343493534936349373493834939349403494134942349433494434945349463494734948349493495034951349523495334954349553495634957349583495934960349613496234963349643496534966349673496834969349703497134972349733497434975349763497734978349793498034981349823498334984349853498634987349883498934990349913499234993349943499534996349973499834999350003500135002350033500435005350063500735008350093501035011350123501335014350153501635017350183501935020350213502235023350243502535026350273502835029350303503135032350333503435035350363503735038350393504035041350423504335044350453504635047350483504935050350513505235053350543505535056350573505835059350603506135062350633506435065350663506735068350693507035071350723507335074350753507635077350783507935080350813508235083350843508535086350873508835089350903509135092350933509435095350963509735098350993510035101351023510335104351053510635107351083510935110351113511235113351143511535116351173511835119351203512135122351233512435125351263512735128351293513035131351323513335134351353513635137351383513935140351413514235143351443514535146351473514835149351503515135152351533515435155351563515735158351593516035161351623516335164351653516635167351683516935170351713517235173351743517535176351773517835179351803518135182351833518435185351863518735188351893519035191351923519335194351953519635197351983519935200352013520235203352043520535206352073520835209352103521135212352133521435215352163521735218352193522035221352223522335224352253522635227352283522935230352313523235233352343523535236352373523835239352403524135242352433524435245352463524735248352493525035251352523525335254352553525635257352583525935260352613526235263352643526535266352673526835269352703527135272352733527435275352763527735278352793528035281352823528335284352853528635287352883528935290352913529235293352943529535296352973529835299353003530135302353033530435305353063530735308353093531035311353123531335314353153531635317353183531935320353213532235323353243532535326353273532835329353303533135332353333533435335353363533735338353393534035341353423534335344353453534635347353483534935350353513535235353353543535535356353573535835359353603536135362353633536435365353663536735368353693537035371353723537335374353753537635377353783537935380353813538235383353843538535386353873538835389353903539135392353933539435395353963539735398353993540035401354023540335404354053540635407354083540935410354113541235413354143541535416354173541835419354203542135422354233542435425354263542735428354293543035431354323543335434354353543635437354383543935440354413544235443354443544535446354473544835449354503545135452354533545435455354563545735458354593546035461354623546335464354653546635467354683546935470354713547235473354743547535476354773547835479354803548135482354833548435485354863548735488354893549035491354923549335494354953549635497354983549935500355013550235503355043550535506355073550835509355103551135512355133551435515355163551735518355193552035521355223552335524355253552635527355283552935530355313553235533355343553535536355373553835539355403554135542355433554435545355463554735548355493555035551355523555335554355553555635557355583555935560355613556235563355643556535566355673556835569355703557135572355733557435575355763557735578355793558035581355823558335584355853558635587355883558935590355913559235593355943559535596355973559835599356003560135602356033560435605356063560735608356093561035611356123561335614356153561635617356183561935620356213562235623356243562535626356273562835629356303563135632356333563435635356363563735638356393564035641356423564335644356453564635647356483564935650356513565235653356543565535656356573565835659356603566135662356633566435665356663566735668356693567035671356723567335674356753567635677356783567935680356813568235683356843568535686356873568835689356903569135692356933569435695356963569735698356993570035701357023570335704357053570635707357083570935710357113571235713357143571535716357173571835719357203572135722357233572435725357263572735728357293573035731357323573335734357353573635737357383573935740357413574235743357443574535746357473574835749357503575135752357533575435755357563575735758357593576035761357623576335764357653576635767357683576935770357713577235773357743577535776357773577835779357803578135782357833578435785357863578735788357893579035791357923579335794357953579635797357983579935800358013580235803358043580535806358073580835809358103581135812358133581435815358163581735818358193582035821358223582335824358253582635827358283582935830358313583235833358343583535836358373583835839358403584135842358433584435845358463584735848358493585035851358523585335854358553585635857358583585935860358613586235863358643586535866358673586835869358703587135872358733587435875358763587735878358793588035881358823588335884358853588635887358883588935890358913589235893358943589535896358973589835899359003590135902359033590435905359063590735908359093591035911359123591335914359153591635917359183591935920359213592235923359243592535926359273592835929359303593135932359333593435935359363593735938359393594035941359423594335944359453594635947359483594935950359513595235953359543595535956359573595835959359603596135962359633596435965359663596735968359693597035971359723597335974359753597635977359783597935980359813598235983359843598535986359873598835989359903599135992359933599435995359963599735998359993600036001360023600336004360053600636007360083600936010360113601236013360143601536016360173601836019360203602136022360233602436025360263602736028360293603036031360323603336034360353603636037360383603936040360413604236043360443604536046360473604836049360503605136052360533605436055360563605736058360593606036061360623606336064360653606636067360683606936070360713607236073360743607536076360773607836079360803608136082360833608436085360863608736088360893609036091360923609336094360953609636097360983609936100361013610236103361043610536106361073610836109361103611136112361133611436115361163611736118361193612036121361223612336124361253612636127361283612936130361313613236133361343613536136361373613836139361403614136142361433614436145361463614736148361493615036151361523615336154361553615636157361583615936160361613616236163361643616536166361673616836169361703617136172361733617436175361763617736178361793618036181361823618336184361853618636187361883618936190361913619236193361943619536196361973619836199362003620136202362033620436205362063620736208362093621036211362123621336214362153621636217362183621936220362213622236223362243622536226362273622836229362303623136232362333623436235362363623736238362393624036241362423624336244362453624636247362483624936250362513625236253362543625536256362573625836259362603626136262362633626436265362663626736268362693627036271362723627336274362753627636277362783627936280362813628236283362843628536286362873628836289362903629136292362933629436295362963629736298362993630036301363023630336304363053630636307363083630936310363113631236313363143631536316363173631836319363203632136322363233632436325363263632736328363293633036331363323633336334363353633636337363383633936340363413634236343363443634536346363473634836349363503635136352363533635436355363563635736358363593636036361363623636336364363653636636367363683636936370363713637236373363743637536376363773637836379363803638136382363833638436385363863638736388363893639036391363923639336394363953639636397363983639936400364013640236403364043640536406364073640836409364103641136412364133641436415364163641736418364193642036421364223642336424364253642636427364283642936430364313643236433364343643536436364373643836439364403644136442364433644436445364463644736448364493645036451364523645336454364553645636457364583645936460364613646236463364643646536466364673646836469364703647136472364733647436475364763647736478364793648036481364823648336484364853648636487364883648936490364913649236493364943649536496364973649836499365003650136502365033650436505365063650736508365093651036511365123651336514365153651636517365183651936520365213652236523365243652536526365273652836529365303653136532365333653436535365363653736538365393654036541365423654336544365453654636547365483654936550365513655236553365543655536556365573655836559365603656136562365633656436565365663656736568365693657036571365723657336574365753657636577365783657936580365813658236583365843658536586365873658836589365903659136592365933659436595365963659736598365993660036601366023660336604366053660636607366083660936610366113661236613366143661536616366173661836619366203662136622366233662436625366263662736628366293663036631366323663336634366353663636637366383663936640366413664236643366443664536646366473664836649366503665136652366533665436655366563665736658366593666036661366623666336664366653666636667366683666936670366713667236673366743667536676366773667836679366803668136682366833668436685366863668736688366893669036691366923669336694366953669636697366983669936700367013670236703367043670536706367073670836709367103671136712367133671436715367163671736718367193672036721367223672336724367253672636727367283672936730367313673236733367343673536736367373673836739367403674136742367433674436745367463674736748367493675036751367523675336754367553675636757367583675936760367613676236763367643676536766367673676836769367703677136772367733677436775367763677736778367793678036781367823678336784367853678636787367883678936790367913679236793367943679536796367973679836799368003680136802368033680436805368063680736808368093681036811368123681336814368153681636817368183681936820368213682236823368243682536826368273682836829368303683136832368333683436835368363683736838368393684036841368423684336844368453684636847368483684936850368513685236853368543685536856368573685836859368603686136862368633686436865368663686736868368693687036871368723687336874368753687636877368783687936880368813688236883368843688536886368873688836889368903689136892368933689436895368963689736898368993690036901369023690336904369053690636907369083690936910369113691236913369143691536916369173691836919369203692136922369233692436925369263692736928369293693036931369323693336934369353693636937369383693936940369413694236943369443694536946369473694836949369503695136952369533695436955369563695736958369593696036961369623696336964369653696636967369683696936970369713697236973369743697536976369773697836979369803698136982369833698436985369863698736988369893699036991369923699336994369953699636997369983699937000370013700237003370043700537006370073700837009370103701137012370133701437015370163701737018370193702037021370223702337024370253702637027370283702937030370313703237033370343703537036370373703837039370403704137042370433704437045370463704737048370493705037051370523705337054370553705637057370583705937060370613706237063370643706537066370673706837069370703707137072370733707437075370763707737078370793708037081370823708337084370853708637087370883708937090370913709237093370943709537096370973709837099371003710137102371033710437105371063710737108371093711037111371123711337114371153711637117371183711937120371213712237123371243712537126371273712837129371303713137132371333713437135371363713737138371393714037141371423714337144371453714637147371483714937150371513715237153371543715537156371573715837159371603716137162371633716437165371663716737168371693717037171371723717337174371753717637177371783717937180371813718237183371843718537186371873718837189371903719137192371933719437195371963719737198371993720037201372023720337204372053720637207372083720937210372113721237213372143721537216372173721837219372203722137222372233722437225372263722737228372293723037231372323723337234372353723637237372383723937240372413724237243372443724537246372473724837249372503725137252372533725437255372563725737258372593726037261372623726337264372653726637267372683726937270372713727237273372743727537276372773727837279372803728137282372833728437285372863728737288372893729037291372923729337294372953729637297372983729937300373013730237303373043730537306373073730837309373103731137312373133731437315373163731737318373193732037321373223732337324373253732637327373283732937330373313733237333373343733537336373373733837339373403734137342373433734437345373463734737348373493735037351373523735337354373553735637357373583735937360373613736237363373643736537366373673736837369373703737137372373733737437375373763737737378373793738037381373823738337384373853738637387373883738937390373913739237393373943739537396373973739837399374003740137402374033740437405374063740737408374093741037411374123741337414374153741637417374183741937420374213742237423374243742537426374273742837429374303743137432374333743437435374363743737438374393744037441374423744337444374453744637447374483744937450374513745237453374543745537456374573745837459374603746137462374633746437465374663746737468374693747037471374723747337474374753747637477374783747937480374813748237483374843748537486374873748837489374903749137492374933749437495374963749737498374993750037501375023750337504375053750637507375083750937510375113751237513375143751537516375173751837519375203752137522375233752437525375263752737528375293753037531375323753337534375353753637537375383753937540375413754237543375443754537546375473754837549375503755137552375533755437555375563755737558375593756037561375623756337564375653756637567375683756937570375713757237573375743757537576375773757837579375803758137582375833758437585375863758737588375893759037591375923759337594375953759637597375983759937600376013760237603376043760537606376073760837609376103761137612376133761437615376163761737618376193762037621376223762337624376253762637627376283762937630376313763237633376343763537636376373763837639376403764137642376433764437645376463764737648376493765037651376523765337654376553765637657376583765937660376613766237663376643766537666376673766837669376703767137672376733767437675376763767737678376793768037681376823768337684376853768637687376883768937690376913769237693376943769537696376973769837699377003770137702377033770437705377063770737708377093771037711377123771337714377153771637717377183771937720377213772237723377243772537726377273772837729377303773137732377333773437735377363773737738377393774037741377423774337744377453774637747377483774937750377513775237753377543775537756377573775837759377603776137762377633776437765377663776737768377693777037771377723777337774377753777637777377783777937780377813778237783377843778537786377873778837789377903779137792377933779437795377963779737798377993780037801378023780337804378053780637807378083780937810378113781237813378143781537816378173781837819378203782137822378233782437825378263782737828378293783037831378323783337834378353783637837378383783937840378413784237843378443784537846378473784837849378503785137852378533785437855378563785737858378593786037861378623786337864378653786637867378683786937870378713787237873378743787537876378773787837879378803788137882378833788437885378863788737888378893789037891378923789337894378953789637897378983789937900379013790237903379043790537906379073790837909379103791137912379133791437915379163791737918379193792037921379223792337924379253792637927379283792937930379313793237933379343793537936379373793837939379403794137942379433794437945379463794737948379493795037951379523795337954379553795637957379583795937960379613796237963379643796537966379673796837969379703797137972379733797437975379763797737978379793798037981379823798337984379853798637987379883798937990379913799237993379943799537996379973799837999380003800138002380033800438005380063800738008380093801038011380123801338014380153801638017380183801938020380213802238023380243802538026380273802838029380303803138032380333803438035380363803738038380393804038041380423804338044380453804638047380483804938050380513805238053380543805538056380573805838059380603806138062380633806438065380663806738068380693807038071380723807338074380753807638077380783807938080380813808238083380843808538086380873808838089380903809138092380933809438095380963809738098380993810038101381023810338104381053810638107381083810938110381113811238113381143811538116381173811838119381203812138122381233812438125381263812738128381293813038131381323813338134381353813638137381383813938140381413814238143381443814538146381473814838149381503815138152381533815438155381563815738158381593816038161381623816338164381653816638167381683816938170381713817238173381743817538176381773817838179381803818138182381833818438185381863818738188381893819038191381923819338194381953819638197381983819938200382013820238203382043820538206382073820838209382103821138212382133821438215382163821738218382193822038221382223822338224382253822638227382283822938230382313823238233382343823538236382373823838239382403824138242382433824438245382463824738248382493825038251382523825338254382553825638257382583825938260382613826238263382643826538266382673826838269382703827138272382733827438275382763827738278382793828038281382823828338284382853828638287382883828938290382913829238293382943829538296382973829838299383003830138302383033830438305383063830738308383093831038311383123831338314383153831638317383183831938320383213832238323383243832538326383273832838329383303833138332383333833438335383363833738338383393834038341383423834338344383453834638347383483834938350383513835238353383543835538356383573835838359383603836138362383633836438365383663836738368383693837038371383723837338374383753837638377383783837938380383813838238383383843838538386383873838838389383903839138392383933839438395383963839738398383993840038401384023840338404384053840638407384083840938410384113841238413384143841538416384173841838419384203842138422384233842438425384263842738428384293843038431384323843338434384353843638437384383843938440384413844238443384443844538446384473844838449384503845138452384533845438455384563845738458384593846038461384623846338464384653846638467384683846938470384713847238473384743847538476384773847838479384803848138482384833848438485384863848738488384893849038491384923849338494384953849638497384983849938500385013850238503385043850538506385073850838509385103851138512385133851438515385163851738518385193852038521385223852338524385253852638527385283852938530385313853238533385343853538536385373853838539385403854138542385433854438545385463854738548385493855038551385523855338554385553855638557385583855938560385613856238563385643856538566385673856838569385703857138572385733857438575385763857738578385793858038581385823858338584385853858638587385883858938590385913859238593385943859538596385973859838599386003860138602386033860438605386063860738608386093861038611386123861338614386153861638617386183861938620386213862238623386243862538626386273862838629386303863138632386333863438635386363863738638386393864038641386423864338644386453864638647386483864938650386513865238653386543865538656386573865838659386603866138662386633866438665386663866738668386693867038671386723867338674386753867638677386783867938680386813868238683386843868538686386873868838689386903869138692386933869438695386963869738698386993870038701387023870338704387053870638707387083870938710387113871238713387143871538716387173871838719387203872138722387233872438725387263872738728387293873038731387323873338734387353873638737387383873938740387413874238743387443874538746387473874838749387503875138752387533875438755387563875738758387593876038761387623876338764387653876638767387683876938770387713877238773387743877538776387773877838779387803878138782387833878438785387863878738788387893879038791387923879338794387953879638797387983879938800388013880238803388043880538806388073880838809388103881138812388133881438815388163881738818388193882038821388223882338824388253882638827388283882938830388313883238833388343883538836388373883838839388403884138842388433884438845388463884738848388493885038851388523885338854388553885638857388583885938860388613886238863388643886538866388673886838869388703887138872388733887438875388763887738878388793888038881388823888338884388853888638887388883888938890388913889238893388943889538896388973889838899389003890138902389033890438905389063890738908389093891038911389123891338914389153891638917389183891938920389213892238923389243892538926389273892838929389303893138932389333893438935389363893738938389393894038941389423894338944389453894638947389483894938950389513895238953389543895538956389573895838959389603896138962389633896438965389663896738968389693897038971389723897338974389753897638977389783897938980389813898238983389843898538986389873898838989389903899138992389933899438995389963899738998389993900039001390023900339004390053900639007390083900939010390113901239013390143901539016390173901839019390203902139022390233902439025390263902739028390293903039031390323903339034390353903639037390383903939040390413904239043390443904539046390473904839049390503905139052390533905439055390563905739058390593906039061390623906339064390653906639067390683906939070390713907239073390743907539076390773907839079390803908139082390833908439085390863908739088390893909039091390923909339094390953909639097390983909939100391013910239103391043910539106391073910839109391103911139112391133911439115391163911739118391193912039121391223912339124391253912639127391283912939130391313913239133391343913539136391373913839139391403914139142391433914439145391463914739148391493915039151391523915339154391553915639157391583915939160391613916239163391643916539166391673916839169391703917139172391733917439175391763917739178391793918039181391823918339184391853918639187391883918939190391913919239193391943919539196391973919839199392003920139202392033920439205392063920739208392093921039211392123921339214392153921639217392183921939220392213922239223392243922539226392273922839229392303923139232392333923439235392363923739238392393924039241392423924339244392453924639247392483924939250392513925239253392543925539256392573925839259392603926139262392633926439265392663926739268392693927039271392723927339274392753927639277392783927939280392813928239283392843928539286392873928839289392903929139292392933929439295392963929739298392993930039301393023930339304393053930639307393083930939310393113931239313393143931539316393173931839319393203932139322393233932439325393263932739328393293933039331393323933339334393353933639337393383933939340393413934239343393443934539346393473934839349393503935139352393533935439355393563935739358393593936039361393623936339364393653936639367393683936939370393713937239373393743937539376393773937839379393803938139382393833938439385393863938739388393893939039391393923939339394393953939639397393983939939400394013940239403394043940539406394073940839409394103941139412394133941439415394163941739418394193942039421394223942339424394253942639427394283942939430394313943239433394343943539436394373943839439394403944139442394433944439445394463944739448394493945039451394523945339454394553945639457394583945939460394613946239463394643946539466394673946839469394703947139472394733947439475394763947739478394793948039481394823948339484394853948639487394883948939490394913949239493394943949539496394973949839499395003950139502395033950439505395063950739508395093951039511395123951339514395153951639517395183951939520395213952239523395243952539526395273952839529395303953139532395333953439535395363953739538395393954039541395423954339544395453954639547395483954939550395513955239553395543955539556395573955839559395603956139562395633956439565395663956739568395693957039571395723957339574395753957639577395783957939580395813958239583395843958539586395873958839589395903959139592395933959439595395963959739598395993960039601396023960339604396053960639607396083960939610396113961239613396143961539616396173961839619396203962139622396233962439625396263962739628396293963039631396323963339634396353963639637396383963939640396413964239643396443964539646396473964839649396503965139652396533965439655396563965739658396593966039661396623966339664396653966639667396683966939670396713967239673396743967539676396773967839679396803968139682396833968439685396863968739688396893969039691396923969339694396953969639697396983969939700397013970239703397043970539706397073970839709397103971139712397133971439715397163971739718397193972039721397223972339724397253972639727397283972939730397313973239733397343973539736397373973839739397403974139742397433974439745397463974739748397493975039751397523975339754397553975639757397583975939760397613976239763397643976539766397673976839769397703977139772397733977439775397763977739778397793978039781397823978339784397853978639787397883978939790397913979239793397943979539796397973979839799398003980139802398033980439805398063980739808398093981039811398123981339814398153981639817398183981939820398213982239823398243982539826398273982839829398303983139832398333983439835398363983739838398393984039841398423984339844398453984639847398483984939850398513985239853398543985539856398573985839859398603986139862398633986439865398663986739868398693987039871398723987339874398753987639877398783987939880398813988239883398843988539886398873988839889398903989139892398933989439895398963989739898398993990039901399023990339904399053990639907399083990939910399113991239913399143991539916399173991839919399203992139922399233992439925399263992739928399293993039931399323993339934399353993639937399383993939940399413994239943399443994539946399473994839949399503995139952399533995439955399563995739958399593996039961399623996339964399653996639967399683996939970399713997239973399743997539976399773997839979399803998139982399833998439985399863998739988399893999039991399923999339994399953999639997399983999940000400014000240003400044000540006400074000840009400104001140012400134001440015400164001740018400194002040021400224002340024400254002640027400284002940030400314003240033400344003540036400374003840039400404004140042400434004440045400464004740048400494005040051400524005340054400554005640057400584005940060400614006240063400644006540066400674006840069400704007140072400734007440075400764007740078400794008040081400824008340084400854008640087400884008940090400914009240093400944009540096400974009840099401004010140102401034010440105401064010740108401094011040111401124011340114401154011640117401184011940120401214012240123401244012540126401274012840129401304013140132401334013440135401364013740138401394014040141401424014340144401454014640147401484014940150401514015240153401544015540156401574015840159401604016140162401634016440165401664016740168401694017040171401724017340174401754017640177401784017940180401814018240183401844018540186401874018840189401904019140192401934019440195401964019740198401994020040201402024020340204402054020640207402084020940210402114021240213402144021540216402174021840219402204022140222402234022440225402264022740228402294023040231402324023340234402354023640237402384023940240402414024240243402444024540246402474024840249402504025140252402534025440255402564025740258402594026040261402624026340264402654026640267402684026940270402714027240273402744027540276402774027840279402804028140282402834028440285402864028740288402894029040291402924029340294402954029640297402984029940300403014030240303403044030540306403074030840309403104031140312403134031440315403164031740318403194032040321403224032340324403254032640327403284032940330403314033240333403344033540336403374033840339403404034140342403434034440345403464034740348403494035040351403524035340354403554035640357403584035940360403614036240363403644036540366403674036840369403704037140372403734037440375403764037740378403794038040381403824038340384403854038640387403884038940390403914039240393403944039540396403974039840399404004040140402404034040440405404064040740408404094041040411404124041340414404154041640417404184041940420404214042240423404244042540426404274042840429404304043140432404334043440435404364043740438404394044040441404424044340444404454044640447404484044940450404514045240453404544045540456404574045840459404604046140462404634046440465404664046740468404694047040471404724047340474404754047640477404784047940480404814048240483404844048540486404874048840489404904049140492404934049440495404964049740498404994050040501405024050340504405054050640507405084050940510405114051240513405144051540516405174051840519405204052140522405234052440525405264052740528405294053040531405324053340534405354053640537405384053940540405414054240543405444054540546405474054840549405504055140552405534055440555405564055740558405594056040561405624056340564405654056640567405684056940570405714057240573405744057540576405774057840579405804058140582405834058440585405864058740588405894059040591405924059340594405954059640597405984059940600406014060240603406044060540606406074060840609406104061140612406134061440615406164061740618406194062040621406224062340624406254062640627406284062940630406314063240633406344063540636406374063840639406404064140642406434064440645406464064740648406494065040651406524065340654406554065640657406584065940660406614066240663406644066540666406674066840669406704067140672406734067440675406764067740678406794068040681406824068340684406854068640687406884068940690406914069240693406944069540696406974069840699407004070140702407034070440705407064070740708407094071040711407124071340714407154071640717407184071940720407214072240723407244072540726407274072840729407304073140732407334073440735407364073740738407394074040741407424074340744407454074640747407484074940750407514075240753407544075540756407574075840759407604076140762407634076440765407664076740768407694077040771407724077340774407754077640777407784077940780407814078240783407844078540786407874078840789407904079140792407934079440795407964079740798407994080040801408024080340804408054080640807408084080940810408114081240813408144081540816408174081840819408204082140822408234082440825408264082740828408294083040831408324083340834408354083640837408384083940840408414084240843408444084540846408474084840849408504085140852408534085440855408564085740858408594086040861408624086340864408654086640867408684086940870408714087240873408744087540876408774087840879408804088140882408834088440885408864088740888408894089040891408924089340894408954089640897408984089940900409014090240903409044090540906409074090840909409104091140912409134091440915409164091740918409194092040921409224092340924409254092640927409284092940930409314093240933409344093540936409374093840939409404094140942409434094440945409464094740948409494095040951409524095340954409554095640957409584095940960409614096240963409644096540966409674096840969409704097140972409734097440975409764097740978409794098040981409824098340984409854098640987409884098940990409914099240993409944099540996409974099840999410004100141002410034100441005410064100741008410094101041011410124101341014410154101641017410184101941020410214102241023410244102541026410274102841029410304103141032410334103441035410364103741038410394104041041410424104341044410454104641047410484104941050410514105241053410544105541056410574105841059410604106141062410634106441065410664106741068410694107041071410724107341074410754107641077410784107941080410814108241083410844108541086410874108841089410904109141092410934109441095410964109741098410994110041101411024110341104411054110641107411084110941110411114111241113411144111541116411174111841119411204112141122411234112441125411264112741128411294113041131411324113341134411354113641137411384113941140411414114241143411444114541146411474114841149411504115141152411534115441155411564115741158411594116041161411624116341164411654116641167411684116941170411714117241173411744117541176411774117841179411804118141182411834118441185411864118741188411894119041191411924119341194411954119641197411984119941200412014120241203412044120541206412074120841209412104121141212412134121441215412164121741218412194122041221412224122341224412254122641227412284122941230412314123241233412344123541236412374123841239412404124141242412434124441245412464124741248412494125041251412524125341254412554125641257412584125941260412614126241263412644126541266412674126841269412704127141272412734127441275412764127741278412794128041281412824128341284412854128641287412884128941290412914129241293412944129541296412974129841299413004130141302413034130441305413064130741308413094131041311413124131341314413154131641317413184131941320413214132241323413244132541326413274132841329413304133141332413334133441335413364133741338413394134041341413424134341344413454134641347413484134941350413514135241353413544135541356413574135841359413604136141362413634136441365413664136741368413694137041371413724137341374413754137641377413784137941380413814138241383413844138541386413874138841389413904139141392413934139441395413964139741398413994140041401414024140341404414054140641407414084140941410414114141241413414144141541416414174141841419414204142141422414234142441425414264142741428414294143041431414324143341434414354143641437414384143941440414414144241443414444144541446414474144841449414504145141452414534145441455414564145741458414594146041461414624146341464414654146641467414684146941470414714147241473414744147541476414774147841479414804148141482414834148441485414864148741488414894149041491414924149341494414954149641497414984149941500415014150241503415044150541506415074150841509415104151141512415134151441515415164151741518415194152041521415224152341524415254152641527415284152941530415314153241533415344153541536415374153841539415404154141542415434154441545415464154741548415494155041551415524155341554415554155641557415584155941560415614156241563415644156541566415674156841569415704157141572415734157441575415764157741578415794158041581415824158341584415854158641587415884158941590415914159241593415944159541596415974159841599416004160141602416034160441605416064160741608416094161041611416124161341614416154161641617416184161941620416214162241623416244162541626416274162841629416304163141632416334163441635416364163741638416394164041641416424164341644416454164641647416484164941650416514165241653416544165541656416574165841659416604166141662416634166441665416664166741668416694167041671416724167341674416754167641677416784167941680416814168241683416844168541686416874168841689416904169141692416934169441695416964169741698416994170041701417024170341704417054170641707417084170941710417114171241713417144171541716417174171841719417204172141722417234172441725417264172741728417294173041731417324173341734417354173641737417384173941740417414174241743417444174541746417474174841749417504175141752417534175441755417564175741758417594176041761417624176341764417654176641767417684176941770417714177241773417744177541776417774177841779417804178141782417834178441785417864178741788417894179041791417924179341794417954179641797417984179941800418014180241803418044180541806418074180841809418104181141812418134181441815418164181741818418194182041821418224182341824418254182641827418284182941830418314183241833418344183541836418374183841839418404184141842418434184441845418464184741848418494185041851418524185341854418554185641857418584185941860418614186241863418644186541866418674186841869418704187141872418734187441875418764187741878418794188041881418824188341884418854188641887418884188941890418914189241893418944189541896418974189841899419004190141902419034190441905419064190741908419094191041911419124191341914419154191641917419184191941920419214192241923419244192541926419274192841929419304193141932419334193441935419364193741938419394194041941419424194341944419454194641947419484194941950419514195241953419544195541956419574195841959419604196141962419634196441965419664196741968419694197041971419724197341974419754197641977419784197941980419814198241983419844198541986419874198841989419904199141992419934199441995419964199741998419994200042001420024200342004420054200642007420084200942010420114201242013420144201542016420174201842019420204202142022420234202442025420264202742028420294203042031420324203342034420354203642037420384203942040420414204242043420444204542046420474204842049420504205142052420534205442055420564205742058420594206042061420624206342064420654206642067420684206942070420714207242073420744207542076420774207842079420804208142082420834208442085420864208742088420894209042091420924209342094420954209642097420984209942100421014210242103421044210542106421074210842109421104211142112421134211442115421164211742118421194212042121421224212342124421254212642127421284212942130421314213242133421344213542136421374213842139421404214142142421434214442145421464214742148421494215042151421524215342154421554215642157421584215942160421614216242163421644216542166421674216842169421704217142172421734217442175421764217742178421794218042181421824218342184421854218642187421884218942190421914219242193421944219542196421974219842199422004220142202422034220442205422064220742208422094221042211422124221342214422154221642217422184221942220422214222242223422244222542226422274222842229422304223142232422334223442235422364223742238422394224042241422424224342244422454224642247422484224942250422514225242253422544225542256422574225842259422604226142262422634226442265422664226742268422694227042271422724227342274422754227642277422784227942280422814228242283422844228542286422874228842289422904229142292422934229442295422964229742298422994230042301423024230342304423054230642307423084230942310423114231242313423144231542316423174231842319423204232142322423234232442325423264232742328423294233042331423324233342334423354233642337423384233942340423414234242343423444234542346423474234842349423504235142352423534235442355423564235742358423594236042361423624236342364423654236642367423684236942370423714237242373423744237542376423774237842379423804238142382423834238442385423864238742388423894239042391423924239342394423954239642397423984239942400424014240242403424044240542406424074240842409424104241142412424134241442415424164241742418424194242042421424224242342424424254242642427424284242942430424314243242433424344243542436424374243842439424404244142442424434244442445424464244742448424494245042451424524245342454424554245642457424584245942460424614246242463424644246542466424674246842469424704247142472424734247442475424764247742478424794248042481424824248342484424854248642487424884248942490424914249242493424944249542496424974249842499425004250142502425034250442505425064250742508425094251042511425124251342514425154251642517425184251942520425214252242523425244252542526425274252842529425304253142532425334253442535425364253742538425394254042541425424254342544425454254642547425484254942550425514255242553425544255542556425574255842559425604256142562425634256442565425664256742568425694257042571425724257342574425754257642577425784257942580425814258242583425844258542586425874258842589425904259142592425934259442595425964259742598425994260042601426024260342604426054260642607426084260942610426114261242613426144261542616426174261842619426204262142622426234262442625426264262742628426294263042631426324263342634426354263642637426384263942640426414264242643426444264542646426474264842649426504265142652426534265442655426564265742658426594266042661426624266342664426654266642667426684266942670426714267242673426744267542676426774267842679426804268142682426834268442685426864268742688426894269042691426924269342694426954269642697426984269942700427014270242703427044270542706427074270842709427104271142712427134271442715427164271742718427194272042721427224272342724427254272642727427284272942730427314273242733427344273542736427374273842739427404274142742427434274442745427464274742748427494275042751427524275342754427554275642757427584275942760427614276242763427644276542766427674276842769427704277142772427734277442775427764277742778427794278042781427824278342784427854278642787427884278942790427914279242793427944279542796427974279842799428004280142802428034280442805428064280742808428094281042811428124281342814428154281642817428184281942820428214282242823428244282542826428274282842829428304283142832428334283442835428364283742838428394284042841428424284342844428454284642847428484284942850428514285242853428544285542856428574285842859428604286142862428634286442865428664286742868428694287042871428724287342874428754287642877428784287942880428814288242883428844288542886428874288842889428904289142892428934289442895428964289742898428994290042901429024290342904429054290642907429084290942910429114291242913429144291542916429174291842919429204292142922429234292442925429264292742928429294293042931429324293342934429354293642937429384293942940429414294242943429444294542946429474294842949429504295142952429534295442955429564295742958429594296042961429624296342964429654296642967429684296942970429714297242973429744297542976429774297842979429804298142982429834298442985429864298742988429894299042991429924299342994429954299642997429984299943000430014300243003430044300543006430074300843009430104301143012430134301443015430164301743018430194302043021430224302343024430254302643027430284302943030430314303243033430344303543036430374303843039430404304143042430434304443045430464304743048430494305043051430524305343054430554305643057430584305943060430614306243063430644306543066430674306843069430704307143072430734307443075430764307743078430794308043081430824308343084430854308643087430884308943090430914309243093430944309543096430974309843099431004310143102431034310443105431064310743108431094311043111431124311343114431154311643117431184311943120431214312243123431244312543126431274312843129431304313143132431334313443135431364313743138431394314043141431424314343144431454314643147431484314943150431514315243153431544315543156431574315843159431604316143162431634316443165431664316743168431694317043171431724317343174431754317643177431784317943180431814318243183431844318543186431874318843189431904319143192431934319443195431964319743198431994320043201432024320343204432054320643207432084320943210432114321243213432144321543216432174321843219432204322143222432234322443225432264322743228432294323043231432324323343234432354323643237432384323943240432414324243243432444324543246432474324843249432504325143252432534325443255432564325743258432594326043261432624326343264432654326643267432684326943270432714327243273432744327543276432774327843279432804328143282432834328443285432864328743288432894329043291432924329343294432954329643297432984329943300433014330243303433044330543306433074330843309433104331143312433134331443315433164331743318433194332043321433224332343324433254332643327433284332943330433314333243333433344333543336433374333843339433404334143342433434334443345433464334743348433494335043351433524335343354433554335643357433584335943360433614336243363433644336543366433674336843369433704337143372433734337443375433764337743378433794338043381433824338343384433854338643387433884338943390433914339243393433944339543396433974339843399434004340143402434034340443405434064340743408434094341043411434124341343414434154341643417434184341943420434214342243423434244342543426434274342843429434304343143432434334343443435434364343743438434394344043441434424344343444434454344643447434484344943450434514345243453434544345543456434574345843459434604346143462434634346443465434664346743468434694347043471434724347343474434754347643477434784347943480434814348243483434844348543486434874348843489434904349143492434934349443495434964349743498434994350043501435024350343504435054350643507435084350943510435114351243513435144351543516435174351843519435204352143522435234352443525435264352743528435294353043531435324353343534435354353643537435384353943540435414354243543435444354543546435474354843549435504355143552435534355443555435564355743558435594356043561435624356343564435654356643567435684356943570435714357243573435744357543576435774357843579435804358143582435834358443585435864358743588435894359043591435924359343594435954359643597435984359943600436014360243603436044360543606436074360843609436104361143612436134361443615436164361743618436194362043621436224362343624436254362643627436284362943630436314363243633436344363543636436374363843639436404364143642436434364443645436464364743648436494365043651436524365343654436554365643657436584365943660436614366243663436644366543666436674366843669436704367143672436734367443675436764367743678436794368043681436824368343684436854368643687436884368943690436914369243693436944369543696436974369843699437004370143702437034370443705437064370743708437094371043711437124371343714437154371643717437184371943720437214372243723437244372543726437274372843729437304373143732437334373443735437364373743738437394374043741437424374343744437454374643747437484374943750437514375243753437544375543756437574375843759437604376143762437634376443765437664376743768437694377043771437724377343774437754377643777437784377943780437814378243783437844378543786437874378843789437904379143792437934379443795437964379743798437994380043801438024380343804438054380643807438084380943810438114381243813438144381543816438174381843819438204382143822438234382443825438264382743828438294383043831438324383343834438354383643837438384383943840438414384243843438444384543846438474384843849438504385143852438534385443855438564385743858438594386043861438624386343864438654386643867438684386943870438714387243873438744387543876438774387843879438804388143882438834388443885438864388743888438894389043891438924389343894438954389643897438984389943900439014390243903439044390543906439074390843909439104391143912439134391443915439164391743918439194392043921439224392343924439254392643927439284392943930439314393243933439344393543936439374393843939439404394143942439434394443945439464394743948439494395043951439524395343954439554395643957439584395943960439614396243963439644396543966439674396843969439704397143972439734397443975439764397743978439794398043981439824398343984439854398643987439884398943990439914399243993439944399543996439974399843999440004400144002440034400444005440064400744008440094401044011440124401344014440154401644017440184401944020440214402244023440244402544026440274402844029440304403144032440334403444035440364403744038440394404044041440424404344044440454404644047440484404944050440514405244053440544405544056440574405844059440604406144062440634406444065440664406744068440694407044071440724407344074440754407644077440784407944080440814408244083440844408544086440874408844089440904409144092440934409444095440964409744098440994410044101441024410344104441054410644107441084410944110441114411244113441144411544116441174411844119441204412144122441234412444125441264412744128441294413044131441324413344134441354413644137441384413944140441414414244143441444414544146441474414844149441504415144152441534415444155441564415744158441594416044161441624416344164441654416644167441684416944170441714417244173441744417544176441774417844179441804418144182441834418444185441864418744188441894419044191441924419344194441954419644197441984419944200442014420244203442044420544206442074420844209442104421144212442134421444215442164421744218442194422044221442224422344224442254422644227442284422944230442314423244233442344423544236442374423844239442404424144242442434424444245442464424744248442494425044251442524425344254442554425644257442584425944260442614426244263442644426544266442674426844269442704427144272442734427444275442764427744278442794428044281442824428344284442854428644287442884428944290442914429244293442944429544296442974429844299443004430144302443034430444305443064430744308443094431044311443124431344314443154431644317443184431944320443214432244323443244432544326443274432844329443304433144332443334433444335443364433744338443394434044341443424434344344443454434644347443484434944350443514435244353443544435544356443574435844359443604436144362443634436444365443664436744368443694437044371443724437344374443754437644377443784437944380443814438244383443844438544386443874438844389443904439144392443934439444395443964439744398443994440044401444024440344404444054440644407444084440944410444114441244413444144441544416444174441844419444204442144422444234442444425444264442744428444294443044431444324443344434444354443644437444384443944440444414444244443444444444544446444474444844449444504445144452444534445444455444564445744458444594446044461444624446344464444654446644467444684446944470444714447244473444744447544476444774447844479444804448144482444834448444485444864448744488444894449044491444924449344494444954449644497444984449944500445014450244503445044450544506445074450844509445104451144512445134451444515445164451744518445194452044521445224452344524445254452644527445284452944530445314453244533445344453544536445374453844539445404454144542445434454444545445464454744548445494455044551445524455344554445554455644557445584455944560445614456244563445644456544566445674456844569445704457144572445734457444575445764457744578445794458044581445824458344584445854458644587445884458944590445914459244593445944459544596445974459844599446004460144602446034460444605446064460744608446094461044611446124461344614446154461644617446184461944620446214462244623446244462544626446274462844629446304463144632446334463444635446364463744638446394464044641446424464344644446454464644647446484464944650446514465244653446544465544656446574465844659446604466144662446634466444665446664466744668446694467044671446724467344674446754467644677446784467944680446814468244683446844468544686446874468844689446904469144692446934469444695446964469744698446994470044701447024470344704447054470644707447084470944710447114471244713447144471544716447174471844719447204472144722447234472444725447264472744728447294473044731447324473344734447354473644737447384473944740447414474244743447444474544746447474474844749447504475144752447534475444755447564475744758447594476044761447624476344764447654476644767447684476944770447714477244773447744477544776447774477844779447804478144782447834478444785447864478744788447894479044791447924479344794447954479644797447984479944800448014480244803448044480544806448074480844809448104481144812448134481444815448164481744818448194482044821448224482344824448254482644827448284482944830448314483244833448344483544836448374483844839448404484144842448434484444845448464484744848448494485044851448524485344854448554485644857448584485944860448614486244863448644486544866448674486844869448704487144872448734487444875448764487744878448794488044881448824488344884448854488644887448884488944890448914489244893448944489544896448974489844899449004490144902449034490444905449064490744908449094491044911449124491344914449154491644917449184491944920449214492244923449244492544926449274492844929449304493144932449334493444935449364493744938449394494044941449424494344944449454494644947449484494944950449514495244953449544495544956449574495844959449604496144962449634496444965449664496744968449694497044971449724497344974449754497644977449784497944980449814498244983449844498544986449874498844989449904499144992449934499444995449964499744998449994500045001450024500345004450054500645007450084500945010450114501245013450144501545016450174501845019450204502145022450234502445025450264502745028450294503045031450324503345034450354503645037450384503945040450414504245043450444504545046450474504845049450504505145052450534505445055450564505745058450594506045061450624506345064450654506645067450684506945070450714507245073450744507545076450774507845079450804508145082450834508445085450864508745088450894509045091450924509345094450954509645097450984509945100451014510245103451044510545106451074510845109451104511145112451134511445115451164511745118451194512045121451224512345124451254512645127451284512945130451314513245133451344513545136451374513845139451404514145142451434514445145451464514745148451494515045151451524515345154451554515645157451584515945160451614516245163451644516545166451674516845169451704517145172451734517445175451764517745178451794518045181451824518345184451854518645187451884518945190451914519245193451944519545196451974519845199452004520145202452034520445205452064520745208452094521045211452124521345214452154521645217452184521945220452214522245223452244522545226452274522845229452304523145232452334523445235452364523745238452394524045241452424524345244452454524645247452484524945250452514525245253452544525545256452574525845259452604526145262452634526445265452664526745268452694527045271452724527345274452754527645277452784527945280452814528245283452844528545286452874528845289452904529145292452934529445295452964529745298452994530045301453024530345304453054530645307453084530945310453114531245313453144531545316453174531845319453204532145322453234532445325453264532745328453294533045331453324533345334453354533645337453384533945340453414534245343453444534545346453474534845349453504535145352453534535445355453564535745358453594536045361453624536345364453654536645367453684536945370453714537245373453744537545376453774537845379453804538145382453834538445385453864538745388453894539045391453924539345394453954539645397453984539945400454014540245403454044540545406454074540845409454104541145412454134541445415454164541745418454194542045421454224542345424454254542645427454284542945430454314543245433454344543545436454374543845439454404544145442454434544445445454464544745448454494545045451454524545345454454554545645457454584545945460454614546245463454644546545466454674546845469454704547145472454734547445475454764547745478454794548045481454824548345484454854548645487454884548945490454914549245493454944549545496454974549845499455004550145502455034550445505455064550745508455094551045511455124551345514455154551645517455184551945520455214552245523455244552545526455274552845529455304553145532455334553445535455364553745538455394554045541455424554345544455454554645547455484554945550455514555245553455544555545556455574555845559455604556145562455634556445565455664556745568455694557045571455724557345574455754557645577455784557945580455814558245583455844558545586455874558845589455904559145592455934559445595455964559745598455994560045601456024560345604456054560645607456084560945610456114561245613456144561545616456174561845619456204562145622456234562445625456264562745628456294563045631456324563345634456354563645637456384563945640456414564245643456444564545646456474564845649456504565145652456534565445655456564565745658456594566045661456624566345664456654566645667456684566945670456714567245673456744567545676456774567845679456804568145682456834568445685456864568745688456894569045691456924569345694456954569645697456984569945700457014570245703457044570545706457074570845709457104571145712457134571445715457164571745718457194572045721457224572345724457254572645727457284572945730457314573245733457344573545736457374573845739457404574145742457434574445745457464574745748457494575045751457524575345754457554575645757457584575945760457614576245763457644576545766457674576845769457704577145772457734577445775457764577745778457794578045781457824578345784457854578645787457884578945790457914579245793457944579545796457974579845799458004580145802458034580445805458064580745808458094581045811458124581345814458154581645817458184581945820458214582245823458244582545826458274582845829458304583145832458334583445835458364583745838458394584045841458424584345844458454584645847458484584945850458514585245853458544585545856458574585845859458604586145862458634586445865458664586745868458694587045871458724587345874458754587645877458784587945880458814588245883458844588545886458874588845889458904589145892458934589445895458964589745898458994590045901459024590345904459054590645907459084590945910459114591245913459144591545916459174591845919459204592145922459234592445925459264592745928459294593045931459324593345934459354593645937459384593945940459414594245943459444594545946459474594845949459504595145952459534595445955459564595745958459594596045961459624596345964459654596645967459684596945970459714597245973459744597545976459774597845979459804598145982459834598445985459864598745988459894599045991459924599345994459954599645997459984599946000460014600246003460044600546006460074600846009460104601146012460134601446015460164601746018460194602046021460224602346024460254602646027460284602946030460314603246033460344603546036460374603846039460404604146042460434604446045460464604746048460494605046051460524605346054460554605646057460584605946060460614606246063460644606546066460674606846069460704607146072460734607446075460764607746078460794608046081460824608346084460854608646087460884608946090460914609246093460944609546096460974609846099461004610146102461034610446105461064610746108461094611046111461124611346114461154611646117461184611946120461214612246123461244612546126461274612846129461304613146132461334613446135461364613746138461394614046141461424614346144461454614646147461484614946150461514615246153461544615546156461574615846159461604616146162461634616446165461664616746168461694617046171461724617346174461754617646177461784617946180461814618246183461844618546186461874618846189461904619146192461934619446195461964619746198461994620046201462024620346204462054620646207462084620946210462114621246213462144621546216462174621846219462204622146222462234622446225462264622746228462294623046231462324623346234462354623646237462384623946240462414624246243462444624546246462474624846249462504625146252462534625446255462564625746258462594626046261462624626346264462654626646267462684626946270462714627246273462744627546276462774627846279462804628146282462834628446285462864628746288462894629046291462924629346294462954629646297462984629946300463014630246303463044630546306463074630846309463104631146312463134631446315463164631746318463194632046321463224632346324463254632646327463284632946330463314633246333463344633546336463374633846339463404634146342463434634446345463464634746348463494635046351463524635346354463554635646357463584635946360463614636246363463644636546366463674636846369463704637146372463734637446375463764637746378463794638046381463824638346384463854638646387463884638946390463914639246393463944639546396463974639846399464004640146402464034640446405464064640746408464094641046411464124641346414464154641646417464184641946420464214642246423464244642546426464274642846429464304643146432464334643446435464364643746438464394644046441464424644346444464454644646447464484644946450464514645246453464544645546456464574645846459464604646146462464634646446465464664646746468464694647046471464724647346474464754647646477464784647946480464814648246483464844648546486464874648846489464904649146492464934649446495464964649746498464994650046501465024650346504465054650646507465084650946510465114651246513465144651546516465174651846519465204652146522465234652446525465264652746528465294653046531465324653346534465354653646537465384653946540465414654246543465444654546546465474654846549465504655146552465534655446555465564655746558465594656046561465624656346564465654656646567465684656946570465714657246573465744657546576465774657846579465804658146582465834658446585465864658746588465894659046591465924659346594465954659646597465984659946600466014660246603466044660546606466074660846609466104661146612466134661446615466164661746618466194662046621466224662346624466254662646627466284662946630466314663246633466344663546636466374663846639466404664146642466434664446645466464664746648466494665046651466524665346654466554665646657466584665946660466614666246663466644666546666466674666846669466704667146672466734667446675466764667746678466794668046681466824668346684466854668646687466884668946690466914669246693466944669546696466974669846699467004670146702467034670446705467064670746708467094671046711467124671346714467154671646717467184671946720467214672246723467244672546726467274672846729467304673146732467334673446735467364673746738467394674046741467424674346744467454674646747467484674946750467514675246753467544675546756467574675846759467604676146762467634676446765467664676746768467694677046771467724677346774467754677646777467784677946780467814678246783467844678546786467874678846789467904679146792467934679446795467964679746798467994680046801468024680346804468054680646807468084680946810468114681246813468144681546816468174681846819468204682146822468234682446825468264682746828468294683046831468324683346834468354683646837468384683946840468414684246843468444684546846468474684846849468504685146852468534685446855468564685746858468594686046861468624686346864468654686646867468684686946870468714687246873468744687546876468774687846879468804688146882468834688446885468864688746888468894689046891468924689346894468954689646897468984689946900469014690246903469044690546906469074690846909469104691146912469134691446915469164691746918469194692046921469224692346924469254692646927469284692946930469314693246933469344693546936469374693846939469404694146942469434694446945469464694746948469494695046951469524695346954469554695646957469584695946960469614696246963469644696546966469674696846969469704697146972469734697446975469764697746978469794698046981469824698346984469854698646987469884698946990469914699246993469944699546996469974699846999470004700147002470034700447005470064700747008470094701047011470124701347014470154701647017470184701947020470214702247023470244702547026470274702847029470304703147032470334703447035470364703747038470394704047041470424704347044470454704647047470484704947050470514705247053470544705547056470574705847059470604706147062470634706447065470664706747068470694707047071470724707347074470754707647077470784707947080470814708247083470844708547086470874708847089470904709147092470934709447095470964709747098470994710047101471024710347104471054710647107471084710947110471114711247113471144711547116471174711847119471204712147122471234712447125471264712747128471294713047131471324713347134471354713647137471384713947140471414714247143471444714547146471474714847149471504715147152471534715447155471564715747158471594716047161471624716347164471654716647167471684716947170471714717247173471744717547176471774717847179471804718147182471834718447185471864718747188471894719047191471924719347194471954719647197471984719947200472014720247203472044720547206472074720847209472104721147212472134721447215472164721747218472194722047221472224722347224472254722647227472284722947230472314723247233472344723547236472374723847239472404724147242472434724447245472464724747248472494725047251472524725347254472554725647257472584725947260472614726247263472644726547266472674726847269472704727147272472734727447275472764727747278472794728047281472824728347284472854728647287472884728947290472914729247293472944729547296472974729847299473004730147302473034730447305473064730747308473094731047311473124731347314473154731647317473184731947320473214732247323473244732547326473274732847329473304733147332473334733447335473364733747338473394734047341473424734347344473454734647347473484734947350473514735247353473544735547356473574735847359473604736147362473634736447365473664736747368473694737047371473724737347374473754737647377473784737947380473814738247383473844738547386473874738847389473904739147392473934739447395473964739747398473994740047401474024740347404474054740647407474084740947410474114741247413474144741547416474174741847419474204742147422474234742447425474264742747428474294743047431474324743347434474354743647437474384743947440474414744247443474444744547446474474744847449474504745147452474534745447455474564745747458474594746047461474624746347464474654746647467474684746947470474714747247473474744747547476474774747847479474804748147482474834748447485474864748747488474894749047491474924749347494474954749647497474984749947500475014750247503475044750547506475074750847509475104751147512475134751447515475164751747518475194752047521475224752347524475254752647527475284752947530475314753247533475344753547536475374753847539475404754147542475434754447545475464754747548475494755047551475524755347554475554755647557475584755947560475614756247563475644756547566475674756847569475704757147572475734757447575475764757747578475794758047581475824758347584475854758647587475884758947590475914759247593475944759547596475974759847599476004760147602476034760447605476064760747608476094761047611476124761347614476154761647617476184761947620476214762247623476244762547626476274762847629476304763147632476334763447635476364763747638476394764047641476424764347644476454764647647476484764947650476514765247653476544765547656476574765847659476604766147662476634766447665476664766747668476694767047671476724767347674476754767647677476784767947680476814768247683476844768547686476874768847689476904769147692476934769447695476964769747698476994770047701477024770347704477054770647707477084770947710477114771247713477144771547716477174771847719477204772147722477234772447725477264772747728477294773047731477324773347734477354773647737477384773947740477414774247743477444774547746477474774847749477504775147752477534775447755477564775747758477594776047761477624776347764477654776647767477684776947770477714777247773477744777547776477774777847779477804778147782477834778447785477864778747788477894779047791477924779347794477954779647797477984779947800478014780247803478044780547806478074780847809478104781147812478134781447815478164781747818478194782047821478224782347824478254782647827478284782947830478314783247833478344783547836478374783847839478404784147842478434784447845478464784747848478494785047851478524785347854478554785647857478584785947860478614786247863478644786547866478674786847869478704787147872478734787447875478764787747878478794788047881478824788347884478854788647887478884788947890478914789247893478944789547896478974789847899479004790147902479034790447905479064790747908479094791047911479124791347914479154791647917479184791947920479214792247923479244792547926479274792847929479304793147932479334793447935479364793747938479394794047941479424794347944479454794647947479484794947950479514795247953479544795547956479574795847959479604796147962479634796447965479664796747968479694797047971479724797347974479754797647977479784797947980479814798247983479844798547986479874798847989479904799147992479934799447995479964799747998479994800048001480024800348004480054800648007480084800948010480114801248013480144801548016480174801848019480204802148022480234802448025480264802748028480294803048031480324803348034480354803648037480384803948040480414804248043480444804548046480474804848049480504805148052480534805448055480564805748058480594806048061480624806348064480654806648067480684806948070480714807248073480744807548076480774807848079480804808148082480834808448085480864808748088480894809048091480924809348094480954809648097480984809948100481014810248103481044810548106481074810848109481104811148112481134811448115481164811748118481194812048121481224812348124481254812648127481284812948130481314813248133481344813548136481374813848139481404814148142481434814448145481464814748148481494815048151481524815348154481554815648157481584815948160481614816248163481644816548166481674816848169481704817148172481734817448175481764817748178481794818048181481824818348184481854818648187481884818948190481914819248193481944819548196481974819848199482004820148202482034820448205482064820748208482094821048211482124821348214482154821648217482184821948220482214822248223482244822548226482274822848229482304823148232482334823448235482364823748238482394824048241482424824348244482454824648247482484824948250482514825248253482544825548256482574825848259482604826148262482634826448265482664826748268482694827048271482724827348274482754827648277482784827948280482814828248283482844828548286482874828848289482904829148292482934829448295482964829748298482994830048301483024830348304483054830648307483084830948310483114831248313483144831548316483174831848319483204832148322483234832448325483264832748328483294833048331483324833348334483354833648337483384833948340483414834248343483444834548346483474834848349483504835148352483534835448355483564835748358483594836048361483624836348364483654836648367483684836948370483714837248373483744837548376483774837848379483804838148382483834838448385483864838748388483894839048391483924839348394483954839648397483984839948400484014840248403484044840548406484074840848409484104841148412484134841448415484164841748418484194842048421484224842348424484254842648427484284842948430484314843248433484344843548436484374843848439484404844148442484434844448445484464844748448484494845048451484524845348454484554845648457484584845948460484614846248463484644846548466484674846848469484704847148472484734847448475484764847748478484794848048481484824848348484484854848648487484884848948490484914849248493484944849548496484974849848499485004850148502485034850448505485064850748508485094851048511485124851348514485154851648517485184851948520485214852248523485244852548526485274852848529485304853148532485334853448535485364853748538485394854048541485424854348544485454854648547485484854948550485514855248553485544855548556485574855848559485604856148562485634856448565485664856748568485694857048571485724857348574485754857648577485784857948580485814858248583485844858548586485874858848589485904859148592485934859448595485964859748598485994860048601486024860348604486054860648607486084860948610486114861248613486144861548616486174861848619486204862148622486234862448625486264862748628486294863048631486324863348634486354863648637486384863948640486414864248643486444864548646486474864848649486504865148652486534865448655486564865748658486594866048661486624866348664486654866648667486684866948670486714867248673486744867548676486774867848679486804868148682486834868448685486864868748688486894869048691486924869348694486954869648697486984869948700487014870248703487044870548706487074870848709487104871148712487134871448715487164871748718487194872048721487224872348724487254872648727487284872948730487314873248733487344873548736487374873848739487404874148742487434874448745487464874748748487494875048751487524875348754487554875648757487584875948760487614876248763487644876548766487674876848769487704877148772487734877448775487764877748778487794878048781487824878348784487854878648787487884878948790487914879248793487944879548796487974879848799488004880148802488034880448805488064880748808488094881048811488124881348814488154881648817488184881948820488214882248823488244882548826488274882848829488304883148832488334883448835488364883748838488394884048841488424884348844488454884648847488484884948850488514885248853488544885548856488574885848859488604886148862488634886448865488664886748868488694887048871488724887348874488754887648877488784887948880488814888248883488844888548886488874888848889488904889148892488934889448895488964889748898488994890048901489024890348904489054890648907489084890948910489114891248913489144891548916489174891848919489204892148922489234892448925489264892748928489294893048931489324893348934489354893648937489384893948940489414894248943489444894548946489474894848949489504895148952489534895448955489564895748958489594896048961489624896348964489654896648967489684896948970489714897248973489744897548976489774897848979489804898148982489834898448985489864898748988489894899048991489924899348994489954899648997489984899949000490014900249003490044900549006490074900849009490104901149012490134901449015490164901749018490194902049021490224902349024490254902649027490284902949030490314903249033490344903549036490374903849039490404904149042490434904449045490464904749048490494905049051490524905349054490554905649057490584905949060490614906249063490644906549066490674906849069490704907149072490734907449075490764907749078490794908049081490824908349084490854908649087490884908949090490914909249093490944909549096490974909849099491004910149102491034910449105491064910749108491094911049111491124911349114491154911649117491184911949120491214912249123491244912549126491274912849129491304913149132491334913449135491364913749138491394914049141491424914349144491454914649147491484914949150491514915249153491544915549156491574915849159491604916149162491634916449165491664916749168491694917049171491724917349174491754917649177491784917949180491814918249183491844918549186491874918849189491904919149192491934919449195491964919749198491994920049201492024920349204492054920649207492084920949210492114921249213492144921549216492174921849219492204922149222492234922449225492264922749228492294923049231492324923349234492354923649237492384923949240492414924249243492444924549246492474924849249492504925149252492534925449255492564925749258492594926049261492624926349264492654926649267492684926949270492714927249273492744927549276492774927849279492804928149282492834928449285492864928749288492894929049291492924929349294492954929649297492984929949300493014930249303493044930549306493074930849309493104931149312493134931449315493164931749318493194932049321493224932349324493254932649327493284932949330493314933249333493344933549336493374933849339493404934149342493434934449345493464934749348493494935049351493524935349354493554935649357493584935949360493614936249363493644936549366493674936849369493704937149372493734937449375493764937749378493794938049381493824938349384493854938649387493884938949390493914939249393493944939549396493974939849399494004940149402494034940449405494064940749408494094941049411494124941349414494154941649417494184941949420494214942249423494244942549426494274942849429494304943149432494334943449435494364943749438494394944049441494424944349444494454944649447494484944949450494514945249453494544945549456494574945849459494604946149462494634946449465494664946749468494694947049471494724947349474494754947649477494784947949480494814948249483494844948549486494874948849489494904949149492494934949449495494964949749498494994950049501495024950349504495054950649507495084950949510495114951249513495144951549516495174951849519495204952149522495234952449525495264952749528495294953049531495324953349534495354953649537495384953949540495414954249543495444954549546495474954849549495504955149552495534955449555495564955749558495594956049561495624956349564495654956649567495684956949570495714957249573495744957549576495774957849579495804958149582495834958449585495864958749588495894959049591495924959349594495954959649597495984959949600496014960249603496044960549606496074960849609496104961149612496134961449615496164961749618496194962049621496224962349624496254962649627496284962949630496314963249633496344963549636496374963849639496404964149642496434964449645496464964749648496494965049651496524965349654496554965649657496584965949660496614966249663496644966549666496674966849669496704967149672496734967449675496764967749678496794968049681496824968349684496854968649687496884968949690496914969249693496944969549696496974969849699497004970149702497034970449705497064970749708497094971049711497124971349714497154971649717497184971949720497214972249723497244972549726497274972849729497304973149732497334973449735497364973749738497394974049741497424974349744497454974649747497484974949750497514975249753497544975549756497574975849759497604976149762497634976449765497664976749768497694977049771497724977349774497754977649777497784977949780497814978249783497844978549786497874978849789497904979149792497934979449795497964979749798497994980049801498024980349804498054980649807498084980949810498114981249813498144981549816498174981849819498204982149822498234982449825498264982749828498294983049831498324983349834498354983649837498384983949840498414984249843498444984549846498474984849849498504985149852498534985449855498564985749858498594986049861498624986349864498654986649867498684986949870498714987249873498744987549876498774987849879498804988149882498834988449885498864988749888498894989049891498924989349894498954989649897498984989949900499014990249903499044990549906499074990849909499104991149912499134991449915499164991749918499194992049921499224992349924499254992649927499284992949930499314993249933499344993549936499374993849939499404994149942499434994449945499464994749948499494995049951499524995349954499554995649957499584995949960499614996249963499644996549966499674996849969499704997149972499734997449975499764997749978499794998049981499824998349984499854998649987499884998949990499914999249993499944999549996499974999849999500005000150002500035000450005500065000750008500095001050011500125001350014500155001650017500185001950020500215002250023500245002550026500275002850029500305003150032500335003450035500365003750038500395004050041500425004350044500455004650047500485004950050500515005250053500545005550056500575005850059500605006150062500635006450065500665006750068500695007050071500725007350074500755007650077500785007950080500815008250083500845008550086500875008850089500905009150092500935009450095500965009750098500995010050101501025010350104501055010650107501085010950110501115011250113501145011550116501175011850119501205012150122501235012450125501265012750128501295013050131501325013350134501355013650137501385013950140501415014250143501445014550146501475014850149501505015150152501535015450155501565015750158501595016050161501625016350164501655016650167501685016950170501715017250173501745017550176501775017850179501805018150182501835018450185501865018750188501895019050191501925019350194501955019650197501985019950200502015020250203502045020550206502075020850209502105021150212502135021450215502165021750218502195022050221502225022350224502255022650227502285022950230502315023250233502345023550236502375023850239502405024150242502435024450245502465024750248502495025050251502525025350254502555025650257502585025950260502615026250263502645026550266502675026850269502705027150272502735027450275502765027750278502795028050281502825028350284502855028650287502885028950290502915029250293502945029550296502975029850299503005030150302503035030450305503065030750308503095031050311503125031350314503155031650317503185031950320503215032250323503245032550326503275032850329503305033150332503335033450335503365033750338503395034050341503425034350344503455034650347503485034950350503515035250353503545035550356503575035850359503605036150362503635036450365503665036750368503695037050371503725037350374503755037650377503785037950380503815038250383503845038550386503875038850389503905039150392503935039450395503965039750398503995040050401504025040350404504055040650407504085040950410504115041250413504145041550416504175041850419504205042150422504235042450425504265042750428504295043050431504325043350434504355043650437504385043950440504415044250443504445044550446504475044850449504505045150452504535045450455504565045750458504595046050461504625046350464504655046650467504685046950470504715047250473504745047550476504775047850479504805048150482504835048450485504865048750488504895049050491504925049350494504955049650497504985049950500505015050250503505045050550506505075050850509505105051150512505135051450515505165051750518505195052050521505225052350524505255052650527505285052950530505315053250533505345053550536505375053850539505405054150542505435054450545505465054750548505495055050551505525055350554505555055650557505585055950560505615056250563505645056550566505675056850569505705057150572505735057450575505765057750578505795058050581505825058350584505855058650587505885058950590505915059250593505945059550596505975059850599506005060150602506035060450605506065060750608506095061050611506125061350614506155061650617506185061950620506215062250623506245062550626506275062850629506305063150632506335063450635506365063750638506395064050641506425064350644506455064650647506485064950650506515065250653506545065550656506575065850659506605066150662506635066450665506665066750668506695067050671506725067350674506755067650677506785067950680506815068250683506845068550686506875068850689506905069150692506935069450695506965069750698506995070050701507025070350704507055070650707507085070950710507115071250713507145071550716507175071850719507205072150722507235072450725507265072750728507295073050731507325073350734507355073650737507385073950740507415074250743507445074550746507475074850749507505075150752507535075450755507565075750758507595076050761507625076350764507655076650767507685076950770507715077250773507745077550776507775077850779507805078150782507835078450785507865078750788507895079050791507925079350794507955079650797507985079950800508015080250803508045080550806508075080850809508105081150812508135081450815508165081750818508195082050821508225082350824508255082650827508285082950830508315083250833508345083550836508375083850839508405084150842508435084450845508465084750848508495085050851508525085350854508555085650857508585085950860508615086250863508645086550866508675086850869508705087150872508735087450875508765087750878508795088050881508825088350884508855088650887508885088950890508915089250893508945089550896508975089850899509005090150902509035090450905509065090750908509095091050911509125091350914509155091650917509185091950920509215092250923509245092550926509275092850929509305093150932509335093450935509365093750938509395094050941509425094350944509455094650947509485094950950509515095250953509545095550956509575095850959509605096150962509635096450965509665096750968509695097050971509725097350974509755097650977509785097950980509815098250983509845098550986509875098850989509905099150992509935099450995509965099750998509995100051001510025100351004510055100651007510085100951010510115101251013510145101551016510175101851019510205102151022510235102451025510265102751028510295103051031510325103351034510355103651037510385103951040510415104251043510445104551046510475104851049510505105151052510535105451055510565105751058510595106051061510625106351064510655106651067510685106951070510715107251073510745107551076510775107851079510805108151082510835108451085510865108751088510895109051091510925109351094510955109651097510985109951100511015110251103511045110551106511075110851109511105111151112511135111451115511165111751118511195112051121511225112351124511255112651127511285112951130511315113251133511345113551136511375113851139511405114151142511435114451145511465114751148511495115051151511525115351154511555115651157511585115951160511615116251163511645116551166511675116851169511705117151172511735117451175511765117751178511795118051181511825118351184511855118651187511885118951190511915119251193511945119551196511975119851199512005120151202512035120451205512065120751208512095121051211512125121351214512155121651217512185121951220512215122251223512245122551226512275122851229512305123151232512335123451235512365123751238512395124051241512425124351244512455124651247512485124951250512515125251253512545125551256512575125851259512605126151262512635126451265512665126751268512695127051271512725127351274512755127651277512785127951280512815128251283512845128551286512875128851289512905129151292512935129451295512965129751298512995130051301513025130351304513055130651307513085130951310513115131251313513145131551316513175131851319513205132151322513235132451325513265132751328513295133051331513325133351334513355133651337513385133951340513415134251343513445134551346513475134851349513505135151352513535135451355513565135751358513595136051361513625136351364513655136651367513685136951370513715137251373513745137551376513775137851379513805138151382513835138451385513865138751388513895139051391513925139351394513955139651397513985139951400514015140251403514045140551406514075140851409514105141151412514135141451415514165141751418514195142051421514225142351424514255142651427514285142951430514315143251433514345143551436514375143851439514405144151442514435144451445514465144751448514495145051451514525145351454514555145651457514585145951460514615146251463514645146551466514675146851469514705147151472514735147451475514765147751478514795148051481514825148351484514855148651487514885148951490514915149251493514945149551496514975149851499515005150151502515035150451505515065150751508515095151051511515125151351514515155151651517515185151951520515215152251523515245152551526515275152851529515305153151532515335153451535515365153751538515395154051541515425154351544515455154651547515485154951550515515155251553515545155551556515575155851559515605156151562515635156451565515665156751568515695157051571515725157351574515755157651577515785157951580515815158251583515845158551586515875158851589515905159151592515935159451595515965159751598515995160051601516025160351604516055160651607516085160951610516115161251613516145161551616516175161851619516205162151622516235162451625516265162751628516295163051631516325163351634516355163651637516385163951640516415164251643516445164551646516475164851649516505165151652516535165451655516565165751658516595166051661516625166351664516655166651667516685166951670516715167251673516745167551676516775167851679516805168151682516835168451685516865168751688516895169051691516925169351694516955169651697516985169951700517015170251703517045170551706517075170851709517105171151712517135171451715517165171751718517195172051721517225172351724517255172651727517285172951730517315173251733517345173551736517375173851739517405174151742517435174451745517465174751748517495175051751517525175351754517555175651757517585175951760517615176251763517645176551766517675176851769517705177151772517735177451775517765177751778517795178051781517825178351784517855178651787517885178951790517915179251793517945179551796517975179851799518005180151802518035180451805518065180751808518095181051811518125181351814518155181651817518185181951820518215182251823518245182551826518275182851829518305183151832518335183451835518365183751838518395184051841518425184351844518455184651847518485184951850518515185251853518545185551856518575185851859518605186151862518635186451865518665186751868518695187051871518725187351874518755187651877518785187951880518815188251883518845188551886518875188851889518905189151892518935189451895518965189751898518995190051901519025190351904519055190651907519085190951910519115191251913519145191551916519175191851919519205192151922519235192451925519265192751928519295193051931519325193351934519355193651937519385193951940519415194251943519445194551946519475194851949519505195151952519535195451955519565195751958519595196051961519625196351964519655196651967519685196951970519715197251973519745197551976519775197851979519805198151982519835198451985519865198751988519895199051991519925199351994519955199651997519985199952000520015200252003520045200552006520075200852009520105201152012520135201452015520165201752018520195202052021520225202352024520255202652027520285202952030520315203252033520345203552036520375203852039520405204152042520435204452045520465204752048520495205052051520525205352054520555205652057520585205952060520615206252063520645206552066520675206852069520705207152072520735207452075520765207752078520795208052081520825208352084520855208652087520885208952090520915209252093520945209552096520975209852099521005210152102521035210452105521065210752108521095211052111521125211352114521155211652117521185211952120521215212252123521245212552126521275212852129521305213152132521335213452135521365213752138521395214052141521425214352144521455214652147521485214952150521515215252153521545215552156521575215852159521605216152162521635216452165521665216752168521695217052171521725217352174521755217652177521785217952180521815218252183521845218552186521875218852189521905219152192521935219452195521965219752198521995220052201522025220352204522055220652207522085220952210522115221252213522145221552216522175221852219522205222152222522235222452225522265222752228522295223052231522325223352234522355223652237522385223952240522415224252243522445224552246522475224852249522505225152252522535225452255522565225752258522595226052261522625226352264522655226652267522685226952270522715227252273522745227552276522775227852279522805228152282522835228452285522865228752288522895229052291522925229352294522955229652297522985229952300523015230252303523045230552306523075230852309523105231152312523135231452315523165231752318523195232052321523225232352324523255232652327523285232952330523315233252333523345233552336523375233852339523405234152342523435234452345523465234752348523495235052351523525235352354523555235652357523585235952360523615236252363523645236552366523675236852369523705237152372523735237452375523765237752378523795238052381523825238352384523855238652387523885238952390523915239252393523945239552396523975239852399524005240152402524035240452405524065240752408524095241052411524125241352414524155241652417524185241952420524215242252423524245242552426524275242852429524305243152432524335243452435524365243752438524395244052441524425244352444524455244652447524485244952450524515245252453524545245552456524575245852459524605246152462524635246452465524665246752468524695247052471524725247352474524755247652477524785247952480524815248252483524845248552486524875248852489524905249152492524935249452495524965249752498524995250052501525025250352504525055250652507525085250952510525115251252513525145251552516525175251852519525205252152522525235252452525525265252752528525295253052531525325253352534525355253652537525385253952540525415254252543525445254552546525475254852549525505255152552525535255452555525565255752558525595256052561525625256352564525655256652567525685256952570525715257252573525745257552576525775257852579525805258152582525835258452585525865258752588525895259052591525925259352594525955259652597525985259952600526015260252603526045260552606526075260852609526105261152612526135261452615526165261752618526195262052621526225262352624526255262652627526285262952630526315263252633526345263552636526375263852639526405264152642526435264452645526465264752648526495265052651526525265352654526555265652657526585265952660526615266252663526645266552666526675266852669526705267152672526735267452675526765267752678526795268052681526825268352684526855268652687526885268952690526915269252693526945269552696526975269852699527005270152702527035270452705527065270752708527095271052711527125271352714527155271652717527185271952720527215272252723527245272552726527275272852729527305273152732527335273452735527365273752738527395274052741527425274352744527455274652747527485274952750527515275252753527545275552756527575275852759527605276152762527635276452765527665276752768527695277052771527725277352774527755277652777527785277952780527815278252783527845278552786527875278852789527905279152792527935279452795527965279752798527995280052801528025280352804528055280652807528085280952810528115281252813528145281552816528175281852819528205282152822528235282452825528265282752828528295283052831528325283352834528355283652837528385283952840528415284252843528445284552846528475284852849528505285152852528535285452855528565285752858528595286052861528625286352864528655286652867528685286952870528715287252873528745287552876528775287852879528805288152882528835288452885528865288752888528895289052891528925289352894528955289652897528985289952900529015290252903529045290552906529075290852909529105291152912529135291452915529165291752918529195292052921529225292352924529255292652927529285292952930529315293252933529345293552936529375293852939529405294152942529435294452945529465294752948529495295052951529525295352954529555295652957529585295952960529615296252963529645296552966529675296852969529705297152972529735297452975529765297752978529795298052981529825298352984529855298652987529885298952990529915299252993529945299552996529975299852999530005300153002530035300453005530065300753008530095301053011530125301353014530155301653017530185301953020530215302253023530245302553026530275302853029530305303153032530335303453035530365303753038530395304053041530425304353044530455304653047530485304953050530515305253053530545305553056530575305853059530605306153062530635306453065530665306753068530695307053071530725307353074530755307653077530785307953080530815308253083530845308553086530875308853089530905309153092530935309453095530965309753098530995310053101531025310353104531055310653107531085310953110531115311253113531145311553116531175311853119531205312153122531235312453125531265312753128531295313053131531325313353134531355313653137531385313953140531415314253143531445314553146531475314853149531505315153152531535315453155531565315753158531595316053161531625316353164531655316653167531685316953170531715317253173531745317553176531775317853179531805318153182531835318453185531865318753188531895319053191531925319353194531955319653197531985319953200532015320253203532045320553206532075320853209532105321153212532135321453215532165321753218532195322053221532225322353224532255322653227532285322953230532315323253233532345323553236532375323853239532405324153242532435324453245532465324753248532495325053251532525325353254532555325653257532585325953260532615326253263532645326553266532675326853269532705327153272532735327453275532765327753278532795328053281532825328353284532855328653287532885328953290532915329253293532945329553296532975329853299533005330153302533035330453305533065330753308533095331053311533125331353314533155331653317533185331953320533215332253323533245332553326533275332853329533305333153332533335333453335533365333753338533395334053341533425334353344533455334653347533485334953350533515335253353533545335553356533575335853359533605336153362533635336453365533665336753368533695337053371533725337353374533755337653377533785337953380533815338253383533845338553386533875338853389533905339153392533935339453395533965339753398533995340053401534025340353404534055340653407534085340953410534115341253413534145341553416534175341853419534205342153422534235342453425534265342753428534295343053431534325343353434534355343653437534385343953440534415344253443534445344553446534475344853449534505345153452534535345453455534565345753458534595346053461534625346353464534655346653467534685346953470534715347253473534745347553476534775347853479534805348153482534835348453485534865348753488534895349053491534925349353494534955349653497534985349953500535015350253503535045350553506535075350853509535105351153512535135351453515535165351753518535195352053521535225352353524535255352653527535285352953530535315353253533535345353553536535375353853539535405354153542535435354453545535465354753548535495355053551535525355353554535555355653557535585355953560535615356253563535645356553566535675356853569535705357153572535735357453575535765357753578535795358053581535825358353584535855358653587535885358953590535915359253593535945359553596535975359853599536005360153602536035360453605536065360753608536095361053611536125361353614536155361653617536185361953620536215362253623536245362553626536275362853629536305363153632536335363453635536365363753638536395364053641536425364353644536455364653647536485364953650536515365253653536545365553656536575365853659536605366153662536635366453665536665366753668536695367053671536725367353674536755367653677536785367953680536815368253683536845368553686536875368853689536905369153692536935369453695536965369753698536995370053701537025370353704537055370653707537085370953710537115371253713537145371553716537175371853719537205372153722537235372453725537265372753728537295373053731537325373353734537355373653737537385373953740537415374253743537445374553746537475374853749537505375153752537535375453755537565375753758537595376053761537625376353764537655376653767537685376953770537715377253773537745377553776537775377853779537805378153782537835378453785537865378753788537895379053791537925379353794537955379653797537985379953800538015380253803538045380553806538075380853809538105381153812538135381453815538165381753818538195382053821538225382353824538255382653827538285382953830538315383253833538345383553836538375383853839538405384153842538435384453845538465384753848538495385053851538525385353854538555385653857538585385953860538615386253863538645386553866538675386853869538705387153872538735387453875538765387753878538795388053881538825388353884538855388653887538885388953890538915389253893538945389553896538975389853899539005390153902539035390453905539065390753908539095391053911539125391353914539155391653917539185391953920539215392253923539245392553926539275392853929539305393153932539335393453935539365393753938539395394053941539425394353944539455394653947539485394953950539515395253953539545395553956539575395853959539605396153962539635396453965539665396753968539695397053971539725397353974539755397653977539785397953980539815398253983539845398553986539875398853989539905399153992539935399453995539965399753998539995400054001540025400354004540055400654007540085400954010540115401254013540145401554016540175401854019540205402154022540235402454025540265402754028540295403054031540325403354034540355403654037540385403954040540415404254043540445404554046540475404854049540505405154052540535405454055540565405754058540595406054061540625406354064540655406654067540685406954070540715407254073540745407554076540775407854079540805408154082540835408454085540865408754088540895409054091540925409354094540955409654097540985409954100541015410254103541045410554106541075410854109541105411154112541135411454115541165411754118541195412054121541225412354124541255412654127541285412954130541315413254133541345413554136541375413854139541405414154142541435414454145541465414754148541495415054151541525415354154541555415654157541585415954160541615416254163541645416554166541675416854169541705417154172541735417454175541765417754178541795418054181541825418354184541855418654187541885418954190541915419254193541945419554196541975419854199542005420154202542035420454205542065420754208542095421054211542125421354214542155421654217542185421954220542215422254223542245422554226542275422854229542305423154232542335423454235542365423754238542395424054241542425424354244542455424654247542485424954250542515425254253542545425554256542575425854259542605426154262542635426454265542665426754268542695427054271542725427354274542755427654277542785427954280542815428254283542845428554286542875428854289542905429154292542935429454295542965429754298542995430054301543025430354304543055430654307543085430954310543115431254313543145431554316543175431854319543205432154322543235432454325543265432754328543295433054331543325433354334543355433654337543385433954340543415434254343543445434554346543475434854349543505435154352543535435454355543565435754358543595436054361543625436354364543655436654367543685436954370543715437254373543745437554376543775437854379543805438154382543835438454385543865438754388543895439054391543925439354394543955439654397543985439954400544015440254403544045440554406544075440854409544105441154412544135441454415544165441754418544195442054421544225442354424544255442654427544285442954430544315443254433544345443554436544375443854439544405444154442544435444454445544465444754448544495445054451544525445354454544555445654457544585445954460544615446254463544645446554466544675446854469544705447154472544735447454475544765447754478544795448054481544825448354484544855448654487544885448954490544915449254493544945449554496544975449854499545005450154502545035450454505545065450754508545095451054511545125451354514545155451654517545185451954520545215452254523545245452554526545275452854529545305453154532545335453454535545365453754538545395454054541545425454354544545455454654547545485454954550545515455254553545545455554556545575455854559545605456154562545635456454565545665456754568545695457054571545725457354574545755457654577545785457954580545815458254583545845458554586545875458854589545905459154592545935459454595545965459754598545995460054601546025460354604546055460654607546085460954610546115461254613546145461554616546175461854619546205462154622546235462454625546265462754628546295463054631546325463354634546355463654637546385463954640546415464254643546445464554646546475464854649546505465154652546535465454655546565465754658546595466054661546625466354664546655466654667546685466954670546715467254673546745467554676546775467854679546805468154682546835468454685546865468754688546895469054691546925469354694546955469654697546985469954700547015470254703547045470554706547075470854709547105471154712547135471454715547165471754718547195472054721547225472354724547255472654727547285472954730547315473254733547345473554736547375473854739547405474154742547435474454745547465474754748547495475054751547525475354754547555475654757547585475954760547615476254763547645476554766547675476854769547705477154772547735477454775547765477754778547795478054781547825478354784547855478654787547885478954790547915479254793547945479554796547975479854799548005480154802548035480454805548065480754808548095481054811548125481354814548155481654817548185481954820548215482254823548245482554826548275482854829548305483154832548335483454835548365483754838548395484054841548425484354844548455484654847548485484954850548515485254853548545485554856548575485854859548605486154862548635486454865548665486754868548695487054871548725487354874548755487654877548785487954880548815488254883548845488554886548875488854889548905489154892548935489454895548965489754898548995490054901549025490354904549055490654907549085490954910549115491254913549145491554916549175491854919549205492154922549235492454925549265492754928549295493054931549325493354934549355493654937549385493954940549415494254943549445494554946549475494854949549505495154952549535495454955549565495754958549595496054961549625496354964549655496654967549685496954970549715497254973549745497554976549775497854979549805498154982549835498454985549865498754988549895499054991549925499354994549955499654997549985499955000550015500255003550045500555006550075500855009550105501155012550135501455015550165501755018550195502055021550225502355024550255502655027550285502955030550315503255033550345503555036550375503855039550405504155042550435504455045550465504755048550495505055051550525505355054550555505655057550585505955060550615506255063550645506555066550675506855069550705507155072550735507455075550765507755078550795508055081550825508355084550855508655087550885508955090550915509255093550945509555096550975509855099551005510155102551035510455105551065510755108551095511055111551125511355114551155511655117551185511955120551215512255123551245512555126551275512855129551305513155132551335513455135551365513755138551395514055141551425514355144551455514655147551485514955150551515515255153551545515555156551575515855159551605516155162551635516455165551665516755168551695517055171551725517355174551755517655177551785517955180551815518255183551845518555186551875518855189551905519155192551935519455195551965519755198551995520055201552025520355204552055520655207552085520955210552115521255213552145521555216552175521855219552205522155222552235522455225552265522755228552295523055231552325523355234552355523655237552385523955240552415524255243552445524555246552475524855249552505525155252552535525455255552565525755258552595526055261552625526355264552655526655267552685526955270552715527255273552745527555276552775527855279552805528155282552835528455285552865528755288552895529055291552925529355294552955529655297552985529955300553015530255303553045530555306553075530855309553105531155312553135531455315553165531755318553195532055321553225532355324553255532655327553285532955330553315533255333553345533555336553375533855339553405534155342553435534455345553465534755348553495535055351553525535355354553555535655357553585535955360553615536255363553645536555366553675536855369553705537155372553735537455375553765537755378553795538055381553825538355384553855538655387553885538955390553915539255393553945539555396553975539855399554005540155402554035540455405554065540755408554095541055411554125541355414554155541655417554185541955420554215542255423554245542555426554275542855429554305543155432554335543455435554365543755438554395544055441554425544355444554455544655447554485544955450554515545255453554545545555456554575545855459554605546155462554635546455465554665546755468554695547055471554725547355474554755547655477554785547955480554815548255483554845548555486554875548855489554905549155492554935549455495554965549755498554995550055501555025550355504555055550655507555085550955510555115551255513555145551555516555175551855519555205552155522555235552455525555265552755528555295553055531555325553355534555355553655537555385553955540555415554255543555445554555546555475554855549555505555155552555535555455555555565555755558555595556055561555625556355564555655556655567555685556955570555715557255573555745557555576555775557855579555805558155582555835558455585555865558755588555895559055591555925559355594555955559655597555985559955600556015560255603556045560555606556075560855609556105561155612556135561455615556165561755618556195562055621556225562355624556255562655627556285562955630556315563255633556345563555636556375563855639556405564155642556435564455645556465564755648556495565055651556525565355654556555565655657556585565955660556615566255663556645566555666556675566855669556705567155672556735567455675556765567755678556795568055681556825568355684556855568655687556885568955690556915569255693556945569555696556975569855699557005570155702557035570455705557065570755708557095571055711557125571355714557155571655717557185571955720557215572255723557245572555726557275572855729557305573155732557335573455735557365573755738557395574055741557425574355744557455574655747557485574955750557515575255753557545575555756557575575855759557605576155762557635576455765557665576755768557695577055771557725577355774557755577655777557785577955780557815578255783557845578555786557875578855789557905579155792557935579455795557965579755798557995580055801558025580355804558055580655807558085580955810558115581255813558145581555816558175581855819558205582155822558235582455825558265582755828558295583055831558325583355834558355583655837558385583955840558415584255843558445584555846558475584855849558505585155852558535585455855558565585755858558595586055861558625586355864558655586655867558685586955870558715587255873558745587555876558775587855879558805588155882558835588455885558865588755888558895589055891558925589355894558955589655897558985589955900559015590255903559045590555906559075590855909559105591155912559135591455915559165591755918559195592055921559225592355924559255592655927559285592955930559315593255933559345593555936559375593855939559405594155942559435594455945559465594755948559495595055951559525595355954559555595655957559585595955960559615596255963559645596555966559675596855969559705597155972559735597455975559765597755978559795598055981559825598355984559855598655987559885598955990559915599255993559945599555996559975599855999560005600156002560035600456005560065600756008560095601056011560125601356014560155601656017560185601956020560215602256023560245602556026560275602856029560305603156032560335603456035560365603756038560395604056041560425604356044560455604656047560485604956050560515605256053560545605556056560575605856059560605606156062560635606456065560665606756068560695607056071560725607356074560755607656077560785607956080560815608256083560845608556086560875608856089560905609156092560935609456095560965609756098560995610056101561025610356104561055610656107561085610956110561115611256113561145611556116561175611856119561205612156122561235612456125561265612756128561295613056131561325613356134561355613656137561385613956140561415614256143561445614556146561475614856149561505615156152561535615456155561565615756158561595616056161561625616356164561655616656167561685616956170561715617256173561745617556176561775617856179561805618156182561835618456185561865618756188561895619056191561925619356194561955619656197561985619956200562015620256203562045620556206562075620856209562105621156212562135621456215562165621756218562195622056221562225622356224562255622656227562285622956230562315623256233562345623556236562375623856239562405624156242562435624456245562465624756248562495625056251562525625356254562555625656257562585625956260562615626256263562645626556266562675626856269562705627156272562735627456275562765627756278562795628056281562825628356284562855628656287562885628956290562915629256293562945629556296562975629856299563005630156302563035630456305563065630756308563095631056311563125631356314563155631656317563185631956320563215632256323563245632556326563275632856329563305633156332563335633456335563365633756338563395634056341563425634356344563455634656347563485634956350563515635256353563545635556356563575635856359563605636156362563635636456365563665636756368563695637056371563725637356374563755637656377563785637956380563815638256383563845638556386563875638856389563905639156392563935639456395563965639756398563995640056401564025640356404564055640656407564085640956410564115641256413564145641556416564175641856419564205642156422564235642456425564265642756428564295643056431564325643356434564355643656437564385643956440564415644256443564445644556446564475644856449564505645156452564535645456455564565645756458564595646056461564625646356464564655646656467564685646956470564715647256473564745647556476564775647856479564805648156482564835648456485564865648756488564895649056491564925649356494564955649656497564985649956500565015650256503565045650556506565075650856509565105651156512565135651456515565165651756518565195652056521565225652356524565255652656527565285652956530565315653256533565345653556536565375653856539565405654156542565435654456545565465654756548565495655056551565525655356554565555655656557565585655956560565615656256563565645656556566565675656856569565705657156572565735657456575565765657756578565795658056581565825658356584565855658656587565885658956590565915659256593565945659556596565975659856599566005660156602566035660456605566065660756608566095661056611566125661356614566155661656617566185661956620566215662256623566245662556626566275662856629566305663156632566335663456635566365663756638566395664056641566425664356644566455664656647566485664956650566515665256653566545665556656566575665856659566605666156662566635666456665566665666756668566695667056671566725667356674566755667656677566785667956680566815668256683566845668556686566875668856689566905669156692566935669456695566965669756698566995670056701567025670356704567055670656707567085670956710567115671256713567145671556716567175671856719567205672156722567235672456725567265672756728567295673056731567325673356734567355673656737567385673956740567415674256743567445674556746567475674856749567505675156752567535675456755567565675756758567595676056761567625676356764567655676656767567685676956770567715677256773567745677556776567775677856779567805678156782567835678456785567865678756788567895679056791567925679356794567955679656797567985679956800568015680256803568045680556806568075680856809568105681156812568135681456815568165681756818568195682056821568225682356824568255682656827568285682956830568315683256833568345683556836568375683856839568405684156842568435684456845568465684756848568495685056851568525685356854568555685656857568585685956860568615686256863568645686556866568675686856869568705687156872568735687456875568765687756878568795688056881568825688356884568855688656887568885688956890568915689256893568945689556896568975689856899569005690156902569035690456905569065690756908569095691056911569125691356914569155691656917569185691956920569215692256923569245692556926569275692856929569305693156932569335693456935569365693756938569395694056941569425694356944569455694656947569485694956950569515695256953569545695556956569575695856959569605696156962569635696456965569665696756968569695697056971569725697356974569755697656977569785697956980569815698256983569845698556986569875698856989569905699156992569935699456995569965699756998569995700057001570025700357004570055700657007570085700957010570115701257013570145701557016570175701857019570205702157022570235702457025570265702757028570295703057031570325703357034570355703657037570385703957040570415704257043570445704557046570475704857049570505705157052570535705457055570565705757058570595706057061570625706357064570655706657067570685706957070570715707257073570745707557076570775707857079570805708157082570835708457085570865708757088570895709057091570925709357094570955709657097570985709957100571015710257103571045710557106571075710857109571105711157112571135711457115571165711757118571195712057121571225712357124571255712657127571285712957130571315713257133571345713557136571375713857139571405714157142571435714457145571465714757148571495715057151571525715357154571555715657157571585715957160571615716257163571645716557166571675716857169571705717157172571735717457175571765717757178571795718057181571825718357184571855718657187571885718957190571915719257193571945719557196571975719857199572005720157202572035720457205572065720757208572095721057211572125721357214572155721657217572185721957220572215722257223572245722557226572275722857229572305723157232572335723457235572365723757238572395724057241572425724357244572455724657247572485724957250572515725257253572545725557256572575725857259572605726157262572635726457265572665726757268572695727057271572725727357274572755727657277572785727957280572815728257283572845728557286572875728857289572905729157292572935729457295572965729757298572995730057301573025730357304573055730657307573085730957310573115731257313573145731557316573175731857319573205732157322573235732457325573265732757328573295733057331573325733357334573355733657337573385733957340573415734257343573445734557346573475734857349573505735157352573535735457355573565735757358573595736057361573625736357364573655736657367573685736957370573715737257373573745737557376573775737857379573805738157382573835738457385573865738757388573895739057391573925739357394573955739657397573985739957400574015740257403574045740557406574075740857409574105741157412574135741457415574165741757418574195742057421574225742357424574255742657427574285742957430574315743257433574345743557436574375743857439574405744157442574435744457445574465744757448574495745057451574525745357454574555745657457574585745957460574615746257463574645746557466574675746857469574705747157472574735747457475574765747757478574795748057481574825748357484574855748657487574885748957490574915749257493574945749557496574975749857499575005750157502575035750457505575065750757508575095751057511575125751357514575155751657517575185751957520575215752257523575245752557526575275752857529575305753157532575335753457535575365753757538575395754057541575425754357544575455754657547575485754957550575515755257553575545755557556575575755857559575605756157562575635756457565575665756757568575695757057571575725757357574575755757657577575785757957580575815758257583575845758557586575875758857589575905759157592575935759457595575965759757598575995760057601576025760357604576055760657607576085760957610576115761257613576145761557616576175761857619576205762157622576235762457625576265762757628576295763057631576325763357634576355763657637576385763957640576415764257643576445764557646576475764857649576505765157652576535765457655576565765757658576595766057661576625766357664576655766657667576685766957670576715767257673576745767557676576775767857679576805768157682576835768457685576865768757688576895769057691576925769357694576955769657697576985769957700577015770257703577045770557706577075770857709577105771157712577135771457715577165771757718577195772057721577225772357724577255772657727577285772957730577315773257733577345773557736577375773857739577405774157742577435774457745577465774757748577495775057751577525775357754577555775657757577585775957760577615776257763577645776557766577675776857769577705777157772577735777457775577765777757778577795778057781577825778357784577855778657787577885778957790577915779257793577945779557796577975779857799578005780157802578035780457805578065780757808578095781057811578125781357814578155781657817578185781957820578215782257823578245782557826578275782857829578305783157832578335783457835578365783757838578395784057841578425784357844578455784657847578485784957850578515785257853578545785557856578575785857859578605786157862578635786457865578665786757868578695787057871578725787357874578755787657877578785787957880578815788257883578845788557886578875788857889578905789157892578935789457895578965789757898578995790057901579025790357904579055790657907579085790957910579115791257913579145791557916579175791857919579205792157922579235792457925579265792757928579295793057931579325793357934579355793657937579385793957940579415794257943579445794557946579475794857949579505795157952579535795457955579565795757958579595796057961579625796357964579655796657967579685796957970579715797257973579745797557976579775797857979579805798157982579835798457985579865798757988579895799057991579925799357994579955799657997579985799958000580015800258003580045800558006580075800858009580105801158012580135801458015580165801758018580195802058021580225802358024580255802658027580285802958030580315803258033580345803558036580375803858039580405804158042580435804458045580465804758048580495805058051580525805358054580555805658057580585805958060580615806258063580645806558066580675806858069580705807158072580735807458075580765807758078580795808058081580825808358084580855808658087580885808958090580915809258093580945809558096580975809858099581005810158102
  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. console.log('放大',chart,selectionData)
  26109. if (chart.zoomBool== false || chart.zoomBool==undefined) { //true才可以放大
  26110. fireEvent(chart, 'selection', selectionData, function (args) {
  26111. chart.zoom(extend(args, hasPinched ? { animation: false } : null));
  26112. });
  26113. }else{
  26114. fireEvent(chart, 'selection', selectionData);
  26115. }
  26116. }
  26117. if (isNumber(chart.index)) {
  26118. this.selectionMarker = this.selectionMarker.destroy();
  26119. }
  26120. // Reset scaling preview
  26121. if (hasPinched) {
  26122. this.scaleGroups();
  26123. }
  26124. }
  26125. // Reset all. Check isNumber because it may be destroyed on mouse up
  26126. // (#877)
  26127. if (chart && isNumber(chart.index)) {
  26128. css(chart.container, { cursor: chart._cursor });
  26129. chart.cancelClick = this.hasDragged > 10; // #370
  26130. chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
  26131. this.pinchDown = [];
  26132. }
  26133. }
  26134. /**
  26135. * Finds the closest point to a set of coordinates, using the k-d-tree
  26136. * algorithm.
  26137. *
  26138. * @function Highcharts.Pointer#findNearestKDPoint
  26139. *
  26140. * @param {Array<Highcharts.Series>} series
  26141. * All the series to search in.
  26142. *
  26143. * @param {boolean|undefined} shared
  26144. * Whether it is a shared tooltip or not.
  26145. *
  26146. * @param {Highcharts.PointerEventObject} e
  26147. * The pointer event object, containing chart coordinates of the pointer.
  26148. *
  26149. * @return {Highcharts.Point|undefined}
  26150. * The point closest to given coordinates.
  26151. */
  26152. findNearestKDPoint(series, shared, e) {
  26153. let closest;
  26154. /** @private */
  26155. function sort(p1, p2) {
  26156. const isCloserX = p1.distX - p2.distX, isCloser = p1.dist - p2.dist, isAbove = ((p2.series.group && p2.series.group.zIndex) -
  26157. (p1.series.group && p1.series.group.zIndex));
  26158. let result;
  26159. // We have two points which are not in the same place on xAxis
  26160. // and shared tooltip:
  26161. if (isCloserX !== 0 && shared) { // #5721
  26162. result = isCloserX;
  26163. // Points are not exactly in the same place on x/yAxis:
  26164. }
  26165. else if (isCloser !== 0) {
  26166. result = isCloser;
  26167. // The same xAxis and yAxis position, sort by z-index:
  26168. }
  26169. else if (isAbove !== 0) {
  26170. result = isAbove;
  26171. // The same zIndex, sort by array index:
  26172. }
  26173. else {
  26174. result =
  26175. p1.series.index > p2.series.index ?
  26176. -1 :
  26177. 1;
  26178. }
  26179. return result;
  26180. }
  26181. series.forEach(function (s) {
  26182. const noSharedTooltip = s.noSharedTooltip && shared, compareX = (!noSharedTooltip &&
  26183. s.options.findNearestPointBy.indexOf('y') < 0), point = s.searchPoint(e, compareX);
  26184. if ( // Check that we actually found a point on the series.
  26185. isObject(point, true) && point.series &&
  26186. // Use the new point if it is closer.
  26187. (!isObject(closest, true) ||
  26188. (sort(closest, point) > 0))) {
  26189. closest = point;
  26190. }
  26191. });
  26192. return closest;
  26193. }
  26194. /**
  26195. * @private
  26196. * @function Highcharts.Pointer#getChartCoordinatesFromPoint
  26197. */
  26198. getChartCoordinatesFromPoint(point, inverted) {
  26199. const series = point.series, xAxis = series.xAxis, yAxis = series.yAxis, shapeArgs = point.shapeArgs;
  26200. if (xAxis && yAxis) {
  26201. let x = pick(point.clientX, point.plotX);
  26202. let y = point.plotY || 0;
  26203. if (point.isNode &&
  26204. shapeArgs &&
  26205. isNumber(shapeArgs.x) &&
  26206. isNumber(shapeArgs.y)) {
  26207. x = shapeArgs.x;
  26208. y = shapeArgs.y;
  26209. }
  26210. return inverted ? {
  26211. chartX: yAxis.len + yAxis.pos - y,
  26212. chartY: xAxis.len + xAxis.pos - x
  26213. } : {
  26214. chartX: x + xAxis.pos,
  26215. chartY: y + yAxis.pos
  26216. };
  26217. }
  26218. if (shapeArgs && shapeArgs.x && shapeArgs.y) {
  26219. // E.g. pies do not have axes
  26220. return {
  26221. chartX: shapeArgs.x,
  26222. chartY: shapeArgs.y
  26223. };
  26224. }
  26225. }
  26226. /**
  26227. * Return the cached chartPosition if it is available on the Pointer,
  26228. * otherwise find it. Running offset is quite expensive, so it should be
  26229. * avoided when we know the chart hasn't moved.
  26230. *
  26231. * @function Highcharts.Pointer#getChartPosition
  26232. *
  26233. * @return {Highcharts.ChartPositionObject}
  26234. * The offset of the chart container within the page
  26235. */
  26236. getChartPosition() {
  26237. if (this.chartPosition) {
  26238. return this.chartPosition;
  26239. }
  26240. const { container } = this.chart;
  26241. const pos = offset(container);
  26242. this.chartPosition = {
  26243. left: pos.left,
  26244. top: pos.top,
  26245. scaleX: 1,
  26246. scaleY: 1
  26247. };
  26248. const offsetWidth = container.offsetWidth;
  26249. const offsetHeight = container.offsetHeight;
  26250. // #13342 - tooltip was not visible in Chrome, when chart
  26251. // updates height.
  26252. if (offsetWidth > 2 && // #13342
  26253. offsetHeight > 2 // #13342
  26254. ) {
  26255. this.chartPosition.scaleX = pos.width / offsetWidth;
  26256. this.chartPosition.scaleY = pos.height / offsetHeight;
  26257. }
  26258. return this.chartPosition;
  26259. }
  26260. /**
  26261. * Get the click position in terms of axis values.
  26262. *
  26263. * @function Highcharts.Pointer#getCoordinates
  26264. *
  26265. * @param {Highcharts.PointerEventObject} e
  26266. * Pointer event, extended with `chartX` and `chartY` properties.
  26267. *
  26268. * @return {Highcharts.PointerAxisCoordinatesObject}
  26269. * Axis coordinates.
  26270. */
  26271. getCoordinates(e) {
  26272. const coordinates = {
  26273. xAxis: [],
  26274. yAxis: []
  26275. };
  26276. this.chart.axes.forEach(function (axis) {
  26277. coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
  26278. axis: axis,
  26279. value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
  26280. });
  26281. });
  26282. return coordinates;
  26283. }
  26284. /**
  26285. * Calculates what is the current hovered point/points and series.
  26286. *
  26287. * @private
  26288. * @function Highcharts.Pointer#getHoverData
  26289. *
  26290. * @param {Highcharts.Point|undefined} existingHoverPoint
  26291. * The point currently being hovered.
  26292. *
  26293. * @param {Highcharts.Series|undefined} existingHoverSeries
  26294. * The series currently being hovered.
  26295. *
  26296. * @param {Array<Highcharts.Series>} series
  26297. * All the series in the chart.
  26298. *
  26299. * @param {boolean} isDirectTouch
  26300. * Is the pointer directly hovering the point.
  26301. *
  26302. * @param {boolean|undefined} shared
  26303. * Whether it is a shared tooltip or not.
  26304. *
  26305. * @param {Highcharts.PointerEventObject} [e]
  26306. * The triggering event, containing chart coordinates of the pointer.
  26307. *
  26308. * @return {Object}
  26309. * Object containing resulting hover data: hoverPoint, hoverSeries, and
  26310. * hoverPoints.
  26311. */
  26312. getHoverData(existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
  26313. const hoverPoints = [], useExisting = !!(isDirectTouch && existingHoverPoint), filter = function (s) {
  26314. return (s.visible &&
  26315. !(!shared && s.directTouch) && // #3821
  26316. pick(s.options.enableMouseTracking, true));
  26317. };
  26318. let hoverSeries = existingHoverSeries,
  26319. // Which series to look in for the hover point
  26320. searchSeries,
  26321. // Parameters needed for beforeGetHoverData event.
  26322. eventArgs = {
  26323. chartX: e ? e.chartX : void 0,
  26324. chartY: e ? e.chartY : void 0,
  26325. shared: shared
  26326. };
  26327. // Find chart.hoverPane and update filter method in polar.
  26328. fireEvent(this, 'beforeGetHoverData', eventArgs);
  26329. const notSticky = hoverSeries && !hoverSeries.stickyTracking;
  26330. searchSeries = notSticky ?
  26331. // Only search on hovered series if it has stickyTracking false
  26332. [hoverSeries] :
  26333. // Filter what series to look in.
  26334. series.filter((s) => s.stickyTracking &&
  26335. (eventArgs.filter || filter)(s));
  26336. // Use existing hovered point or find the one closest to coordinates.
  26337. const hoverPoint = useExisting || !e ?
  26338. existingHoverPoint :
  26339. this.findNearestKDPoint(searchSeries, shared, e);
  26340. // Assign hover series
  26341. hoverSeries = hoverPoint && hoverPoint.series;
  26342. // If we have a hoverPoint, assign hoverPoints.
  26343. if (hoverPoint) {
  26344. // When tooltip is shared, it displays more than one point
  26345. if (shared && !hoverSeries.noSharedTooltip) {
  26346. searchSeries = series.filter(function (s) {
  26347. return eventArgs.filter ?
  26348. eventArgs.filter(s) : filter(s) && !s.noSharedTooltip;
  26349. });
  26350. // Get all points with the same x value as the hoverPoint
  26351. searchSeries.forEach(function (s) {
  26352. let point = find(s.points, function (p) {
  26353. return p.x === hoverPoint.x && !p.isNull;
  26354. });
  26355. if (isObject(point)) {
  26356. /*
  26357. * Boost returns a minimal point. Convert it to a usable
  26358. * point for tooltip and states.
  26359. */
  26360. if (s.boosted && s.boost) {
  26361. point = s.boost.getPoint(point);
  26362. }
  26363. hoverPoints.push(point);
  26364. }
  26365. });
  26366. }
  26367. else {
  26368. hoverPoints.push(hoverPoint);
  26369. }
  26370. }
  26371. // Check whether the hoverPoint is inside pane we are hovering over.
  26372. eventArgs = { hoverPoint: hoverPoint };
  26373. fireEvent(this, 'afterGetHoverData', eventArgs);
  26374. return {
  26375. hoverPoint: eventArgs.hoverPoint,
  26376. hoverSeries: hoverSeries,
  26377. hoverPoints: hoverPoints
  26378. };
  26379. }
  26380. /**
  26381. * @private
  26382. * @function Highcharts.Pointer#getPointFromEvent
  26383. */
  26384. getPointFromEvent(e) {
  26385. let target = e.target, point;
  26386. while (target && !point) {
  26387. point = target.point;
  26388. target = target.parentNode;
  26389. }
  26390. return point;
  26391. }
  26392. /**
  26393. * @private
  26394. * @function Highcharts.Pointer#onTrackerMouseOut
  26395. */
  26396. onTrackerMouseOut(e) {
  26397. const chart = this.chart;
  26398. const relatedTarget = e.relatedTarget;
  26399. const series = chart.hoverSeries;
  26400. this.isDirectTouch = false;
  26401. if (series &&
  26402. relatedTarget &&
  26403. !series.stickyTracking &&
  26404. !this.inClass(relatedTarget, 'highcharts-tooltip') &&
  26405. (!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465, #5553
  26406. !this.inClass(relatedTarget, 'highcharts-tracker'))) {
  26407. series.onMouseOut();
  26408. }
  26409. }
  26410. /**
  26411. * Utility to detect whether an element has, or has a parent with, a
  26412. * specific class name. Used on detection of tracker objects and on deciding
  26413. * whether hovering the tooltip should cause the active series to mouse out.
  26414. *
  26415. * @function Highcharts.Pointer#inClass
  26416. *
  26417. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  26418. * The element to investigate.
  26419. *
  26420. * @param {string} className
  26421. * The class name to look for.
  26422. *
  26423. * @return {boolean|undefined}
  26424. * True if either the element or one of its parents has the given class
  26425. * name.
  26426. */
  26427. inClass(element, className) {
  26428. let elem = element, elemClassName;
  26429. while (elem) {
  26430. elemClassName = attr(elem, 'class');
  26431. if (elemClassName) {
  26432. if (elemClassName.indexOf(className) !== -1) {
  26433. return true;
  26434. }
  26435. if (elemClassName.indexOf('highcharts-container') !== -1) {
  26436. return false;
  26437. }
  26438. }
  26439. elem = elem.parentElement;
  26440. }
  26441. }
  26442. /**
  26443. * Initialize the Pointer.
  26444. *
  26445. * @private
  26446. * @function Highcharts.Pointer#init
  26447. *
  26448. * @param {Highcharts.Chart} chart
  26449. * The Chart instance.
  26450. *
  26451. * @param {Highcharts.Options} options
  26452. * The root options object. The pointer uses options from the chart and
  26453. * tooltip structures.
  26454. */
  26455. init(chart, options) {
  26456. // Store references
  26457. this.options = options;
  26458. this.chart = chart;
  26459. // Do we need to handle click on a touch device?
  26460. this.runChartClick = Boolean(options.chart.events && options.chart.events.click);
  26461. this.pinchDown = [];
  26462. this.lastValidTouch = {};
  26463. this.setDOMEvents();
  26464. fireEvent(this, 'afterInit');
  26465. }
  26466. /**
  26467. * Takes a browser event object and extends it with custom Highcharts
  26468. * properties `chartX` and `chartY` in order to work on the internal
  26469. * coordinate system.
  26470. *
  26471. * On map charts, the properties `lon` and `lat` are added to the event
  26472. * object given that the chart has projection information.
  26473. *
  26474. * @function Highcharts.Pointer#normalize
  26475. *
  26476. * @param {global.MouseEvent|global.PointerEvent|global.TouchEvent} e
  26477. * Event object in standard browsers.
  26478. *
  26479. * @param {Highcharts.OffsetObject} [chartPosition]
  26480. * Additional chart offset.
  26481. *
  26482. * @return {Highcharts.PointerEventObject}
  26483. * A browser event with extended properties `chartX` and `chartY`.
  26484. */
  26485. normalize(e, chartPosition) {
  26486. const touches = e.touches;
  26487. // iOS (#2757)
  26488. const ePos = (touches ?
  26489. touches.length ?
  26490. touches.item(0) :
  26491. (pick(// #13534
  26492. touches.changedTouches, e.changedTouches))[0] :
  26493. e);
  26494. // Get mouse position
  26495. if (!chartPosition) {
  26496. chartPosition = this.getChartPosition();
  26497. }
  26498. let chartX = ePos.pageX - chartPosition.left, chartY = ePos.pageY - chartPosition.top;
  26499. // #11329 - when there is scaling on a parent element, we need to take
  26500. // this into account
  26501. chartX /= chartPosition.scaleX;
  26502. chartY /= chartPosition.scaleY;
  26503. return extend(e, {
  26504. chartX: Math.round(chartX),
  26505. chartY: Math.round(chartY)
  26506. });
  26507. }
  26508. /**
  26509. * @private
  26510. * @function Highcharts.Pointer#onContainerClick
  26511. */
  26512. onContainerClick(e) {
  26513. const chart = this.chart;
  26514. const hoverPoint = chart.hoverPoint;
  26515. const pEvt = this.normalize(e);
  26516. const plotLeft = chart.plotLeft;
  26517. const plotTop = chart.plotTop;
  26518. if (!chart.cancelClick) {
  26519. // On tracker click, fire the series and point events. #783, #1583
  26520. if (hoverPoint &&
  26521. this.inClass(pEvt.target, 'highcharts-tracker')) {
  26522. // the series click event
  26523. fireEvent(hoverPoint.series, 'click', extend(pEvt, {
  26524. point: hoverPoint
  26525. }));
  26526. // the point click event
  26527. if (chart.hoverPoint) { // it may be destroyed (#1844)
  26528. hoverPoint.firePointEvent('click', pEvt);
  26529. }
  26530. // When clicking outside a tracker, fire a chart event
  26531. }
  26532. else {
  26533. extend(pEvt, this.getCoordinates(pEvt));
  26534. // fire a click event in the chart
  26535. if (chart.isInsidePlot(pEvt.chartX - plotLeft, pEvt.chartY - plotTop, {
  26536. visiblePlotOnly: true
  26537. })) {
  26538. fireEvent(chart, 'click', pEvt);
  26539. }
  26540. }
  26541. }
  26542. }
  26543. /**
  26544. * @private
  26545. * @function Highcharts.Pointer#onContainerMouseDown
  26546. */
  26547. onContainerMouseDown(e) {
  26548. const isPrimaryButton = ((e.buttons || e.button) & 1) === 1;
  26549. e = this.normalize(e);
  26550. // #11635, Firefox does not reliably fire move event after click scroll
  26551. if (H.isFirefox &&
  26552. e.button !== 0) {
  26553. this.onContainerMouseMove(e);
  26554. }
  26555. // #11635, limiting to primary button
  26556. if (typeof e.button === 'undefined' ||
  26557. isPrimaryButton) {
  26558. this.zoomOption(e);
  26559. // #295, #13737 solve conflict between container drag and chart zoom
  26560. if (isPrimaryButton &&
  26561. e.preventDefault) {
  26562. e.preventDefault();
  26563. }
  26564. this.dragStart(e);
  26565. }
  26566. }
  26567. /**
  26568. * When mouse leaves the container, hide the tooltip.
  26569. * @private
  26570. * @function Highcharts.Pointer#onContainerMouseLeave
  26571. */
  26572. onContainerMouseLeave(e) {
  26573. const chart = charts[pick(Pointer.hoverChartIndex, -1)];
  26574. e = this.normalize(e);
  26575. // #4886, MS Touch end fires mouseleave but with no related target
  26576. if (chart &&
  26577. e.relatedTarget &&
  26578. !this.inClass(e.relatedTarget, 'highcharts-tooltip')) {
  26579. chart.pointer.reset();
  26580. // Also reset the chart position, used in #149 fix
  26581. chart.pointer.chartPosition = void 0;
  26582. }
  26583. }
  26584. /**
  26585. * When mouse enters the container, delete pointer's chartPosition.
  26586. * @private
  26587. * @function Highcharts.Pointer#onContainerMouseEnter
  26588. */
  26589. onContainerMouseEnter(e) {
  26590. delete this.chartPosition;
  26591. }
  26592. /**
  26593. * The mousemove, touchmove and touchstart event handler
  26594. * @private
  26595. * @function Highcharts.Pointer#onContainerMouseMove
  26596. */
  26597. onContainerMouseMove(e) {
  26598. const chart = this.chart, tooltip = chart.tooltip, pEvt = this.normalize(e);
  26599. this.setHoverChartIndex();
  26600. if (chart.mouseIsDown === 'mousedown' || this.touchSelect(pEvt)) {
  26601. this.drag(pEvt);
  26602. }
  26603. // Show the tooltip and run mouse over events (#977)
  26604. if (!chart.openMenu &&
  26605. (this.inClass(pEvt.target, 'highcharts-tracker') ||
  26606. chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
  26607. visiblePlotOnly: true
  26608. })) &&
  26609. // If the tooltip has stickOnContact enabled, do nothing. This
  26610. // applies regardless of any combinations of the `split` and
  26611. // `useHTML` options.
  26612. !(tooltip &&
  26613. tooltip.shouldStickOnContact(pEvt))) {
  26614. if (this.inClass(pEvt.target, 'highcharts-no-tooltip')) {
  26615. this.reset(false, 0);
  26616. }
  26617. else {
  26618. this.runPointActions(pEvt);
  26619. }
  26620. }
  26621. }
  26622. /**
  26623. * @private
  26624. * @function Highcharts.Pointer#onDocumentTouchEnd
  26625. */
  26626. onDocumentTouchEnd(e) {
  26627. const hoverChart = charts[pick(Pointer.hoverChartIndex, -1)];
  26628. if (hoverChart) {
  26629. hoverChart.pointer.drop(e);
  26630. }
  26631. }
  26632. /**
  26633. * @private
  26634. * @function Highcharts.Pointer#onContainerTouchMove
  26635. */
  26636. onContainerTouchMove(e) {
  26637. if (this.touchSelect(e)) {
  26638. this.onContainerMouseMove(e);
  26639. }
  26640. else {
  26641. this.touch(e);
  26642. }
  26643. }
  26644. /**
  26645. * @private
  26646. * @function Highcharts.Pointer#onContainerTouchStart
  26647. */
  26648. onContainerTouchStart(e) {
  26649. if (this.touchSelect(e)) {
  26650. this.onContainerMouseDown(e);
  26651. }
  26652. else {
  26653. this.zoomOption(e);
  26654. this.touch(e, true);
  26655. }
  26656. }
  26657. /**
  26658. * Special handler for mouse move that will hide the tooltip when the mouse
  26659. * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
  26660. * always fire.
  26661. * @private
  26662. * @function Highcharts.Pointer#onDocumentMouseMove
  26663. */
  26664. onDocumentMouseMove(e) {
  26665. const chart = this.chart;
  26666. const tooltip = chart.tooltip;
  26667. const chartPosition = this.chartPosition;
  26668. const pEvt = this.normalize(e, chartPosition);
  26669. // If we're outside, hide the tooltip
  26670. if (chartPosition &&
  26671. !chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
  26672. visiblePlotOnly: true
  26673. }) &&
  26674. !(tooltip &&
  26675. tooltip.shouldStickOnContact(pEvt)) &&
  26676. !this.inClass(pEvt.target, 'highcharts-tracker')) {
  26677. this.reset();
  26678. }
  26679. }
  26680. /**
  26681. * @private
  26682. * @function Highcharts.Pointer#onDocumentMouseUp
  26683. */
  26684. onDocumentMouseUp(e) {
  26685. const chart = charts[pick(Pointer.hoverChartIndex, -1)];
  26686. if (chart) {
  26687. chart.pointer.drop(e);
  26688. }
  26689. }
  26690. /**
  26691. * Handle touch events with two touches
  26692. * @private
  26693. * @function Highcharts.Pointer#pinch
  26694. */
  26695. pinch(e) {
  26696. 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') &&
  26697. chart.runTrackerClick) ||
  26698. self.runChartClick), clip = {}, tooltip = self.chart.tooltip, followTouchMove = touchesLength === 1 &&
  26699. pick((tooltip && tooltip.options.followTouchMove), true);
  26700. let selectionMarker = self.selectionMarker;
  26701. // Don't initiate panning until the user has pinched. This prevents us
  26702. // from blocking page scrolling as users scroll down a long page
  26703. // (#4210).
  26704. if (touchesLength > 1) {
  26705. self.initiated = true;
  26706. }
  26707. else if (followTouchMove) {
  26708. // #16119: Prevent blocking scroll when single-finger panning is
  26709. // not enabled
  26710. self.initiated = false;
  26711. }
  26712. // On touch devices, only proceed to trigger click if a handler is
  26713. // defined
  26714. if (hasZoom &&
  26715. self.initiated &&
  26716. !fireClickEvent &&
  26717. e.cancelable !== false) {
  26718. e.preventDefault();
  26719. }
  26720. // Normalize each touch
  26721. [].map.call(touches, function (e) {
  26722. return self.normalize(e);
  26723. });
  26724. // Register the touch start position
  26725. if (e.type === 'touchstart') {
  26726. [].forEach.call(touches, function (e, i) {
  26727. pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
  26728. });
  26729. lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
  26730. pinchDown[1].chartX];
  26731. lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
  26732. pinchDown[1].chartY];
  26733. // Identify the data bounds in pixels
  26734. chart.axes.forEach(function (axis) {
  26735. if (axis.zoomEnabled) {
  26736. 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);
  26737. // Store the bounds for use in the touchmove handler
  26738. bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
  26739. bounds.max = Math.max(axis.pos + axis.len, absMax + minPixelPadding);
  26740. }
  26741. });
  26742. self.res = true; // reset on next move
  26743. // Optionally move the tooltip on touchmove
  26744. }
  26745. else if (followTouchMove) {
  26746. this.runPointActions(self.normalize(e));
  26747. // Event type is touchmove, handle panning and pinching
  26748. }
  26749. else if (pinchDown.length) { // can be 0 when releasing, if touchend
  26750. // fires first
  26751. fireEvent(chart, 'touchpan', { originalEvent: e }, () => {
  26752. // Set the marker
  26753. if (!selectionMarker) {
  26754. // @todo It's a mock object, so maybe we need a separate
  26755. // interface
  26756. self.selectionMarker = selectionMarker = extend({
  26757. destroy: noop,
  26758. touch: true
  26759. }, chart.plotBox);
  26760. }
  26761. self.pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26762. self.hasPinched = hasZoom;
  26763. // Scale and translate the groups to provide visual feedback
  26764. // during pinching
  26765. self.scaleGroups(transform, clip);
  26766. });
  26767. if (self.res) {
  26768. self.res = false;
  26769. this.reset(false, 0);
  26770. }
  26771. }
  26772. }
  26773. /**
  26774. * Run translation operations
  26775. * @private
  26776. * @function Highcharts.Pointer#pinchTranslate
  26777. */
  26778. pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
  26779. if (this.zoomHor) {
  26780. this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26781. }
  26782. if (this.zoomVert) {
  26783. this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  26784. }
  26785. }
  26786. /**
  26787. * Run translation operations for each direction (horizontal and vertical)
  26788. * independently.
  26789. * @private
  26790. * @function Highcharts.Pointer#pinchTranslateDirection
  26791. */
  26792. pinchTranslateDirection(horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) {
  26793. 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 () {
  26794. // Don't zoom if fingers are too close on this axis
  26795. if (typeof touch1Now === 'number' &&
  26796. Math.abs(touch0Start - touch1Start) > 20) {
  26797. scale = forcedScale ||
  26798. Math.abs(touch0Now - touch1Now) /
  26799. Math.abs(touch0Start - touch1Start);
  26800. }
  26801. clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
  26802. selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
  26803. };
  26804. let selectionWH, selectionXY, clipXY, scale = forcedScale || 1, touch0Now = touches[0][sChartXY], touch1Now = !singleTouch && touches[1][sChartXY], outOfBounds;
  26805. // Set the scale, first pass
  26806. setScale();
  26807. // The clip position (x or y) is altered if out of bounds, the selection
  26808. // position is not
  26809. selectionXY = clipXY;
  26810. // Out of bounds
  26811. if (selectionXY < bounds.min) {
  26812. selectionXY = bounds.min;
  26813. outOfBounds = true;
  26814. }
  26815. else if (selectionXY + selectionWH > bounds.max) {
  26816. selectionXY = bounds.max - selectionWH;
  26817. outOfBounds = true;
  26818. }
  26819. // Is the chart dragged off its bounds, determined by dataMin and
  26820. // dataMax?
  26821. if (outOfBounds) {
  26822. // Modify the touchNow position in order to create an elastic drag
  26823. // movement. This indicates to the user that the chart is responsive
  26824. // but can't be dragged further.
  26825. touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
  26826. if (typeof touch1Now === 'number') {
  26827. touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
  26828. }
  26829. // Set the scale, second pass to adapt to the modified touchNow
  26830. // positions
  26831. setScale();
  26832. }
  26833. else {
  26834. lastValidTouch[xy] = [touch0Now, touch1Now];
  26835. }
  26836. // Set geometry for clipping, selection and transformation
  26837. if (!inverted) {
  26838. clip[xy] = clipXY - plotLeftTop;
  26839. clip[wh] = selectionWH;
  26840. }
  26841. const scaleKey = inverted ?
  26842. (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
  26843. const transformScale = inverted ? 1 / scale : scale;
  26844. selectionMarker[wh] = selectionWH;
  26845. selectionMarker[xy] = selectionXY;
  26846. transform[scaleKey] = scale;
  26847. transform['translate' + XY] = (transformScale * plotLeftTop) +
  26848. (touch0Now - (transformScale * touch0Start));
  26849. }
  26850. /**
  26851. * Reset the tracking by hiding the tooltip, the hover series state and the
  26852. * hover point
  26853. *
  26854. * @function Highcharts.Pointer#reset
  26855. *
  26856. * @param {boolean} [allowMove]
  26857. * Instead of destroying the tooltip altogether, allow moving it if
  26858. * possible.
  26859. *
  26860. * @param {number} [delay]
  26861. */
  26862. reset(allowMove, delay) {
  26863. const pointer = this, chart = pointer.chart, hoverSeries = chart.hoverSeries, hoverPoint = chart.hoverPoint, hoverPoints = chart.hoverPoints, tooltip = chart.tooltip, tooltipPoints = tooltip && tooltip.shared ?
  26864. hoverPoints :
  26865. hoverPoint;
  26866. // Check if the points have moved outside the plot area (#1003, #4736,
  26867. // #5101)
  26868. if (allowMove && tooltipPoints) {
  26869. splat(tooltipPoints).forEach(function (point) {
  26870. if (point.series.isCartesian &&
  26871. typeof point.plotX === 'undefined') {
  26872. allowMove = false;
  26873. }
  26874. });
  26875. }
  26876. // Just move the tooltip, #349
  26877. if (allowMove) {
  26878. if (tooltip && tooltipPoints && splat(tooltipPoints).length) {
  26879. tooltip.refresh(tooltipPoints);
  26880. if (tooltip.shared && hoverPoints) { // #8284
  26881. hoverPoints.forEach(function (point) {
  26882. point.setState(point.state, true);
  26883. if (point.series.isCartesian) {
  26884. if (point.series.xAxis.crosshair) {
  26885. point.series.xAxis
  26886. .drawCrosshair(null, point);
  26887. }
  26888. if (point.series.yAxis.crosshair) {
  26889. point.series.yAxis
  26890. .drawCrosshair(null, point);
  26891. }
  26892. }
  26893. });
  26894. }
  26895. else if (hoverPoint) { // #2500
  26896. hoverPoint.setState(hoverPoint.state, true);
  26897. chart.axes.forEach(function (axis) {
  26898. if (axis.crosshair &&
  26899. hoverPoint.series[axis.coll] === axis) {
  26900. axis.drawCrosshair(null, hoverPoint);
  26901. }
  26902. });
  26903. }
  26904. }
  26905. // Full reset
  26906. }
  26907. else {
  26908. if (hoverPoint) {
  26909. hoverPoint.onMouseOut();
  26910. }
  26911. if (hoverPoints) {
  26912. hoverPoints.forEach(function (point) {
  26913. point.setState();
  26914. });
  26915. }
  26916. if (hoverSeries) {
  26917. hoverSeries.onMouseOut();
  26918. }
  26919. if (tooltip) {
  26920. tooltip.hide(delay);
  26921. }
  26922. if (pointer.unDocMouseMove) {
  26923. pointer.unDocMouseMove = pointer.unDocMouseMove();
  26924. }
  26925. // Remove crosshairs
  26926. chart.axes.forEach(function (axis) {
  26927. axis.hideCrosshair();
  26928. });
  26929. pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
  26930. }
  26931. }
  26932. /**
  26933. * With line type charts with a single tracker, get the point closest to the
  26934. * mouse. Run Point.onMouseOver and display tooltip for the point or points.
  26935. *
  26936. * @private
  26937. * @function Highcharts.Pointer#runPointActions
  26938. *
  26939. * @emits Highcharts.Point#event:mouseOut
  26940. * @emits Highcharts.Point#event:mouseOver
  26941. */
  26942. runPointActions(e, p, force) {
  26943. const pointer = this, chart = pointer.chart, series = chart.series, tooltip = (chart.tooltip && chart.tooltip.options.enabled ?
  26944. chart.tooltip :
  26945. void 0), shared = (tooltip ?
  26946. tooltip.shared :
  26947. false);
  26948. let hoverPoint = p || chart.hoverPoint, hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries;
  26949. const // onMouseOver or already hovering a series with directTouch
  26950. isDirectTouch = (!e || e.type !== 'touchmove') && (!!p || ((hoverSeries && hoverSeries.directTouch) &&
  26951. pointer.isDirectTouch)), hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e);
  26952. // Update variables from hoverData.
  26953. hoverPoint = hoverData.hoverPoint;
  26954. hoverSeries = hoverData.hoverSeries;
  26955. const points = hoverData.hoverPoints, followPointer = hoverSeries &&
  26956. hoverSeries.tooltipOptions.followPointer &&
  26957. !hoverSeries.tooltipOptions.split, useSharedTooltip = (shared &&
  26958. hoverSeries &&
  26959. !hoverSeries.noSharedTooltip);
  26960. // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
  26961. // #3926, #4200
  26962. if (hoverPoint &&
  26963. (force ||
  26964. hoverPoint !== chart.hoverPoint ||
  26965. (tooltip && tooltip.isHidden))) {
  26966. (chart.hoverPoints || []).forEach(function (p) {
  26967. if (points.indexOf(p) === -1) {
  26968. p.setState();
  26969. }
  26970. });
  26971. // Set normal state to previous series
  26972. if (chart.hoverSeries !== hoverSeries) {
  26973. hoverSeries.onMouseOver();
  26974. }
  26975. pointer.applyInactiveState(points);
  26976. // Do mouseover on all points (#3919, #3985, #4410, #5622)
  26977. (points || []).forEach(function (p) {
  26978. p.setState('hover');
  26979. });
  26980. // If tracking is on series in stead of on each point,
  26981. // fire mouseOver on hover point. // #4448
  26982. if (chart.hoverPoint) {
  26983. chart.hoverPoint.firePointEvent('mouseOut');
  26984. }
  26985. // Hover point may have been destroyed in the event handlers (#7127)
  26986. if (!hoverPoint.series) {
  26987. return;
  26988. }
  26989. /**
  26990. * Contains all hovered points.
  26991. *
  26992. * @name Highcharts.Chart#hoverPoints
  26993. * @type {Array<Highcharts.Point>|null}
  26994. */
  26995. chart.hoverPoints = points;
  26996. /**
  26997. * Contains the original hovered point.
  26998. *
  26999. * @name Highcharts.Chart#hoverPoint
  27000. * @type {Highcharts.Point|null}
  27001. */
  27002. chart.hoverPoint = hoverPoint;
  27003. /**
  27004. * Hover state should not be lost when axis is updated (#12569)
  27005. * Axis.update runs pointer.reset which uses chart.hoverPoint.state
  27006. * to apply state which does not exist in hoverPoint yet.
  27007. * The mouseOver event should be triggered when hoverPoint
  27008. * is correct.
  27009. */
  27010. hoverPoint.firePointEvent('mouseOver', void 0, () => {
  27011. // Draw tooltip if necessary
  27012. if (tooltip && hoverPoint) {
  27013. tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
  27014. }
  27015. });
  27016. // Update positions (regardless of kdpoint or hoverPoint)
  27017. }
  27018. else if (followPointer && tooltip && !tooltip.isHidden) {
  27019. const anchor = tooltip.getAnchor([{}], e);
  27020. if (chart.isInsidePlot(anchor[0], anchor[1], {
  27021. visiblePlotOnly: true
  27022. })) {
  27023. tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
  27024. }
  27025. }
  27026. // Start the event listener to pick up the tooltip and crosshairs
  27027. if (!pointer.unDocMouseMove) {
  27028. pointer.unDocMouseMove = addEvent(chart.container.ownerDocument, 'mousemove', function (e) {
  27029. const chart = charts[Pointer.hoverChartIndex];
  27030. if (chart) {
  27031. chart.pointer.onDocumentMouseMove(e);
  27032. }
  27033. });
  27034. pointer.eventsToUnbind.push(pointer.unDocMouseMove);
  27035. }
  27036. // Issues related to crosshair #4927, #5269 #5066, #5658
  27037. chart.axes.forEach(function drawAxisCrosshair(axis) {
  27038. const snap = pick((axis.crosshair || {}).snap, true);
  27039. let point;
  27040. if (snap) {
  27041. point = chart.hoverPoint; // #13002
  27042. if (!point || point.series[axis.coll] !== axis) {
  27043. point = find(points, (p) => p.series && p.series[axis.coll] === axis);
  27044. }
  27045. }
  27046. // Axis has snapping crosshairs, and one of the hover points belongs
  27047. // to axis. Always call drawCrosshair when it is not snap.
  27048. if (point || !snap) {
  27049. axis.drawCrosshair(e, point);
  27050. // Axis has snapping crosshairs, but no hover point belongs to axis
  27051. }
  27052. else {
  27053. axis.hideCrosshair();
  27054. }
  27055. });
  27056. }
  27057. /**
  27058. * Scale series groups to a certain scale and translation.
  27059. * @private
  27060. * @function Highcharts.Pointer#scaleGroups
  27061. */
  27062. scaleGroups(attribs, clip) {
  27063. const chart = this.chart;
  27064. // Scale each series
  27065. chart.series.forEach(function (series) {
  27066. const seriesAttribs = attribs || series.getPlotBox(); // #1701
  27067. if (series.group &&
  27068. ((series.xAxis && series.xAxis.zoomEnabled) ||
  27069. chart.mapView)) {
  27070. series.group.attr(seriesAttribs);
  27071. if (series.markerGroup) {
  27072. series.markerGroup.attr(seriesAttribs);
  27073. series.markerGroup.clip(clip ? chart.clipRect : null);
  27074. }
  27075. if (series.dataLabelsGroup) {
  27076. series.dataLabelsGroup.attr(seriesAttribs);
  27077. }
  27078. }
  27079. });
  27080. // Clip
  27081. chart.clipRect.attr(clip || chart.clipBox);
  27082. }
  27083. /**
  27084. * Set the JS DOM events on the container and document. This method should
  27085. * contain a one-to-one assignment between methods and their handlers. Any
  27086. * advanced logic should be moved to the handler reflecting the event's
  27087. * name.
  27088. * @private
  27089. * @function Highcharts.Pointer#setDOMEvents
  27090. */
  27091. setDOMEvents() {
  27092. const container = this.chart.container, ownerDoc = container.ownerDocument;
  27093. container.onmousedown = this.onContainerMouseDown.bind(this);
  27094. container.onmousemove = this.onContainerMouseMove.bind(this);
  27095. container.onclick = this.onContainerClick.bind(this);
  27096. this.eventsToUnbind.push(addEvent(container, 'mouseenter', this.onContainerMouseEnter.bind(this)));
  27097. this.eventsToUnbind.push(addEvent(container, 'mouseleave', this.onContainerMouseLeave.bind(this)));
  27098. if (!Pointer.unbindDocumentMouseUp) {
  27099. Pointer.unbindDocumentMouseUp = addEvent(ownerDoc, 'mouseup', this.onDocumentMouseUp.bind(this));
  27100. }
  27101. // In case we are dealing with overflow, reset the chart position when
  27102. // scrolling parent elements
  27103. let parent = this.chart.renderTo.parentElement;
  27104. while (parent && parent.tagName !== 'BODY') {
  27105. this.eventsToUnbind.push(addEvent(parent, 'scroll', () => {
  27106. delete this.chartPosition;
  27107. }));
  27108. parent = parent.parentElement;
  27109. }
  27110. if (H.hasTouch) {
  27111. this.eventsToUnbind.push(addEvent(container, 'touchstart', this.onContainerTouchStart.bind(this), { passive: false }));
  27112. this.eventsToUnbind.push(addEvent(container, 'touchmove', this.onContainerTouchMove.bind(this), { passive: false }));
  27113. if (!Pointer.unbindDocumentTouchEnd) {
  27114. Pointer.unbindDocumentTouchEnd = addEvent(ownerDoc, 'touchend', this.onDocumentTouchEnd.bind(this), { passive: false });
  27115. }
  27116. }
  27117. }
  27118. /**
  27119. * Sets the index of the hovered chart and leaves the previous hovered
  27120. * chart, to reset states like tooltip.
  27121. * @private
  27122. * @function Highcharts.Pointer#setHoverChartIndex
  27123. */
  27124. setHoverChartIndex() {
  27125. const chart = this.chart;
  27126. const hoverChart = H.charts[pick(Pointer.hoverChartIndex, -1)];
  27127. if (hoverChart &&
  27128. hoverChart !== chart) {
  27129. hoverChart.pointer.onContainerMouseLeave({ relatedTarget: chart.container });
  27130. }
  27131. if (!hoverChart ||
  27132. !hoverChart.mouseIsDown) {
  27133. Pointer.hoverChartIndex = chart.index;
  27134. }
  27135. }
  27136. /**
  27137. * General touch handler shared by touchstart and touchmove.
  27138. * @private
  27139. * @function Highcharts.Pointer#touch
  27140. */
  27141. touch(e, start) {
  27142. const chart = this.chart;
  27143. let hasMoved, pinchDown, isInside;
  27144. this.setHoverChartIndex();
  27145. if (e.touches.length === 1) {
  27146. e = this.normalize(e);
  27147. isInside = chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop, {
  27148. visiblePlotOnly: true
  27149. });
  27150. if (isInside && !chart.openMenu) {
  27151. // Run mouse events and display tooltip etc
  27152. if (start) {
  27153. this.runPointActions(e);
  27154. }
  27155. // Android fires touchmove events after the touchstart even if
  27156. // the finger hasn't moved, or moved only a pixel or two. In iOS
  27157. // however, the touchmove doesn't fire unless the finger moves
  27158. // more than ~4px. So we emulate this behaviour in Android by
  27159. // checking how much it moved, and cancelling on small
  27160. // distances. #3450.
  27161. if (e.type === 'touchmove') {
  27162. pinchDown = this.pinchDown;
  27163. hasMoved = pinchDown[0] ? Math.sqrt(// #5266
  27164. Math.pow(pinchDown[0].chartX - e.chartX, 2) +
  27165. Math.pow(pinchDown[0].chartY - e.chartY, 2)) >= 4 : false;
  27166. }
  27167. if (pick(hasMoved, true)) {
  27168. this.pinch(e);
  27169. }
  27170. }
  27171. else if (start) {
  27172. // Hide the tooltip on touching outside the plot area (#1203)
  27173. this.reset();
  27174. }
  27175. }
  27176. else if (e.touches.length === 2) {
  27177. this.pinch(e);
  27178. }
  27179. }
  27180. /**
  27181. * Returns true if the chart is set up for zooming by single touch and the
  27182. * event is capable
  27183. * @private
  27184. * @function Highcharts.Pointer#touchSelect
  27185. */
  27186. touchSelect(e) {
  27187. return Boolean(this.chart.zooming.singleTouch &&
  27188. e.touches &&
  27189. e.touches.length === 1);
  27190. }
  27191. /**
  27192. * Resolve the zoomType option, this is reset on all touch start and mouse
  27193. * down events.
  27194. * @private
  27195. * @function Highcharts.Pointer#zoomOption
  27196. */
  27197. zoomOption(e) {
  27198. const chart = this.chart, options = chart.options.chart, inverted = chart.inverted;
  27199. let zoomType = chart.zooming.type || '', zoomX, zoomY;
  27200. // Look for the pinchType option
  27201. if (/touch/.test(e.type)) {
  27202. zoomType = pick(chart.zooming.pinchType, zoomType);
  27203. }
  27204. this.zoomX = zoomX = /x/.test(zoomType);
  27205. this.zoomY = zoomY = /y/.test(zoomType);
  27206. this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
  27207. this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
  27208. this.hasZoom = zoomX || zoomY;
  27209. }
  27210. }
  27211. /* *
  27212. *
  27213. * Class Namespace
  27214. *
  27215. * */
  27216. (function (Pointer) {
  27217. /* *
  27218. *
  27219. * Declarations
  27220. *
  27221. * */
  27222. /* *
  27223. *
  27224. * Constants
  27225. *
  27226. * */
  27227. const composedEvents = [];
  27228. const composedMembers = [];
  27229. /* *
  27230. *
  27231. * Functions
  27232. *
  27233. * */
  27234. /**
  27235. * @private
  27236. */
  27237. function compose(ChartClass) {
  27238. if (U.pushUnique(composedMembers, ChartClass)) {
  27239. addEvent(ChartClass, 'beforeRender', function () {
  27240. /**
  27241. * The Pointer that keeps track of mouse and touch
  27242. * interaction.
  27243. *
  27244. * @memberof Highcharts.Chart
  27245. * @name pointer
  27246. * @type {Highcharts.Pointer}
  27247. * @instance
  27248. */
  27249. this.pointer = new Pointer(this, this.options);
  27250. });
  27251. }
  27252. }
  27253. Pointer.compose = compose;
  27254. /**
  27255. * @private
  27256. */
  27257. function dissolve() {
  27258. for (let i = 0, iEnd = composedEvents.length; i < iEnd; ++i) {
  27259. composedEvents[i]();
  27260. }
  27261. composedEvents.length = 0;
  27262. }
  27263. Pointer.dissolve = dissolve;
  27264. })(Pointer || (Pointer = {}));
  27265. /* *
  27266. *
  27267. * Default Export
  27268. *
  27269. * */
  27270. /* *
  27271. *
  27272. * API Declarations
  27273. *
  27274. * */
  27275. /**
  27276. * Chart position and scale.
  27277. *
  27278. * @interface Highcharts.ChartPositionObject
  27279. */ /**
  27280. * @name Highcharts.ChartPositionObject#left
  27281. * @type {number}
  27282. */ /**
  27283. * @name Highcharts.ChartPositionObject#scaleX
  27284. * @type {number}
  27285. */ /**
  27286. * @name Highcharts.ChartPositionObject#scaleY
  27287. * @type {number}
  27288. */ /**
  27289. * @name Highcharts.ChartPositionObject#top
  27290. * @type {number}
  27291. */
  27292. /**
  27293. * One position in relation to an axis.
  27294. *
  27295. * @interface Highcharts.PointerAxisCoordinateObject
  27296. */ /**
  27297. * Related axis.
  27298. *
  27299. * @name Highcharts.PointerAxisCoordinateObject#axis
  27300. * @type {Highcharts.Axis}
  27301. */ /**
  27302. * Axis value.
  27303. *
  27304. * @name Highcharts.PointerAxisCoordinateObject#value
  27305. * @type {number}
  27306. */
  27307. /**
  27308. * Positions in terms of axis values.
  27309. *
  27310. * @interface Highcharts.PointerAxisCoordinatesObject
  27311. */ /**
  27312. * Positions on the x-axis.
  27313. * @name Highcharts.PointerAxisCoordinatesObject#xAxis
  27314. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  27315. */ /**
  27316. * Positions on the y-axis.
  27317. * @name Highcharts.PointerAxisCoordinatesObject#yAxis
  27318. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  27319. */
  27320. /**
  27321. * Pointer coordinates.
  27322. *
  27323. * @interface Highcharts.PointerCoordinatesObject
  27324. */ /**
  27325. * @name Highcharts.PointerCoordinatesObject#chartX
  27326. * @type {number}
  27327. */ /**
  27328. * @name Highcharts.PointerCoordinatesObject#chartY
  27329. * @type {number}
  27330. */
  27331. /**
  27332. * A native browser mouse or touch event, extended with position information
  27333. * relative to the {@link Chart.container}.
  27334. *
  27335. * @interface Highcharts.PointerEventObject
  27336. * @extends global.PointerEvent
  27337. */ /**
  27338. * The X coordinate of the pointer interaction relative to the chart.
  27339. *
  27340. * @name Highcharts.PointerEventObject#chartX
  27341. * @type {number}
  27342. */ /**
  27343. * The Y coordinate of the pointer interaction relative to the chart.
  27344. *
  27345. * @name Highcharts.PointerEventObject#chartY
  27346. * @type {number}
  27347. */
  27348. /**
  27349. * Axis-specific data of a selection.
  27350. *
  27351. * @interface Highcharts.SelectDataObject
  27352. */ /**
  27353. * The selected Axis.
  27354. * @name Highcharts.SelectDataObject#axis
  27355. * @type {Highcharts.Axis}
  27356. */ /**
  27357. * The maximum axis value, either automatic or set manually.
  27358. * @name Highcharts.SelectDataObject#max
  27359. * @type {number}
  27360. */ /**
  27361. * The minimum axis value, either automatic or set manually.
  27362. * @name Highcharts.SelectDataObject#min
  27363. * @type {number}
  27364. */
  27365. /**
  27366. * Object for select events.
  27367. * The primary axes are `xAxis[0]` and `yAxis[0]`. Remember the unit of a
  27368. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  27369. *
  27370. * @interface Highcharts.SelectEventObject
  27371. */ /**
  27372. * The related browser event.
  27373. * @name Highcharts.SelectEventObject#originalEvent
  27374. * @type {global.Event}
  27375. */ /**
  27376. * Indicates a reset event to restore default state.
  27377. * @name Highcharts.SelectEventObject#resetSelection
  27378. * @type {boolean|undefined}
  27379. */ /**
  27380. * Arrays containing the axes of each dimension and each axis' min and max
  27381. * values.
  27382. * @name Highcharts.SelectEventObject#xAxis
  27383. * @type {Array<Highcharts.SelectDataObject>}
  27384. */ /**
  27385. * Arrays containing the axes of each dimension and each axis' min and max
  27386. * values.
  27387. * @name Highcharts.SelectEventObject#yAxis
  27388. * @type {Array<Highcharts.SelectDataObject>}
  27389. */
  27390. ''; // keeps doclets above in JS file
  27391. return Pointer;
  27392. });
  27393. _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) {
  27394. /* *
  27395. *
  27396. * (c) 2010-2021 Torstein Honsi
  27397. *
  27398. * License: www.highcharts.com/license
  27399. *
  27400. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  27401. *
  27402. * */
  27403. const { animObject, setAnimation } = A;
  27404. const { format } = F;
  27405. const { marginNames } = H;
  27406. const { distribute } = R;
  27407. const { addEvent, createElement, css, defined, discardElement, find, fireEvent, isNumber, merge, pick, relativeLength, stableSort, syncTimeout } = U;
  27408. /* *
  27409. *
  27410. * Class
  27411. *
  27412. * */
  27413. /**
  27414. * The overview of the chart's series. The legend object is instanciated
  27415. * internally in the chart constructor, and is available from the `chart.legend`
  27416. * property. Each chart has only one legend.
  27417. *
  27418. * @class
  27419. * @name Highcharts.Legend
  27420. *
  27421. * @param {Highcharts.Chart} chart
  27422. * The chart instance.
  27423. *
  27424. * @param {Highcharts.LegendOptions} options
  27425. * Legend options.
  27426. */
  27427. class Legend {
  27428. /* *
  27429. *
  27430. * Constructors
  27431. *
  27432. * */
  27433. constructor(chart, options) {
  27434. /* *
  27435. *
  27436. * Properties
  27437. *
  27438. * */
  27439. this.allItems = [];
  27440. this.box = void 0;
  27441. this.contentGroup = void 0;
  27442. this.display = false;
  27443. this.group = void 0;
  27444. this.initialItemY = 0;
  27445. this.itemHeight = 0;
  27446. this.itemMarginBottom = 0;
  27447. this.itemMarginTop = 0;
  27448. this.itemX = 0;
  27449. this.itemY = 0;
  27450. this.lastItemY = 0;
  27451. this.lastLineHeight = 0;
  27452. this.legendHeight = 0;
  27453. this.legendWidth = 0;
  27454. this.maxItemWidth = 0;
  27455. this.maxLegendWidth = 0;
  27456. this.offsetWidth = 0;
  27457. this.options = void 0;
  27458. this.padding = 0;
  27459. this.pages = [];
  27460. this.proximate = false;
  27461. this.scrollGroup = void 0;
  27462. this.symbolHeight = 0;
  27463. this.symbolWidth = 0;
  27464. this.titleHeight = 0;
  27465. this.totalItemWidth = 0;
  27466. this.widthOption = 0;
  27467. this.chart = chart;
  27468. this.init(chart, options);
  27469. }
  27470. /* *
  27471. *
  27472. * Functions
  27473. *
  27474. * */
  27475. /**
  27476. * Initialize the legend.
  27477. *
  27478. * @private
  27479. * @function Highcharts.Legend#init
  27480. *
  27481. * @param {Highcharts.Chart} chart
  27482. * The chart instance.
  27483. *
  27484. * @param {Highcharts.LegendOptions} options
  27485. * Legend options.
  27486. */
  27487. init(chart, options) {
  27488. /**
  27489. * Chart of this legend.
  27490. *
  27491. * @readonly
  27492. * @name Highcharts.Legend#chart
  27493. * @type {Highcharts.Chart}
  27494. */
  27495. this.chart = chart;
  27496. this.setOptions(options);
  27497. if (options.enabled) {
  27498. // Render it
  27499. this.render();
  27500. // move checkboxes
  27501. addEvent(this.chart, 'endResize', function () {
  27502. this.legend.positionCheckboxes();
  27503. });
  27504. // On Legend.init and Legend.update, make sure that proximate layout
  27505. // events are either added or removed (#18362).
  27506. addEvent(this.chart, 'render', () => {
  27507. if (this.proximate) {
  27508. this.proximatePositions();
  27509. this.positionItems();
  27510. }
  27511. });
  27512. }
  27513. }
  27514. /**
  27515. * @private
  27516. * @function Highcharts.Legend#setOptions
  27517. * @param {Highcharts.LegendOptions} options
  27518. */
  27519. setOptions(options) {
  27520. const padding = pick(options.padding, 8);
  27521. /**
  27522. * Legend options.
  27523. *
  27524. * @readonly
  27525. * @name Highcharts.Legend#options
  27526. * @type {Highcharts.LegendOptions}
  27527. */
  27528. this.options = options;
  27529. if (!this.chart.styledMode) {
  27530. this.itemStyle = options.itemStyle;
  27531. this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle);
  27532. }
  27533. this.itemMarginTop = options.itemMarginTop;
  27534. this.itemMarginBottom = options.itemMarginBottom;
  27535. this.padding = padding;
  27536. this.initialItemY = padding - 5; // 5 is pixels above the text
  27537. this.symbolWidth = pick(options.symbolWidth, 16);
  27538. this.pages = [];
  27539. this.proximate = options.layout === 'proximate' && !this.chart.inverted;
  27540. // #12705: baseline has to be reset on every update
  27541. this.baseline = void 0;
  27542. }
  27543. /**
  27544. * Update the legend with new options. Equivalent to running `chart.update`
  27545. * with a legend configuration option.
  27546. *
  27547. * @sample highcharts/legend/legend-update/
  27548. * Legend update
  27549. *
  27550. * @function Highcharts.Legend#update
  27551. *
  27552. * @param {Highcharts.LegendOptions} options
  27553. * Legend options.
  27554. *
  27555. * @param {boolean} [redraw=true]
  27556. * Whether to redraw the chart after the axis is altered. If doing more
  27557. * operations on the chart, it is a good idea to set redraw to false and
  27558. * call {@link Chart#redraw} after. Whether to redraw the chart.
  27559. *
  27560. * @emits Highcharts.Legends#event:afterUpdate
  27561. */
  27562. update(options, redraw) {
  27563. const chart = this.chart;
  27564. this.setOptions(merge(true, this.options, options));
  27565. this.destroy();
  27566. chart.isDirtyLegend = chart.isDirtyBox = true;
  27567. if (pick(redraw, true)) {
  27568. chart.redraw();
  27569. }
  27570. fireEvent(this, 'afterUpdate');
  27571. }
  27572. /**
  27573. * Set the colors for the legend item.
  27574. *
  27575. * @private
  27576. * @function Highcharts.Legend#colorizeItem
  27577. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27578. * A Series or Point instance
  27579. * @param {boolean} [visible=false]
  27580. * Dimmed or colored
  27581. *
  27582. * @todo
  27583. * Make events official: Fires the event `afterColorizeItem`.
  27584. */
  27585. colorizeItem(item, visible) {
  27586. const { group, label, line, symbol } = item.legendItem || {};
  27587. if (group) {
  27588. group[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
  27589. }
  27590. if (!this.chart.styledMode) {
  27591. const { itemHiddenStyle } = this, hiddenColor = itemHiddenStyle.color, symbolColor = visible ?
  27592. (item.color || hiddenColor) :
  27593. hiddenColor, markerOptions = item.options && item.options.marker;
  27594. let symbolAttr = { fill: symbolColor };
  27595. label === null || label === void 0 ? void 0 : label.css(merge(visible ? this.itemStyle : itemHiddenStyle));
  27596. line === null || line === void 0 ? void 0 : line.attr({ stroke: symbolColor });
  27597. if (symbol) {
  27598. // Apply marker options
  27599. if (markerOptions && symbol.isMarker) { // #585
  27600. symbolAttr = item.pointAttribs();
  27601. if (!visible) {
  27602. // #6769
  27603. symbolAttr.stroke = symbolAttr.fill = hiddenColor;
  27604. }
  27605. }
  27606. symbol.attr(symbolAttr);
  27607. }
  27608. }
  27609. fireEvent(this, 'afterColorizeItem', { item, visible });
  27610. }
  27611. /**
  27612. * @private
  27613. * @function Highcharts.Legend#positionItems
  27614. */
  27615. positionItems() {
  27616. // Now that the legend width and height are established, put the items
  27617. // in the final position
  27618. this.allItems.forEach(this.positionItem, this);
  27619. if (!this.chart.isResizing) {
  27620. this.positionCheckboxes();
  27621. }
  27622. }
  27623. /**
  27624. * Position the legend item.
  27625. *
  27626. * @private
  27627. * @function Highcharts.Legend#positionItem
  27628. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27629. * The item to position
  27630. */
  27631. positionItem(item) {
  27632. const legend = this, { group, x = 0, y = 0 } = item.legendItem || {}, options = legend.options, symbolPadding = options.symbolPadding, ltr = !options.rtl, checkbox = item.checkbox;
  27633. if (group && group.element) {
  27634. const attribs = {
  27635. translateX: ltr ?
  27636. x :
  27637. legend.legendWidth - x - 2 * symbolPadding - 4,
  27638. translateY: y
  27639. };
  27640. const complete = () => {
  27641. fireEvent(this, 'afterPositionItem', { item });
  27642. };
  27643. group[defined(group.translateY) ? 'animate' : 'attr'](attribs, void 0, complete);
  27644. }
  27645. if (checkbox) {
  27646. checkbox.x = x;
  27647. checkbox.y = y;
  27648. }
  27649. }
  27650. /**
  27651. * Destroy a single legend item, used internally on removing series items.
  27652. *
  27653. * @private
  27654. * @function Highcharts.Legend#destroyItem
  27655. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27656. * The item to remove
  27657. */
  27658. destroyItem(item) {
  27659. const checkbox = item.checkbox, legendItem = item.legendItem || {};
  27660. // destroy SVG elements
  27661. for (const key of ['group', 'label', 'line', 'symbol']) {
  27662. if (legendItem[key]) {
  27663. legendItem[key] = legendItem[key].destroy();
  27664. }
  27665. }
  27666. if (checkbox) {
  27667. discardElement(checkbox);
  27668. }
  27669. item.legendItem = void 0;
  27670. }
  27671. /**
  27672. * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
  27673. * must be called after destruction.
  27674. *
  27675. * @private
  27676. * @function Highcharts.Legend#destroy
  27677. */
  27678. destroy() {
  27679. const legend = this;
  27680. // Destroy items
  27681. for (const item of this.getAllItems()) {
  27682. this.destroyItem(item);
  27683. }
  27684. // Destroy legend elements
  27685. for (const key of [
  27686. 'clipRect',
  27687. 'up',
  27688. 'down',
  27689. 'pager',
  27690. 'nav',
  27691. 'box',
  27692. 'title',
  27693. 'group'
  27694. ]) {
  27695. if (legend[key]) {
  27696. legend[key] = legend[key].destroy();
  27697. }
  27698. }
  27699. this.display = null; // Reset in .render on update.
  27700. }
  27701. /**
  27702. * Position the checkboxes after the width is determined.
  27703. *
  27704. * @private
  27705. * @function Highcharts.Legend#positionCheckboxes
  27706. */
  27707. positionCheckboxes() {
  27708. const alignAttr = this.group && this.group.alignAttr, clipHeight = this.clipHeight || this.legendHeight, titleHeight = this.titleHeight;
  27709. let translateY;
  27710. if (alignAttr) {
  27711. translateY = alignAttr.translateY;
  27712. this.allItems.forEach(function (item) {
  27713. const checkbox = item.checkbox;
  27714. let top;
  27715. if (checkbox) {
  27716. top = translateY + titleHeight + checkbox.y +
  27717. (this.scrollOffset || 0) + 3;
  27718. css(checkbox, {
  27719. left: (alignAttr.translateX + item.checkboxOffset +
  27720. checkbox.x - 20) + 'px',
  27721. top: top + 'px',
  27722. display: this.proximate || (top > translateY - 6 &&
  27723. top < translateY + clipHeight - 6) ?
  27724. '' :
  27725. 'none'
  27726. });
  27727. }
  27728. }, this);
  27729. }
  27730. }
  27731. /**
  27732. * Render the legend title on top of the legend.
  27733. *
  27734. * @private
  27735. * @function Highcharts.Legend#renderTitle
  27736. */
  27737. renderTitle() {
  27738. const options = this.options, padding = this.padding, titleOptions = options.title;
  27739. let bBox, titleHeight = 0;
  27740. if (titleOptions.text) {
  27741. if (!this.title) {
  27742. /**
  27743. * SVG element of the legend title.
  27744. *
  27745. * @readonly
  27746. * @name Highcharts.Legend#title
  27747. * @type {Highcharts.SVGElement}
  27748. */
  27749. this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, void 0, void 0, void 0, options.useHTML, void 0, 'legend-title')
  27750. .attr({ zIndex: 1 });
  27751. if (!this.chart.styledMode) {
  27752. this.title.css(titleOptions.style);
  27753. }
  27754. this.title.add(this.group);
  27755. }
  27756. // Set the max title width (#7253)
  27757. if (!titleOptions.width) {
  27758. this.title.css({
  27759. width: this.maxLegendWidth + 'px'
  27760. });
  27761. }
  27762. bBox = this.title.getBBox();
  27763. titleHeight = bBox.height;
  27764. this.offsetWidth = bBox.width; // #1717
  27765. this.contentGroup.attr({ translateY: titleHeight });
  27766. }
  27767. this.titleHeight = titleHeight;
  27768. }
  27769. /**
  27770. * Set the legend item text.
  27771. *
  27772. * @function Highcharts.Legend#setText
  27773. * @param {Highcharts.Point|Highcharts.Series} item
  27774. * The item for which to update the text in the legend.
  27775. */
  27776. setText(item) {
  27777. const options = this.options;
  27778. item.legendItem.label.attr({
  27779. text: options.labelFormat ?
  27780. format(options.labelFormat, item, this.chart) :
  27781. options.labelFormatter.call(item)
  27782. });
  27783. }
  27784. /**
  27785. * Render a single specific legend item. Called internally from the `render`
  27786. * function.
  27787. *
  27788. * @private
  27789. * @function Highcharts.Legend#renderItem
  27790. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27791. * The item to render.
  27792. */
  27793. renderItem(item) {
  27794. 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 ?
  27795. item.series :
  27796. item, seriesOptions = series.options, showCheckbox = (legend.createCheckboxForItem) &&
  27797. seriesOptions &&
  27798. seriesOptions.showCheckbox, useHTML = options.useHTML, itemClassName = item.options.className;
  27799. let label = legendItem.label,
  27800. // full width minus text width
  27801. itemExtraWidth = symbolWidth + symbolPadding +
  27802. itemDistance + (showCheckbox ? 20 : 0);
  27803. if (!label) { // generate it once, later move it
  27804. // Generate the group box, a group to hold the symbol and text. Text
  27805. // is to be appended in Legend class.
  27806. legendItem.group = renderer
  27807. .g('legend-item')
  27808. .addClass('highcharts-' + series.type + '-series ' +
  27809. 'highcharts-color-' + item.colorIndex +
  27810. (itemClassName ? ' ' + itemClassName : '') +
  27811. (isSeries ?
  27812. ' highcharts-series-' + item.index :
  27813. ''))
  27814. .attr({ zIndex: 1 })
  27815. .add(legend.scrollGroup);
  27816. // Generate the list item text and add it to the group
  27817. legendItem.label = label = renderer.text('', ltr ?
  27818. symbolWidth + symbolPadding :
  27819. -symbolPadding, legend.baseline || 0, useHTML);
  27820. if (!chart.styledMode) {
  27821. // merge to prevent modifying original (#1021)
  27822. label.css(merge(item.visible ?
  27823. itemStyle :
  27824. itemHiddenStyle));
  27825. }
  27826. label
  27827. .attr({
  27828. align: ltr ? 'left' : 'right',
  27829. zIndex: 2
  27830. })
  27831. .add(legendItem.group);
  27832. // Get the baseline for the first item - the font size is equal for
  27833. // all
  27834. if (!legend.baseline) {
  27835. legend.fontMetrics = renderer.fontMetrics(label);
  27836. legend.baseline =
  27837. legend.fontMetrics.f + 3 + legend.itemMarginTop;
  27838. label.attr('y', legend.baseline);
  27839. legend.symbolHeight =
  27840. pick(options.symbolHeight, legend.fontMetrics.f);
  27841. if (options.squareSymbol) {
  27842. legend.symbolWidth = pick(options.symbolWidth, Math.max(legend.symbolHeight, 16));
  27843. itemExtraWidth = legend.symbolWidth + symbolPadding +
  27844. itemDistance + (showCheckbox ? 20 : 0);
  27845. if (ltr) {
  27846. label.attr('x', legend.symbolWidth + symbolPadding);
  27847. }
  27848. }
  27849. }
  27850. // Draw the legend symbol inside the group box
  27851. series.drawLegendSymbol(legend, item);
  27852. if (legend.setItemEvents) {
  27853. legend.setItemEvents(item, label, useHTML);
  27854. }
  27855. }
  27856. // Add the HTML checkbox on top
  27857. if (showCheckbox && !item.checkbox && legend.createCheckboxForItem) {
  27858. legend.createCheckboxForItem(item);
  27859. }
  27860. // Colorize the items
  27861. legend.colorizeItem(item, item.visible);
  27862. // Take care of max width and text overflow (#6659)
  27863. if (chart.styledMode || !itemStyle.width) {
  27864. label.css({
  27865. width: ((options.itemWidth ||
  27866. legend.widthOption ||
  27867. chart.spacingBox.width) - itemExtraWidth) + 'px'
  27868. });
  27869. }
  27870. // Always update the text
  27871. legend.setText(item);
  27872. // calculate the positions for the next line
  27873. const bBox = label.getBBox();
  27874. const fontMetricsH = (legend.fontMetrics && legend.fontMetrics.h) || 0;
  27875. item.itemWidth = item.checkboxOffset =
  27876. options.itemWidth ||
  27877. legendItem.labelWidth ||
  27878. bBox.width + itemExtraWidth;
  27879. legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
  27880. legend.totalItemWidth += item.itemWidth;
  27881. legend.itemHeight = item.itemHeight = Math.round(legendItem.labelHeight ||
  27882. // use bBox for multiline (#16398)
  27883. (bBox.height > fontMetricsH * 1.5 ? bBox.height : fontMetricsH));
  27884. }
  27885. /**
  27886. * Get the position of the item in the layout. We now know the
  27887. * maxItemWidth from the previous loop.
  27888. *
  27889. * @private
  27890. * @function Highcharts.Legend#layoutItem
  27891. * @param {Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series} item
  27892. */
  27893. layoutItem(item) {
  27894. 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 &&
  27895. this.totalItemWidth > maxLegendWidth) ?
  27896. this.maxItemWidth :
  27897. item.itemWidth, legendItem = item.legendItem || {};
  27898. // If the item exceeds the width, start a new line
  27899. if (horizontal &&
  27900. this.itemX - padding + itemWidth > maxLegendWidth) {
  27901. this.itemX = padding;
  27902. if (this.lastLineHeight) { // Not for the first line (#10167)
  27903. this.itemY += (itemMarginTop +
  27904. this.lastLineHeight +
  27905. itemMarginBottom);
  27906. }
  27907. this.lastLineHeight = 0; // reset for next line (#915, #3976)
  27908. }
  27909. // Set the edge positions
  27910. this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
  27911. this.lastLineHeight = Math.max(// #915
  27912. itemHeight, this.lastLineHeight);
  27913. // cache the position of the newly generated or reordered items
  27914. legendItem.x = this.itemX;
  27915. legendItem.y = this.itemY;
  27916. // advance
  27917. if (horizontal) {
  27918. this.itemX += itemWidth;
  27919. }
  27920. else {
  27921. this.itemY +=
  27922. itemMarginTop + itemHeight + itemMarginBottom;
  27923. this.lastLineHeight = itemHeight;
  27924. }
  27925. // the width of the widest item
  27926. this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
  27927. // decrease by itemDistance only when no checkbox #4853
  27928. 0 :
  27929. itemDistance) : itemWidth) + padding, this.offsetWidth);
  27930. }
  27931. /**
  27932. * Get all items, which is one item per series for most series and one
  27933. * item per point for pie series and its derivatives. Fires the event
  27934. * `afterGetAllItems`.
  27935. *
  27936. * @private
  27937. * @function Highcharts.Legend#getAllItems
  27938. * @return {Array<(Highcharts.BubbleLegendItem|Highcharts.Point|Highcharts.Series)>}
  27939. * The current items in the legend.
  27940. * @emits Highcharts.Legend#event:afterGetAllItems
  27941. */
  27942. getAllItems() {
  27943. let allItems = [];
  27944. this.chart.series.forEach(function (series) {
  27945. const seriesOptions = series && series.options;
  27946. // Handle showInLegend. If the series is linked to another series,
  27947. // defaults to false.
  27948. if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
  27949. // Use points or series for the legend item depending on
  27950. // legendType
  27951. allItems = allItems.concat((series.legendItem || {}).labels ||
  27952. (seriesOptions.legendType === 'point' ?
  27953. series.data :
  27954. series));
  27955. }
  27956. });
  27957. fireEvent(this, 'afterGetAllItems', { allItems });
  27958. return allItems;
  27959. }
  27960. /**
  27961. * Get a short, three letter string reflecting the alignment and layout.
  27962. *
  27963. * @private
  27964. * @function Highcharts.Legend#getAlignment
  27965. * @return {string}
  27966. * The alignment, empty string if floating
  27967. */
  27968. getAlignment() {
  27969. const options = this.options;
  27970. // Use the first letter of each alignment option in order to detect
  27971. // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
  27972. if (this.proximate) {
  27973. return options.align.charAt(0) + 'tv';
  27974. }
  27975. return options.floating ? '' : (options.align.charAt(0) +
  27976. options.verticalAlign.charAt(0) +
  27977. options.layout.charAt(0));
  27978. }
  27979. /**
  27980. * Adjust the chart margins by reserving space for the legend on only one
  27981. * side of the chart. If the position is set to a corner, top or bottom is
  27982. * reserved for horizontal legends and left or right for vertical ones.
  27983. *
  27984. * @private
  27985. * @function Highcharts.Legend#adjustMargins
  27986. * @param {Array<number>} margin
  27987. * @param {Array<number>} spacing
  27988. */
  27989. adjustMargins(margin, spacing) {
  27990. const chart = this.chart, options = this.options, alignment = this.getAlignment();
  27991. if (alignment) {
  27992. ([
  27993. /(lth|ct|rth)/,
  27994. /(rtv|rm|rbv)/,
  27995. /(rbh|cb|lbh)/,
  27996. /(lbv|lm|ltv)/
  27997. ]).forEach(function (alignments, side) {
  27998. if (alignments.test(alignment) && !defined(margin[side])) {
  27999. // Now we have detected on which side of the chart we should
  28000. // reserve space for the legend
  28001. chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
  28002. [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
  28003. pick(options.margin, 12) +
  28004. spacing[side] +
  28005. (chart.titleOffset[side] || 0)));
  28006. }
  28007. });
  28008. }
  28009. }
  28010. /**
  28011. * @private
  28012. * @function Highcharts.Legend#proximatePositions
  28013. */
  28014. proximatePositions() {
  28015. const chart = this.chart, boxes = [], alignLeft = this.options.align === 'left';
  28016. this.allItems.forEach(function (item) {
  28017. let lastPoint, height, useFirstPoint = alignLeft, target, top;
  28018. if (item.yAxis) {
  28019. if (item.xAxis.options.reversed) {
  28020. useFirstPoint = !useFirstPoint;
  28021. }
  28022. if (item.points) {
  28023. lastPoint = find(useFirstPoint ?
  28024. item.points :
  28025. item.points.slice(0).reverse(), function (item) {
  28026. return isNumber(item.plotY);
  28027. });
  28028. }
  28029. height = this.itemMarginTop +
  28030. item.legendItem.label.getBBox().height +
  28031. this.itemMarginBottom;
  28032. top = item.yAxis.top - chart.plotTop;
  28033. if (item.visible) {
  28034. target = lastPoint ?
  28035. lastPoint.plotY :
  28036. item.yAxis.height;
  28037. target += top - 0.3 * height;
  28038. }
  28039. else {
  28040. target = top + item.yAxis.height;
  28041. }
  28042. boxes.push({
  28043. target: target,
  28044. size: height,
  28045. item
  28046. });
  28047. }
  28048. }, this);
  28049. let legendItem;
  28050. for (const box of distribute(boxes, chart.plotHeight)) {
  28051. legendItem = box.item.legendItem || {};
  28052. if (isNumber(box.pos)) {
  28053. legendItem.y = chart.plotTop - chart.spacing[0] + box.pos;
  28054. }
  28055. }
  28056. }
  28057. /**
  28058. * Render the legend. This method can be called both before and after
  28059. * `chart.render`. If called after, it will only rearrange items instead
  28060. * of creating new ones. Called internally on initial render and after
  28061. * redraws.
  28062. *
  28063. * @private
  28064. * @function Highcharts.Legend#render
  28065. */
  28066. render() {
  28067. const legend = this, chart = legend.chart, renderer = chart.renderer, options = legend.options, padding = legend.padding,
  28068. // add each series or point
  28069. allItems = legend.getAllItems();
  28070. let display, legendWidth, legendHeight, legendGroup = legend.group, allowedWidth, box = legend.box;
  28071. legend.itemX = padding;
  28072. legend.itemY = legend.initialItemY;
  28073. legend.offsetWidth = 0;
  28074. legend.lastItemY = 0;
  28075. legend.widthOption = relativeLength(options.width, chart.spacingBox.width - padding);
  28076. // Compute how wide the legend is allowed to be
  28077. allowedWidth = chart.spacingBox.width - 2 * padding - options.x;
  28078. if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
  28079. allowedWidth /= 2;
  28080. }
  28081. legend.maxLegendWidth = legend.widthOption || allowedWidth;
  28082. if (!legendGroup) {
  28083. /**
  28084. * SVG group of the legend.
  28085. *
  28086. * @readonly
  28087. * @name Highcharts.Legend#group
  28088. * @type {Highcharts.SVGElement}
  28089. */
  28090. legend.group = legendGroup = renderer
  28091. .g('legend')
  28092. .addClass(options.className || '')
  28093. .attr({ zIndex: 7 })
  28094. .add();
  28095. legend.contentGroup = renderer
  28096. .g()
  28097. .attr({ zIndex: 1 }) // above background
  28098. .add(legendGroup);
  28099. legend.scrollGroup = renderer
  28100. .g()
  28101. .add(legend.contentGroup);
  28102. }
  28103. legend.renderTitle();
  28104. // sort by legendIndex
  28105. stableSort(allItems, (a, b) => ((a.options && a.options.legendIndex) || 0) -
  28106. ((b.options && b.options.legendIndex) || 0));
  28107. // reversed legend
  28108. if (options.reversed) {
  28109. allItems.reverse();
  28110. }
  28111. /**
  28112. * All items for the legend, which is an array of series for most series
  28113. * and an array of points for pie series and its derivatives.
  28114. *
  28115. * @readonly
  28116. * @name Highcharts.Legend#allItems
  28117. * @type {Array<(Highcharts.Point|Highcharts.Series)>}
  28118. */
  28119. legend.allItems = allItems;
  28120. legend.display = display = !!allItems.length;
  28121. // Render the items. First we run a loop to set the text and properties
  28122. // and read all the bounding boxes. The next loop computes the item
  28123. // positions based on the bounding boxes.
  28124. legend.lastLineHeight = 0;
  28125. legend.maxItemWidth = 0;
  28126. legend.totalItemWidth = 0;
  28127. legend.itemHeight = 0;
  28128. allItems.forEach(legend.renderItem, legend);
  28129. allItems.forEach(legend.layoutItem, legend);
  28130. // Get the box
  28131. legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
  28132. legendHeight = legend.lastItemY + legend.lastLineHeight +
  28133. legend.titleHeight;
  28134. legendHeight = legend.handleOverflow(legendHeight);
  28135. legendHeight += padding;
  28136. // Draw the border and/or background
  28137. if (!box) {
  28138. /**
  28139. * SVG element of the legend box.
  28140. *
  28141. * @readonly
  28142. * @name Highcharts.Legend#box
  28143. * @type {Highcharts.SVGElement}
  28144. */
  28145. legend.box = box = renderer.rect()
  28146. .addClass('highcharts-legend-box')
  28147. .attr({
  28148. r: options.borderRadius
  28149. })
  28150. .add(legendGroup);
  28151. }
  28152. // Presentational
  28153. if (!chart.styledMode) {
  28154. box
  28155. .attr({
  28156. stroke: options.borderColor,
  28157. 'stroke-width': options.borderWidth || 0,
  28158. fill: options.backgroundColor || 'none'
  28159. })
  28160. .shadow(options.shadow);
  28161. }
  28162. if (legendWidth > 0 && legendHeight > 0) {
  28163. box[box.placed ? 'animate' : 'attr'](box.crisp.call({}, {
  28164. x: 0,
  28165. y: 0,
  28166. width: legendWidth,
  28167. height: legendHeight
  28168. }, box.strokeWidth()));
  28169. }
  28170. // hide the border if no items
  28171. legendGroup[display ? 'show' : 'hide']();
  28172. // Open for responsiveness
  28173. if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
  28174. legendWidth = legendHeight = 0;
  28175. }
  28176. legend.legendWidth = legendWidth;
  28177. legend.legendHeight = legendHeight;
  28178. if (display) {
  28179. legend.align();
  28180. }
  28181. if (!this.proximate) {
  28182. this.positionItems();
  28183. }
  28184. fireEvent(this, 'afterRender');
  28185. }
  28186. /**
  28187. * Align the legend to chart's box.
  28188. *
  28189. * @private
  28190. * @function Highcharts.align
  28191. * @param {Highcharts.BBoxObject} alignTo
  28192. */
  28193. align(alignTo = this.chart.spacingBox) {
  28194. const chart = this.chart, options = this.options;
  28195. // If aligning to the top and the layout is horizontal, adjust for
  28196. // the title (#7428)
  28197. let y = alignTo.y;
  28198. if (/(lth|ct|rth)/.test(this.getAlignment()) &&
  28199. chart.titleOffset[0] > 0) {
  28200. y += chart.titleOffset[0];
  28201. }
  28202. else if (/(lbh|cb|rbh)/.test(this.getAlignment()) &&
  28203. chart.titleOffset[2] > 0) {
  28204. y -= chart.titleOffset[2];
  28205. }
  28206. if (y !== alignTo.y) {
  28207. alignTo = merge(alignTo, { y });
  28208. }
  28209. if (!chart.hasRendered) {
  28210. // Avoid animation when adjusting alignment for responsiveness and
  28211. // colorAxis label layout
  28212. this.group.placed = false;
  28213. }
  28214. this.group.align(merge(options, {
  28215. width: this.legendWidth,
  28216. height: this.legendHeight,
  28217. verticalAlign: this.proximate ? 'top' : options.verticalAlign
  28218. }), true, alignTo);
  28219. }
  28220. /**
  28221. * Set up the overflow handling by adding navigation with up and down arrows
  28222. * below the legend.
  28223. *
  28224. * @private
  28225. * @function Highcharts.Legend#handleOverflow
  28226. */
  28227. handleOverflow(legendHeight) {
  28228. 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) {
  28229. if (typeof height === 'number') {
  28230. clipRect.attr({
  28231. height: height
  28232. });
  28233. }
  28234. else if (clipRect) { // Reset (#5912)
  28235. legend.clipRect = clipRect.destroy();
  28236. legend.contentGroup.clip();
  28237. }
  28238. // useHTML
  28239. if (legend.contentGroup.div) {
  28240. legend.contentGroup.div.style.clip = height ?
  28241. 'rect(' + padding + 'px,9999px,' +
  28242. (padding + height) + 'px,0)' :
  28243. 'auto';
  28244. }
  28245. }, addTracker = function (key) {
  28246. legend[key] = renderer
  28247. .circle(0, 0, arrowSize * 1.3)
  28248. .translate(arrowSize / 2, arrowSize / 2)
  28249. .add(nav);
  28250. if (!chart.styledMode) {
  28251. legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
  28252. }
  28253. return legend[key];
  28254. };
  28255. let clipHeight, lastY, legendItem, spaceHeight = (chart.spacingBox.height +
  28256. (alignTop ? -optionsY : optionsY) - padding), nav = this.nav, clipRect = this.clipRect;
  28257. // Adjust the height
  28258. if (options.layout === 'horizontal' &&
  28259. options.verticalAlign !== 'middle' &&
  28260. !options.floating) {
  28261. spaceHeight /= 2;
  28262. }
  28263. if (maxHeight) {
  28264. spaceHeight = Math.min(spaceHeight, maxHeight);
  28265. }
  28266. // Reset the legend height and adjust the clipping rectangle
  28267. pages.length = 0;
  28268. if (legendHeight &&
  28269. spaceHeight > 0 &&
  28270. legendHeight > spaceHeight &&
  28271. navOptions.enabled !== false) {
  28272. this.clipHeight = clipHeight =
  28273. Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
  28274. this.currentPage = pick(this.currentPage, 1);
  28275. this.fullHeight = legendHeight;
  28276. // Fill pages with Y positions so that the top of each a legend item
  28277. // defines the scroll top for each page (#2098)
  28278. allItems.forEach((item, i) => {
  28279. legendItem = item.legendItem || {};
  28280. const y = legendItem.y || 0, h = Math.round(legendItem.label.getBBox().height);
  28281. let len = pages.length;
  28282. if (!len || (y - pages[len - 1] > clipHeight &&
  28283. (lastY || y) !== pages[len - 1])) {
  28284. pages.push(lastY || y);
  28285. len++;
  28286. }
  28287. // Keep track of which page each item is on
  28288. legendItem.pageIx = len - 1;
  28289. if (lastY) {
  28290. (allItems[i - 1].legendItem || {}).pageIx = len - 1;
  28291. }
  28292. // add the last page if needed (#2617, #13683)
  28293. if (
  28294. // check the last item
  28295. i === allItems.length - 1 &&
  28296. // if adding next page is needed (#18768)
  28297. y + h - pages[len - 1] > clipHeight &&
  28298. y > pages[len - 1]) {
  28299. pages.push(y);
  28300. legendItem.pageIx = len;
  28301. }
  28302. if (y !== lastY) {
  28303. lastY = y;
  28304. }
  28305. });
  28306. // Only apply clipping if needed. Clipping causes blurred legend in
  28307. // PDF export (#1787)
  28308. if (!clipRect) {
  28309. clipRect = legend.clipRect =
  28310. renderer.clipRect(0, padding - 2, 9999, 0);
  28311. legend.contentGroup.clip(clipRect);
  28312. }
  28313. clipToHeight(clipHeight);
  28314. // Add navigation elements
  28315. if (!nav) {
  28316. this.nav = nav = renderer.g()
  28317. .attr({ zIndex: 1 })
  28318. .add(this.group);
  28319. this.up = renderer
  28320. .symbol('triangle', 0, 0, arrowSize, arrowSize)
  28321. .add(nav);
  28322. addTracker('upTracker')
  28323. .on('click', function () {
  28324. legend.scroll(-1, animation);
  28325. });
  28326. this.pager = renderer.text('', 15, 10)
  28327. .addClass('highcharts-legend-navigation');
  28328. if (!chart.styledMode && navOptions.style) {
  28329. this.pager.css(navOptions.style);
  28330. }
  28331. this.pager.add(nav);
  28332. this.down = renderer
  28333. .symbol('triangle-down', 0, 0, arrowSize, arrowSize)
  28334. .add(nav);
  28335. addTracker('downTracker')
  28336. .on('click', function () {
  28337. legend.scroll(1, animation);
  28338. });
  28339. }
  28340. // Set initial position
  28341. legend.scroll(0);
  28342. legendHeight = spaceHeight;
  28343. // Reset
  28344. }
  28345. else if (nav) {
  28346. clipToHeight();
  28347. this.nav = nav.destroy(); // #6322
  28348. this.scrollGroup.attr({
  28349. translateY: 1
  28350. });
  28351. this.clipHeight = 0; // #1379
  28352. }
  28353. return legendHeight;
  28354. }
  28355. /**
  28356. * Scroll the legend by a number of pages.
  28357. *
  28358. * @private
  28359. * @function Highcharts.Legend#scroll
  28360. *
  28361. * @param {number} scrollBy
  28362. * The number of pages to scroll.
  28363. *
  28364. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  28365. * Whether and how to apply animation.
  28366. *
  28367. */
  28368. scroll(scrollBy, animation) {
  28369. const chart = this.chart, pages = this.pages, pageCount = pages.length, clipHeight = this.clipHeight, navOptions = this.options.navigation, pager = this.pager, padding = this.padding;
  28370. let currentPage = this.currentPage + scrollBy;
  28371. // When resizing while looking at the last page
  28372. if (currentPage > pageCount) {
  28373. currentPage = pageCount;
  28374. }
  28375. if (currentPage > 0) {
  28376. if (typeof animation !== 'undefined') {
  28377. setAnimation(animation, chart);
  28378. }
  28379. this.nav.attr({
  28380. translateX: padding,
  28381. translateY: clipHeight + this.padding + 7 + this.titleHeight,
  28382. visibility: 'inherit'
  28383. });
  28384. [this.up, this.upTracker].forEach(function (elem) {
  28385. elem.attr({
  28386. 'class': currentPage === 1 ?
  28387. 'highcharts-legend-nav-inactive' :
  28388. 'highcharts-legend-nav-active'
  28389. });
  28390. });
  28391. pager.attr({
  28392. text: currentPage + '/' + pageCount
  28393. });
  28394. [this.down, this.downTracker].forEach(function (elem) {
  28395. elem.attr({
  28396. // adjust to text width
  28397. x: 18 + this.pager.getBBox().width,
  28398. 'class': currentPage === pageCount ?
  28399. 'highcharts-legend-nav-inactive' :
  28400. 'highcharts-legend-nav-active'
  28401. });
  28402. }, this);
  28403. if (!chart.styledMode) {
  28404. this.up
  28405. .attr({
  28406. fill: currentPage === 1 ?
  28407. navOptions.inactiveColor :
  28408. navOptions.activeColor
  28409. });
  28410. this.upTracker
  28411. .css({
  28412. cursor: currentPage === 1 ? 'default' : 'pointer'
  28413. });
  28414. this.down
  28415. .attr({
  28416. fill: currentPage === pageCount ?
  28417. navOptions.inactiveColor :
  28418. navOptions.activeColor
  28419. });
  28420. this.downTracker
  28421. .css({
  28422. cursor: currentPage === pageCount ?
  28423. 'default' :
  28424. 'pointer'
  28425. });
  28426. }
  28427. this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
  28428. this.scrollGroup.animate({
  28429. translateY: this.scrollOffset
  28430. });
  28431. this.currentPage = currentPage;
  28432. this.positionCheckboxes();
  28433. // Fire event after scroll animation is complete
  28434. const animOptions = animObject(pick(animation, chart.renderer.globalAnimation, true));
  28435. syncTimeout(() => {
  28436. fireEvent(this, 'afterScroll', { currentPage });
  28437. }, animOptions.duration);
  28438. }
  28439. }
  28440. /**
  28441. * @private
  28442. * @function Highcharts.Legend#setItemEvents
  28443. * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
  28444. * @param {Highcharts.SVGElement} legendLabel
  28445. * @param {boolean} [useHTML=false]
  28446. * @emits Highcharts.Point#event:legendItemClick
  28447. * @emits Highcharts.Series#event:legendItemClick
  28448. */
  28449. setItemEvents(item, legendLabel, useHTML) {
  28450. const legend = this, legendItem = item.legendItem || {}, boxWrapper = legend.chart.renderer.boxWrapper, isPoint = item instanceof Point, activeClass = 'highcharts-legend-' +
  28451. (isPoint ? 'point' : 'series') + '-active', styledMode = legend.chart.styledMode,
  28452. // When `useHTML`, the symbol is rendered in other group, so
  28453. // we need to apply events listeners to both places
  28454. legendElements = useHTML ?
  28455. [legendLabel, legendItem.symbol] :
  28456. [legendItem.group];
  28457. const setOtherItemsState = (state) => {
  28458. legend.allItems.forEach((otherItem) => {
  28459. if (item !== otherItem) {
  28460. [otherItem]
  28461. .concat(otherItem.linkedSeries || [])
  28462. .forEach((otherItem) => {
  28463. otherItem.setState(state, !isPoint);
  28464. });
  28465. }
  28466. });
  28467. };
  28468. // Set the events on the item group, or in case of useHTML, the item
  28469. // itself (#1249)
  28470. for (const element of legendElements) {
  28471. if (element) {
  28472. element
  28473. .on('mouseover', function () {
  28474. if (item.visible) {
  28475. setOtherItemsState('inactive');
  28476. }
  28477. item.setState('hover');
  28478. // A CSS class to dim or hide other than the hovered
  28479. // series.
  28480. // Works only if hovered series is visible (#10071).
  28481. if (item.visible) {
  28482. boxWrapper.addClass(activeClass);
  28483. }
  28484. if (!styledMode) {
  28485. legendLabel.css(legend.options.itemHoverStyle);
  28486. }
  28487. })
  28488. .on('mouseout', function () {
  28489. if (!legend.chart.styledMode) {
  28490. legendLabel.css(merge(item.visible ?
  28491. legend.itemStyle :
  28492. legend.itemHiddenStyle));
  28493. }
  28494. setOtherItemsState('');
  28495. // A CSS class to dim or hide other than the hovered
  28496. // series.
  28497. boxWrapper.removeClass(activeClass);
  28498. item.setState();
  28499. })
  28500. .on('click', function (event) {
  28501. const strLegendItemClick = 'legendItemClick', fnLegendItemClick = function () {
  28502. if (item.setVisible) {
  28503. item.setVisible();
  28504. }
  28505. // Reset inactive state
  28506. setOtherItemsState(item.visible ? 'inactive' : '');
  28507. };
  28508. // A CSS class to dim or hide other than the hovered
  28509. // series. Event handling in iOS causes the activeClass
  28510. // to be added prior to click in some cases (#7418).
  28511. boxWrapper.removeClass(activeClass);
  28512. // Pass over the click/touch event. #4.
  28513. event = {
  28514. browserEvent: event
  28515. };
  28516. // click the name or symbol
  28517. if (item.firePointEvent) { // point
  28518. item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
  28519. }
  28520. else {
  28521. fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
  28522. }
  28523. });
  28524. }
  28525. }
  28526. }
  28527. /**
  28528. * @private
  28529. * @function Highcharts.Legend#createCheckboxForItem
  28530. * @param {Highcharts.BubbleLegendItem|Point|Highcharts.Series} item
  28531. * @emits Highcharts.Series#event:checkboxClick
  28532. */
  28533. createCheckboxForItem(item) {
  28534. const legend = this;
  28535. item.checkbox = createElement('input', {
  28536. type: 'checkbox',
  28537. className: 'highcharts-legend-checkbox',
  28538. checked: item.selected,
  28539. defaultChecked: item.selected // required by IE7
  28540. }, legend.options.itemCheckboxStyle, legend.chart.container);
  28541. addEvent(item.checkbox, 'click', function (event) {
  28542. const target = event.target;
  28543. fireEvent(item.series || item, 'checkboxClick', {
  28544. checked: target.checked,
  28545. item: item
  28546. }, function () {
  28547. item.select();
  28548. });
  28549. });
  28550. }
  28551. }
  28552. /* *
  28553. *
  28554. * Class Namespace
  28555. *
  28556. * */
  28557. (function (Legend) {
  28558. /* *
  28559. *
  28560. * Declarations
  28561. *
  28562. * */
  28563. /* *
  28564. *
  28565. * Constants
  28566. *
  28567. * */
  28568. const composedMembers = [];
  28569. /* *
  28570. *
  28571. * Functions
  28572. *
  28573. * */
  28574. /**
  28575. * @private
  28576. */
  28577. function compose(ChartClass) {
  28578. if (U.pushUnique(composedMembers, ChartClass)) {
  28579. addEvent(ChartClass, 'beforeMargins', function () {
  28580. /**
  28581. * The legend contains an interactive overview over chart items,
  28582. * usually individual series or points depending on the series
  28583. * type. The color axis and bubble legend are also rendered in
  28584. * the chart legend.
  28585. *
  28586. * @name Highcharts.Chart#legend
  28587. * @type {Highcharts.Legend}
  28588. */
  28589. this.legend = new Legend(this, this.options.legend);
  28590. });
  28591. }
  28592. }
  28593. Legend.compose = compose;
  28594. })(Legend || (Legend = {}));
  28595. /* *
  28596. *
  28597. * Default Export
  28598. *
  28599. * */
  28600. /* *
  28601. *
  28602. * API Declarations
  28603. *
  28604. * */
  28605. /**
  28606. * @interface Highcharts.LegendItemObject
  28607. */ /**
  28608. * @name Highcharts.LegendItemObject#item
  28609. * @type {Highcharts.SVGElement|undefined}
  28610. */ /**
  28611. * @name Highcharts.LegendItemObject#line
  28612. * @type {Highcharts.SVGElement|undefined}
  28613. */ /**
  28614. * @name Highcharts.LegendItemObject#symbol
  28615. * @type {Highcharts.SVGElement|undefined}
  28616. */
  28617. /**
  28618. * Gets fired when the legend item belonging to a point is clicked. The default
  28619. * action is to toggle the visibility of the point. This can be prevented by
  28620. * returning `false` or calling `event.preventDefault()`.
  28621. *
  28622. * @callback Highcharts.PointLegendItemClickCallbackFunction
  28623. *
  28624. * @param {Highcharts.Point} this
  28625. * The point on which the event occured.
  28626. *
  28627. * @param {Highcharts.PointLegendItemClickEventObject} event
  28628. * The event that occured.
  28629. */
  28630. /**
  28631. * Information about the legend click event.
  28632. *
  28633. * @interface Highcharts.PointLegendItemClickEventObject
  28634. */ /**
  28635. * Related browser event.
  28636. * @name Highcharts.PointLegendItemClickEventObject#browserEvent
  28637. * @type {Highcharts.PointerEvent}
  28638. */ /**
  28639. * Prevent the default action of toggle the visibility of the point.
  28640. * @name Highcharts.PointLegendItemClickEventObject#preventDefault
  28641. * @type {Function}
  28642. */ /**
  28643. * Related point.
  28644. * @name Highcharts.PointLegendItemClickEventObject#target
  28645. * @type {Highcharts.Point}
  28646. */ /**
  28647. * Event type.
  28648. * @name Highcharts.PointLegendItemClickEventObject#type
  28649. * @type {"legendItemClick"}
  28650. */
  28651. /**
  28652. * Series color as used by the legend and some series types.
  28653. * @name Highcharts.Series#color
  28654. * @type {Highcharts.ColorType|undefined}
  28655. */ /**
  28656. * Legend data for the series.
  28657. * @name Highcharts.Series#legendItem
  28658. * @type {Highcharts.LegendItemObject|undefined}
  28659. * @since 10.3.0
  28660. */
  28661. /**
  28662. * Gets fired when the legend item belonging to a series is clicked. The default
  28663. * action is to toggle the visibility of the series. This can be prevented by
  28664. * returning `false` or calling `event.preventDefault()`.
  28665. *
  28666. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  28667. *
  28668. * @param {Highcharts.Series} this
  28669. * The series where the event occured.
  28670. *
  28671. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  28672. * The event that occured.
  28673. */
  28674. /**
  28675. * Information about the legend click event.
  28676. *
  28677. * @interface Highcharts.SeriesLegendItemClickEventObject
  28678. */ /**
  28679. * Related browser event.
  28680. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  28681. * @type {Highcharts.PointerEvent}
  28682. */ /**
  28683. * Prevent the default action of toggle the visibility of the series.
  28684. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  28685. * @type {Function}
  28686. */ /**
  28687. * Related series.
  28688. * @name Highcharts.SeriesLegendItemClickEventObject#target
  28689. * @type {Highcharts.Series}
  28690. */ /**
  28691. * Event type.
  28692. * @name Highcharts.SeriesLegendItemClickEventObject#type
  28693. * @type {"legendItemClick"}
  28694. */
  28695. (''); // keeps doclets above in JS file
  28696. return Legend;
  28697. });
  28698. _registerModule(_modules, 'Core/Legend/LegendSymbol.js', [_modules['Core/Utilities.js']], function (U) {
  28699. /* *
  28700. *
  28701. * (c) 2010-2021 Torstein Honsi
  28702. *
  28703. * License: www.highcharts.com/license
  28704. *
  28705. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28706. *
  28707. * */
  28708. const { extend, merge, pick } = U;
  28709. /* *
  28710. *
  28711. * Namespace
  28712. *
  28713. * */
  28714. var LegendSymbol;
  28715. (function (LegendSymbol) {
  28716. /* *
  28717. *
  28718. * Functions
  28719. *
  28720. * */
  28721. /* eslint-disable valid-jsdoc */
  28722. /**
  28723. * Get the series' symbol in the legend.
  28724. *
  28725. * This method should be overridable to create custom symbols through
  28726. * Highcharts.seriesTypes[type].prototype.drawLegendSymbol.
  28727. *
  28728. * @private
  28729. * @function Highcharts.LegendSymbolMixin.lineMarker
  28730. *
  28731. * @param {Highcharts.Legend} legend
  28732. * The legend object.
  28733. */
  28734. function lineMarker(legend, item) {
  28735. 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 -
  28736. Math.round(legend.fontMetrics.b * 0.3);
  28737. let attr = {}, legendSymbol, markerOptions = options.marker, lineSizer = 0;
  28738. // Draw the line
  28739. if (!this.chart.styledMode) {
  28740. attr = {
  28741. 'stroke-width': Math.min(options.lineWidth || 0, 24)
  28742. };
  28743. if (options.dashStyle) {
  28744. attr.dashstyle = options.dashStyle;
  28745. }
  28746. else if (options.linecap !== 'square') {
  28747. attr['stroke-linecap'] = 'round';
  28748. }
  28749. }
  28750. legendItem.line = renderer
  28751. .path()
  28752. .addClass('highcharts-graph')
  28753. .attr(attr)
  28754. .add(legendItemGroup);
  28755. if (attr['stroke-linecap']) {
  28756. lineSizer = Math.min(legendItem.line.strokeWidth(), symbolWidth) / 2;
  28757. }
  28758. if (symbolWidth) {
  28759. legendItem.line
  28760. .attr({
  28761. d: [
  28762. ['M', lineSizer, verticalCenter],
  28763. ['L', symbolWidth - lineSizer, verticalCenter]
  28764. ]
  28765. });
  28766. }
  28767. // Draw the marker
  28768. if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
  28769. // Do not allow the marker to be larger than the symbolHeight
  28770. let radius = Math.min(pick(markerOptions.radius, generalRadius), generalRadius);
  28771. // Restrict symbol markers size
  28772. if (this.symbol.indexOf('url') === 0) {
  28773. markerOptions = merge(markerOptions, {
  28774. width: symbolHeight,
  28775. height: symbolHeight
  28776. });
  28777. radius = 0;
  28778. }
  28779. legendItem.symbol = legendSymbol = renderer
  28780. .symbol(this.symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, 2 * radius, extend({ context: 'legend' }, markerOptions))
  28781. .addClass('highcharts-point')
  28782. .add(legendItemGroup);
  28783. legendSymbol.isMarker = true;
  28784. }
  28785. }
  28786. LegendSymbol.lineMarker = lineMarker;
  28787. /**
  28788. * Get the series' symbol in the legend.
  28789. *
  28790. * This method should be overridable to create custom symbols through
  28791. * Highcharts.seriesTypes[type].prototype.drawLegendSymbol.
  28792. *
  28793. * @private
  28794. * @function Highcharts.LegendSymbolMixin.rectangle
  28795. *
  28796. * @param {Highcharts.Legend} legend
  28797. * The legend object
  28798. *
  28799. * @param {Highcharts.Point|Highcharts.Series} item
  28800. * The series (this) or point
  28801. */
  28802. function rectangle(legend, item) {
  28803. const legendItem = item.legendItem || {}, options = legend.options, symbolHeight = legend.symbolHeight, square = options.squareSymbol, symbolWidth = square ? symbolHeight : legend.symbolWidth;
  28804. legendItem.symbol = this.chart.renderer
  28805. .rect(square ? (legend.symbolWidth - symbolHeight) / 2 : 0, legend.baseline - symbolHeight + 1, // #3988
  28806. symbolWidth, symbolHeight, pick(legend.options.symbolRadius, symbolHeight / 2))
  28807. .addClass('highcharts-point')
  28808. .attr({
  28809. zIndex: 3
  28810. })
  28811. .add(legendItem.group);
  28812. }
  28813. LegendSymbol.rectangle = rectangle;
  28814. })(LegendSymbol || (LegendSymbol = {}));
  28815. /* *
  28816. *
  28817. * Default Export
  28818. *
  28819. * */
  28820. return LegendSymbol;
  28821. });
  28822. _registerModule(_modules, 'Core/Series/SeriesDefaults.js', [], function () {
  28823. /* *
  28824. *
  28825. * (c) 2010-2021 Torstein Honsi
  28826. *
  28827. * License: www.highcharts.com/license
  28828. *
  28829. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28830. *
  28831. * */
  28832. /* *
  28833. *
  28834. * API Options
  28835. *
  28836. * */
  28837. /**
  28838. * General options for all series types.
  28839. *
  28840. * @optionparent plotOptions.series
  28841. */
  28842. const seriesDefaults = {
  28843. // base series options
  28844. /**
  28845. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  28846. * of a line graph. Round means that lines are rounded in the ends and
  28847. * bends.
  28848. *
  28849. * @type {Highcharts.SeriesLinecapValue}
  28850. * @default round
  28851. * @since 3.0.7
  28852. * @apioption plotOptions.line.linecap
  28853. */
  28854. /**
  28855. * Pixel width of the graph line.
  28856. *
  28857. * @see In styled mode, the line stroke-width can be set with the
  28858. * `.highcharts-graph` class name.
  28859. *
  28860. * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
  28861. * On all series
  28862. * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
  28863. * On one single series
  28864. *
  28865. * @product highcharts highstock
  28866. */
  28867. lineWidth: 1,
  28868. /**
  28869. * For some series, there is a limit that shuts down animation
  28870. * by default when the total number of points in the chart is too high.
  28871. * For example, for a column chart and its derivatives, animation does
  28872. * not run if there is more than 250 points totally. To disable this
  28873. * cap, set `animationLimit` to `Infinity`. This option works if animation
  28874. * is fired on individual points, not on a group of points like e.g. during
  28875. * the initial animation.
  28876. *
  28877. * @sample {highcharts} highcharts/plotoptions/series-animationlimit/
  28878. * Animation limit on updating individual points
  28879. *
  28880. * @type {number}
  28881. * @apioption plotOptions.series.animationLimit
  28882. */
  28883. /**
  28884. * Allow this series' points to be selected by clicking on the graphic
  28885. * (columns, point markers, pie slices, map areas etc).
  28886. *
  28887. * The selected points can be handled by point select and unselect
  28888. * events, or collectively by the [getSelectedPoints
  28889. * ](/class-reference/Highcharts.Chart#getSelectedPoints) function.
  28890. *
  28891. * And alternative way of selecting points is through dragging.
  28892. *
  28893. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
  28894. * Line
  28895. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
  28896. * Column
  28897. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
  28898. * Pie
  28899. * @sample {highcharts} highcharts/chart/events-selection-points/
  28900. * Select a range of points through a drag selection
  28901. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28902. * Map area
  28903. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  28904. * Map bubble
  28905. *
  28906. * @since 1.2.0
  28907. *
  28908. * @private
  28909. */
  28910. allowPointSelect: false,
  28911. /**
  28912. * When true, each point or column edge is rounded to its nearest pixel
  28913. * in order to render sharp on screen. In some cases, when there are a
  28914. * lot of densely packed columns, this leads to visible difference
  28915. * in column widths or distance between columns. In these cases,
  28916. * setting `crisp` to `false` may look better, even though each column
  28917. * is rendered blurry.
  28918. *
  28919. * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
  28920. * Crisp is false
  28921. *
  28922. * @since 5.0.10
  28923. * @product highcharts highstock gantt
  28924. *
  28925. * @private
  28926. */
  28927. crisp: true,
  28928. /**
  28929. * If true, a checkbox is displayed next to the legend item to allow
  28930. * selecting the series. The state of the checkbox is determined by
  28931. * the `selected` option.
  28932. *
  28933. * @productdesc {highmaps}
  28934. * Note that if a `colorAxis` is defined, the color axis is represented
  28935. * in the legend, not the series.
  28936. *
  28937. * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
  28938. * Show select box
  28939. *
  28940. * @since 1.2.0
  28941. *
  28942. * @private
  28943. */
  28944. showCheckbox: false,
  28945. /**
  28946. * Enable or disable the initial animation when a series is displayed.
  28947. * The animation can also be set as a configuration object. Please
  28948. * note that this option only applies to the initial animation of the
  28949. * series itself. For other animations, see [chart.animation](
  28950. * #chart.animation) and the animation parameter under the API methods.
  28951. * The following properties are supported:
  28952. *
  28953. * - `defer`: The animation delay time in milliseconds.
  28954. *
  28955. * - `duration`: The duration of the animation in milliseconds. (Defaults to
  28956. * `1000`)
  28957. *
  28958. * - `easing`: Can be a string reference to an easing function set on
  28959. * the `Math` object or a function. See the _Custom easing function_
  28960. * demo below. (Defaults to `easeInOutSine`)
  28961. *
  28962. * Due to poor performance, animation is disabled in old IE browsers
  28963. * for several chart types.
  28964. *
  28965. * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
  28966. * Animation disabled
  28967. * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
  28968. * Slower animation
  28969. * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
  28970. * Custom easing function
  28971. * @sample {highstock} stock/plotoptions/animation-slower/
  28972. * Slower animation
  28973. * @sample {highstock} stock/plotoptions/animation-easing/
  28974. * Custom easing function
  28975. * @sample {highmaps} maps/plotoptions/series-animation-true/
  28976. * Animation enabled on map series
  28977. * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
  28978. * Disabled on mapbubble series
  28979. *
  28980. * @type {boolean|Highcharts.AnimationOptionsObject}
  28981. * @default {highcharts} true
  28982. * @default {highstock} true
  28983. * @default {highmaps} false
  28984. *
  28985. * @private
  28986. */
  28987. animation: {
  28988. /** @ignore-option */
  28989. duration: 1000
  28990. },
  28991. /**
  28992. * An additional class name to apply to the series' graphical elements.
  28993. * This option does not replace default class names of the graphical
  28994. * element. Changes to the series' color will also be reflected in a
  28995. * chart's legend and tooltip.
  28996. *
  28997. * @sample {highcharts} highcharts/css/point-series-classname
  28998. *
  28999. * @type {string}
  29000. * @since 5.0.0
  29001. * @apioption plotOptions.series.className
  29002. */
  29003. /**
  29004. * Disable this option to allow series rendering in the whole plotting
  29005. * area.
  29006. *
  29007. * **Note:** Clipping should be always enabled when
  29008. * [chart.zoomType](#chart.zoomType) is set
  29009. *
  29010. * @sample {highcharts} highcharts/plotoptions/series-clip/
  29011. * Disabled clipping
  29012. *
  29013. * @default true
  29014. * @type {boolean}
  29015. * @since 3.0.0
  29016. * @apioption plotOptions.series.clip
  29017. */
  29018. /**
  29019. * The main color of the series. In line type series it applies to the
  29020. * line and the point markers unless otherwise specified. In bar type
  29021. * series it applies to the bars unless a color is specified per point.
  29022. * The default value is pulled from the `options.colors` array.
  29023. *
  29024. * In styled mode, the color can be defined by the
  29025. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  29026. * color can be set with the `.highcharts-series`,
  29027. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  29028. * `.highcharts-series-{n}` class, or individual classes given by the
  29029. * `className` option.
  29030. *
  29031. * @productdesc {highmaps}
  29032. * In maps, the series color is rarely used, as most choropleth maps use
  29033. * the color to denote the value of each point. The series color can
  29034. * however be used in a map with multiple series holding categorized
  29035. * data.
  29036. *
  29037. * @sample {highcharts} highcharts/plotoptions/series-color-general/
  29038. * General plot option
  29039. * @sample {highcharts} highcharts/plotoptions/series-color-specific/
  29040. * One specific series
  29041. * @sample {highcharts} highcharts/plotoptions/series-color-area/
  29042. * Area color
  29043. * @sample {highcharts} highcharts/series/infographic/
  29044. * Pattern fill
  29045. * @sample {highmaps} maps/demo/category-map/
  29046. * Category map by multiple series
  29047. *
  29048. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29049. * @apioption plotOptions.series.color
  29050. */
  29051. /**
  29052. * Styled mode only. A specific color index to use for the series, so its
  29053. * graphic representations are given the class name `highcharts-color-{n}`.
  29054. *
  29055. * Since v11, CSS variables on the form `--highcharts-color-{n}` make
  29056. * changing the color scheme very convenient.
  29057. *
  29058. * @sample {highcharts} highcharts/css/colorindex/ Series and point color
  29059. * index
  29060. *
  29061. * @type {number}
  29062. * @since 5.0.0
  29063. * @apioption plotOptions.series.colorIndex
  29064. */
  29065. /**
  29066. * Whether to connect a graph line across null points, or render a gap
  29067. * between the two points on either side of the null.
  29068. *
  29069. * In stacked area chart, if `connectNulls` is set to true,
  29070. * null points are interpreted as 0.
  29071. *
  29072. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
  29073. * False by default
  29074. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
  29075. * True
  29076. *
  29077. * @type {boolean}
  29078. * @default false
  29079. * @product highcharts highstock
  29080. * @apioption plotOptions.series.connectNulls
  29081. */
  29082. /**
  29083. * You can set the cursor to "pointer" if you have click events attached
  29084. * to the series, to signal to the user that the points and lines can
  29085. * be clicked.
  29086. *
  29087. * In styled mode, the series cursor can be set with the same classes
  29088. * as listed under [series.color](#plotOptions.series.color).
  29089. *
  29090. * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
  29091. * On line graph
  29092. * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
  29093. * On columns
  29094. * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
  29095. * On scatter markers
  29096. * @sample {highstock} stock/plotoptions/cursor/
  29097. * Pointer on a line graph
  29098. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  29099. * Map area
  29100. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  29101. * Map bubble
  29102. *
  29103. * @type {string|Highcharts.CursorValue}
  29104. * @apioption plotOptions.series.cursor
  29105. */
  29106. /**
  29107. * A reserved subspace to store options and values for customized
  29108. * functionality. Here you can add additional data for your own event
  29109. * callbacks and formatter callbacks.
  29110. *
  29111. * @sample {highcharts} highcharts/point/custom/
  29112. * Point and series with custom data
  29113. *
  29114. * @type {Highcharts.Dictionary<*>}
  29115. * @apioption plotOptions.series.custom
  29116. */
  29117. /**
  29118. * Name of the dash style to use for the graph, or for some series types
  29119. * the outline of each shape.
  29120. *
  29121. * In styled mode, the
  29122. * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
  29123. * can be set with the same classes as listed under
  29124. * [series.color](#plotOptions.series.color).
  29125. *
  29126. * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
  29127. * Possible values demonstrated
  29128. * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
  29129. * Chart suitable for printing in black and white
  29130. * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
  29131. * Possible values demonstrated
  29132. * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
  29133. * Possible values demonstrated
  29134. * @sample {highmaps} maps/plotoptions/series-dashstyle/
  29135. * Dotted borders on a map
  29136. *
  29137. * @type {Highcharts.DashStyleValue}
  29138. * @default Solid
  29139. * @since 2.1
  29140. * @apioption plotOptions.series.dashStyle
  29141. */
  29142. /**
  29143. * A description of the series to add to the screen reader information
  29144. * about the series.
  29145. *
  29146. * @type {string}
  29147. * @since 5.0.0
  29148. * @requires modules/accessibility
  29149. * @apioption plotOptions.series.description
  29150. */
  29151. /**
  29152. * Options for the series data sorting.
  29153. *
  29154. * @type {Highcharts.DataSortingOptionsObject}
  29155. * @since 8.0.0
  29156. * @product highcharts highstock
  29157. * @apioption plotOptions.series.dataSorting
  29158. */
  29159. /**
  29160. * Enable or disable data sorting for the series. Use [xAxis.reversed](
  29161. * #xAxis.reversed) to change the sorting order.
  29162. *
  29163. * @sample {highcharts} highcharts/datasorting/animation/
  29164. * Data sorting in scatter-3d
  29165. * @sample {highcharts} highcharts/datasorting/labels-animation/
  29166. * Axis labels animation
  29167. * @sample {highcharts} highcharts/datasorting/dependent-sorting/
  29168. * Dependent series sorting
  29169. * @sample {highcharts} highcharts/datasorting/independent-sorting/
  29170. * Independent series sorting
  29171. *
  29172. * @type {boolean}
  29173. * @since 8.0.0
  29174. * @apioption plotOptions.series.dataSorting.enabled
  29175. */
  29176. /**
  29177. * Whether to allow matching points by name in an update. If this option
  29178. * is disabled, points will be matched by order.
  29179. *
  29180. * @sample {highcharts} highcharts/datasorting/match-by-name/
  29181. * Enabled match by name
  29182. *
  29183. * @type {boolean}
  29184. * @since 8.0.0
  29185. * @apioption plotOptions.series.dataSorting.matchByName
  29186. */
  29187. /**
  29188. * Determines what data value should be used to sort by.
  29189. *
  29190. * @sample {highcharts} highcharts/datasorting/sort-key/
  29191. * Sort key as `z` value
  29192. *
  29193. * @type {string}
  29194. * @since 8.0.0
  29195. * @default y
  29196. * @apioption plotOptions.series.dataSorting.sortKey
  29197. */
  29198. /**
  29199. * Enable or disable the mouse tracking for a specific series. This
  29200. * includes point tooltips and click events on graphs and points. For
  29201. * large datasets it improves performance.
  29202. *
  29203. * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
  29204. * No mouse tracking
  29205. * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
  29206. * No mouse tracking
  29207. *
  29208. * @type {boolean}
  29209. * @default true
  29210. * @apioption plotOptions.series.enableMouseTracking
  29211. */
  29212. enableMouseTracking: true,
  29213. /**
  29214. * Whether to use the Y extremes of the total chart width or only the
  29215. * zoomed area when zooming in on parts of the X axis. By default, the
  29216. * Y axis adjusts to the min and max of the visible data. Cartesian
  29217. * series only.
  29218. *
  29219. * @type {boolean}
  29220. * @default false
  29221. * @since 4.1.6
  29222. * @product highcharts highstock gantt
  29223. * @apioption plotOptions.series.getExtremesFromAll
  29224. */
  29225. /**
  29226. * An array specifying which option maps to which key in the data point
  29227. * array. This makes it convenient to work with unstructured data arrays
  29228. * from different sources.
  29229. *
  29230. * @see [series.data](#series.line.data)
  29231. *
  29232. * @sample {highcharts|highstock} highcharts/series/data-keys/
  29233. * An extended data array with keys
  29234. * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
  29235. * Nested keys used to access object properties
  29236. *
  29237. * @type {Array<string>}
  29238. * @since 4.1.6
  29239. * @apioption plotOptions.series.keys
  29240. */
  29241. /**
  29242. * The line cap used for line ends and line joins on the graph.
  29243. *
  29244. * @sample highcharts/series-line/linecap/
  29245. * Line cap comparison
  29246. *
  29247. * @type {Highcharts.SeriesLinecapValue}
  29248. * @default round
  29249. * @product highcharts highstock
  29250. * @apioption plotOptions.series.linecap
  29251. */
  29252. /**
  29253. * The [id](#series.id) of another series to link to. Additionally,
  29254. * the value can be ":previous" to link to the previous series. When
  29255. * two series are linked, only the first one appears in the legend.
  29256. * Toggling the visibility of this also toggles the linked series.
  29257. *
  29258. * If master series uses data sorting and linked series does not have
  29259. * its own sorting definition, the linked series will be sorted in the
  29260. * same order as the master one.
  29261. *
  29262. * @sample {highcharts|highstock} highcharts/demo/arearange-line/
  29263. * Linked series
  29264. *
  29265. * @type {string}
  29266. * @since 3.0
  29267. * @product highcharts highstock gantt
  29268. * @apioption plotOptions.series.linkedTo
  29269. */
  29270. /**
  29271. * Options for the corresponding navigator series if `showInNavigator`
  29272. * is `true` for this series. Available options are the same as any
  29273. * series, documented at [plotOptions](#plotOptions.series) and
  29274. * [series](#series).
  29275. *
  29276. * These options are merged with options in [navigator.series](
  29277. * #navigator.series), and will take precedence if the same option is
  29278. * defined both places.
  29279. *
  29280. * @see [navigator.series](#navigator.series)
  29281. *
  29282. * @type {Highcharts.PlotSeriesOptions}
  29283. * @since 5.0.0
  29284. * @product highstock
  29285. * @apioption plotOptions.series.navigatorOptions
  29286. */
  29287. /**
  29288. * The color for the parts of the graph or points that are below the
  29289. * [threshold](#plotOptions.series.threshold). Note that `zones` takes
  29290. * precedence over the negative color. Using `negativeColor` is
  29291. * equivalent to applying a zone with value of 0.
  29292. *
  29293. * @see In styled mode, a negative color is applied by setting this option
  29294. * to `true` combined with the `.highcharts-negative` class name.
  29295. *
  29296. * @sample {highcharts} highcharts/plotoptions/series-negative-color/
  29297. * Spline, area and column
  29298. * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
  29299. * Arearange
  29300. * @sample {highcharts} highcharts/css/series-negative-color/
  29301. * Styled mode
  29302. * @sample {highstock} highcharts/plotoptions/series-negative-color/
  29303. * Spline, area and column
  29304. * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
  29305. * Arearange
  29306. * @sample {highmaps} highcharts/plotoptions/series-negative-color/
  29307. * Spline, area and column
  29308. * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
  29309. * Arearange
  29310. *
  29311. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29312. * @since 3.0
  29313. * @apioption plotOptions.series.negativeColor
  29314. */
  29315. /**
  29316. * Same as
  29317. * [accessibility.point.descriptionFormat](#accessibility.point.descriptionFormat),
  29318. * but for an individual series. Overrides the chart wide configuration.
  29319. *
  29320. * @type {Function}
  29321. * @since 11.1.0
  29322. * @apioption plotOptions.series.pointDescriptionFormat
  29323. */
  29324. /**
  29325. * Same as
  29326. * [accessibility.series.descriptionFormatter](#accessibility.series.descriptionFormatter),
  29327. * but for an individual series. Overrides the chart wide configuration.
  29328. *
  29329. * @type {Function}
  29330. * @since 5.0.12
  29331. * @apioption plotOptions.series.pointDescriptionFormatter
  29332. */
  29333. /**
  29334. * If no x values are given for the points in a series, `pointInterval`
  29335. * defines the interval of the x values. For example, if a series
  29336. * contains one value every decade starting from year 0, set
  29337. * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
  29338. * is set in milliseconds.
  29339. *
  29340. * It can be also be combined with `pointIntervalUnit` to draw irregular
  29341. * time intervals.
  29342. *
  29343. * If combined with `relativeXValue`, an x value can be set on each
  29344. * point, and the `pointInterval` is added x times to the `pointStart`
  29345. * setting.
  29346. *
  29347. * Please note that this options applies to the _series data_, not the
  29348. * interval of the axis ticks, which is independent.
  29349. *
  29350. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29351. * Datetime X axis
  29352. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29353. * Relative x value
  29354. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29355. * Using pointStart and pointInterval
  29356. * @sample {highstock} stock/plotoptions/relativexvalue/
  29357. * Relative x value
  29358. *
  29359. * @type {number}
  29360. * @default 1
  29361. * @product highcharts highstock gantt
  29362. * @apioption plotOptions.series.pointInterval
  29363. */
  29364. /**
  29365. * On datetime series, this allows for setting the
  29366. * [pointInterval](#plotOptions.series.pointInterval) to irregular time
  29367. * units, `day`, `month` and `year`. A day is usually the same as 24
  29368. * hours, but `pointIntervalUnit` also takes the DST crossover into
  29369. * consideration when dealing with local time. Combine this option with
  29370. * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
  29371. *
  29372. * Please note that this options applies to the _series data_, not the
  29373. * interval of the axis ticks, which is independent.
  29374. *
  29375. * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
  29376. * One point a month
  29377. * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
  29378. * One point a month
  29379. *
  29380. * @type {string}
  29381. * @since 4.1.0
  29382. * @product highcharts highstock gantt
  29383. * @validvalue ["day", "month", "year"]
  29384. * @apioption plotOptions.series.pointIntervalUnit
  29385. */
  29386. /**
  29387. * Possible values: `"on"`, `"between"`, `number`.
  29388. *
  29389. * In a column chart, when pointPlacement is `"on"`, the point will not
  29390. * create any padding of the X axis. In a polar column chart this means
  29391. * that the first column points directly north. If the pointPlacement is
  29392. * `"between"`, the columns will be laid out between ticks. This is
  29393. * useful for example for visualising an amount between two points in
  29394. * time or in a certain sector of a polar chart.
  29395. *
  29396. * Since Highcharts 3.0.2, the point placement can also be numeric,
  29397. * where 0 is on the axis value, -0.5 is between this value and the
  29398. * previous, and 0.5 is between this value and the next. Unlike the
  29399. * textual options, numeric point placement options won't affect axis
  29400. * padding.
  29401. *
  29402. * Note that pointPlacement needs a [pointRange](
  29403. * #plotOptions.series.pointRange) to work. For column series this is
  29404. * computed, but for line-type series it needs to be set.
  29405. *
  29406. * For the `xrange` series type and gantt charts, if the Y axis is a
  29407. * category axis, the `pointPlacement` applies to the Y axis rather than
  29408. * the (typically datetime) X axis.
  29409. *
  29410. * Defaults to `undefined` in cartesian charts, `"between"` in polar
  29411. * charts.
  29412. *
  29413. * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
  29414. *
  29415. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
  29416. * Between in a column chart
  29417. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
  29418. * Numeric placement for custom layout
  29419. * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
  29420. * Placement in heatmap
  29421. *
  29422. * @type {string|number}
  29423. * @since 2.3.0
  29424. * @product highcharts highstock gantt
  29425. * @apioption plotOptions.series.pointPlacement
  29426. */
  29427. /**
  29428. * If no x values are given for the points in a series, pointStart
  29429. * defines on what value to start. For example, if a series contains one
  29430. * yearly value starting from 1945, set pointStart to 1945.
  29431. *
  29432. * If combined with `relativeXValue`, an x value can be set on each
  29433. * point. The x value from the point options is multiplied by
  29434. * `pointInterval` and added to `pointStart` to produce a modified x
  29435. * value.
  29436. *
  29437. * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
  29438. * Linear
  29439. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29440. * Datetime
  29441. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29442. * Relative x value
  29443. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29444. * Using pointStart and pointInterval
  29445. * @sample {highstock} stock/plotoptions/relativexvalue/
  29446. * Relative x value
  29447. *
  29448. * @type {number}
  29449. * @default 0
  29450. * @product highcharts highstock gantt
  29451. * @apioption plotOptions.series.pointStart
  29452. */
  29453. /**
  29454. * When true, X values in the data set are relative to the current
  29455. * `pointStart`, `pointInterval` and `pointIntervalUnit` settings. This
  29456. * allows compression of the data for datasets with irregular X values.
  29457. *
  29458. * The real X values are computed on the formula `f(x) = ax + b`, where
  29459. * `a` is the `pointInterval` (optionally with a time unit given by
  29460. * `pointIntervalUnit`), and `b` is the `pointStart`.
  29461. *
  29462. * @sample {highcharts} highcharts/plotoptions/series-relativexvalue/
  29463. * Relative X value
  29464. * @sample {highstock} stock/plotoptions/relativexvalue/
  29465. * Relative X value
  29466. *
  29467. * @type {boolean}
  29468. * @default false
  29469. * @product highcharts highstock
  29470. * @apioption plotOptions.series.relativeXValue
  29471. */
  29472. /**
  29473. * Whether to select the series initially. If `showCheckbox` is true,
  29474. * the checkbox next to the series name in the legend will be checked
  29475. * for a selected series.
  29476. *
  29477. * @sample {highcharts} highcharts/plotoptions/series-selected/
  29478. * One out of two series selected
  29479. *
  29480. * @type {boolean}
  29481. * @default false
  29482. * @since 1.2.0
  29483. * @apioption plotOptions.series.selected
  29484. */
  29485. /**
  29486. * Whether to apply a drop shadow to the graph line. Since 2.3 the
  29487. * shadow can be an object configuration containing `color`, `offsetX`,
  29488. * `offsetY`, `opacity` and `width`.
  29489. *
  29490. * Note that in some cases, like stacked columns or other dense layouts, the
  29491. * series may cast shadows on each other. In that case, the
  29492. * `chart.seriesGroupShadow` allows applying a common drop shadow to the
  29493. * whole series group.
  29494. *
  29495. * @sample {highcharts} highcharts/plotoptions/series-shadow/
  29496. * Shadow enabled
  29497. *
  29498. * @type {boolean|Highcharts.ShadowOptionsObject}
  29499. * @default false
  29500. * @apioption plotOptions.series.shadow
  29501. */
  29502. /**
  29503. * Whether to display this particular series or series type in the
  29504. * legend. Standalone series are shown in legend by default, and linked
  29505. * series are not. Since v7.2.0 it is possible to show series that use
  29506. * colorAxis by setting this option to `true`.
  29507. *
  29508. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  29509. * One series in the legend, one hidden
  29510. *
  29511. * @type {boolean}
  29512. * @apioption plotOptions.series.showInLegend
  29513. */
  29514. /**
  29515. * Whether or not to show the series in the navigator. Takes precedence
  29516. * over [navigator.baseSeries](#navigator.baseSeries) if defined.
  29517. *
  29518. * @type {boolean}
  29519. * @since 5.0.0
  29520. * @product highstock
  29521. * @apioption plotOptions.series.showInNavigator
  29522. */
  29523. /**
  29524. * If set to `true`, the accessibility module will skip past the points
  29525. * in this series for keyboard navigation.
  29526. *
  29527. * @type {boolean}
  29528. * @since 5.0.12
  29529. * @apioption plotOptions.series.skipKeyboardNavigation
  29530. */
  29531. /**
  29532. * Whether to stack the values of each series on top of each other.
  29533. * Possible values are `undefined` to disable, `"normal"` to stack by
  29534. * value or `"percent"`.
  29535. *
  29536. * When stacking is enabled, data must be sorted
  29537. * in ascending X order.
  29538. *
  29539. * Some stacking options are related to specific series types. In the
  29540. * streamgraph series type, the stacking option is set to `"stream"`.
  29541. * The second one is `"overlap"`, which only applies to waterfall
  29542. * series.
  29543. *
  29544. * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
  29545. *
  29546. * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
  29547. * Line
  29548. * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
  29549. * Column
  29550. * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
  29551. * Bar
  29552. * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
  29553. * Area
  29554. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
  29555. * Line
  29556. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
  29557. * Column
  29558. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
  29559. * Bar
  29560. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
  29561. * Area
  29562. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
  29563. * Waterfall with normal stacking
  29564. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
  29565. * Waterfall with overlap stacking
  29566. * @sample {highstock} stock/plotoptions/stacking/
  29567. * Area
  29568. *
  29569. * @type {string}
  29570. * @product highcharts highstock
  29571. * @validvalue ["normal", "overlap", "percent", "stream"]
  29572. * @apioption plotOptions.series.stacking
  29573. */
  29574. /**
  29575. * Whether to apply steps to the line. Possible values are `left`,
  29576. * `center` and `right`.
  29577. *
  29578. * @sample {highcharts} highcharts/plotoptions/line-step/
  29579. * Different step line options
  29580. * @sample {highcharts} highcharts/plotoptions/area-step/
  29581. * Stepped, stacked area
  29582. * @sample {highstock} stock/plotoptions/line-step/
  29583. * Step line
  29584. *
  29585. * @type {string}
  29586. * @since 1.2.5
  29587. * @product highcharts highstock
  29588. * @validvalue ["left", "center", "right"]
  29589. * @apioption plotOptions.series.step
  29590. */
  29591. /**
  29592. * The threshold, also called zero level or base level. For line type
  29593. * series this is only used in conjunction with
  29594. * [negativeColor](#plotOptions.series.negativeColor).
  29595. *
  29596. * @see [softThreshold](#plotOptions.series.softThreshold).
  29597. *
  29598. * @type {number|null}
  29599. * @default 0
  29600. * @since 3.0
  29601. * @product highcharts highstock
  29602. * @apioption plotOptions.series.threshold
  29603. */
  29604. /**
  29605. * Set the initial visibility of the series.
  29606. *
  29607. * @sample {highcharts} highcharts/plotoptions/series-visible/
  29608. * Two series, one hidden and one visible
  29609. * @sample {highstock} stock/plotoptions/series-visibility/
  29610. * Hidden series
  29611. *
  29612. * @type {boolean}
  29613. * @default true
  29614. * @apioption plotOptions.series.visible
  29615. */
  29616. /**
  29617. * Defines the Axis on which the zones are applied.
  29618. *
  29619. * @see [zones](#plotOptions.series.zones)
  29620. *
  29621. * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
  29622. * Zones on the X-Axis
  29623. * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
  29624. * Zones on the X-Axis
  29625. *
  29626. * @type {string}
  29627. * @default y
  29628. * @since 4.1.0
  29629. * @product highcharts highstock
  29630. * @apioption plotOptions.series.zoneAxis
  29631. */
  29632. /**
  29633. * General event handlers for the series items. These event hooks can
  29634. * also be attached to the series at run time using the
  29635. * `Highcharts.addEvent` function.
  29636. *
  29637. * @declare Highcharts.SeriesEventsOptionsObject
  29638. *
  29639. * @private
  29640. */
  29641. events: {},
  29642. /**
  29643. * Fires after the series has finished its initial animation, or in case
  29644. * animation is disabled, immediately as the series is displayed.
  29645. *
  29646. * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
  29647. * Show label after animate
  29648. * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
  29649. * Show label after animate
  29650. *
  29651. * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
  29652. * @since 4.0
  29653. * @product highcharts highstock gantt
  29654. * @context Highcharts.Series
  29655. * @apioption plotOptions.series.events.afterAnimate
  29656. */
  29657. /**
  29658. * Fires when the checkbox next to the series' name in the legend is
  29659. * clicked. One parameter, `event`, is passed to the function. The state
  29660. * of the checkbox is found by `event.checked`. The checked item is
  29661. * found by `event.item`. Return `false` to prevent the default action
  29662. * which is to toggle the select state of the series.
  29663. *
  29664. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  29665. * Alert checkbox status
  29666. *
  29667. * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
  29668. * @since 1.2.0
  29669. * @context Highcharts.Series
  29670. * @apioption plotOptions.series.events.checkboxClick
  29671. */
  29672. /**
  29673. * Fires when the series is clicked. One parameter, `event`, is passed
  29674. * to the function, containing common event information. Additionally,
  29675. * `event.point` holds a pointer to the nearest point on the graph.
  29676. *
  29677. * @sample {highcharts} highcharts/plotoptions/series-events-click/
  29678. * Alert click info
  29679. * @sample {highstock} stock/plotoptions/series-events-click/
  29680. * Alert click info
  29681. * @sample {highmaps} maps/plotoptions/series-events-click/
  29682. * Display click info in subtitle
  29683. *
  29684. * @type {Highcharts.SeriesClickCallbackFunction}
  29685. * @context Highcharts.Series
  29686. * @apioption plotOptions.series.events.click
  29687. */
  29688. /**
  29689. * Fires when the series is hidden after chart generation time, either
  29690. * by clicking the legend item or by calling `.hide()`.
  29691. *
  29692. * @sample {highcharts} highcharts/plotoptions/series-events-hide/
  29693. * Alert when the series is hidden by clicking the legend item
  29694. *
  29695. * @type {Highcharts.SeriesHideCallbackFunction}
  29696. * @since 1.2.0
  29697. * @context Highcharts.Series
  29698. * @apioption plotOptions.series.events.hide
  29699. */
  29700. /**
  29701. * Fires when the legend item belonging to the series is clicked. One
  29702. * parameter, `event`, is passed to the function. The default action
  29703. * is to toggle the visibility of the series. This can be prevented
  29704. * by returning `false` or calling `event.preventDefault()`.
  29705. *
  29706. * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
  29707. * Confirm hiding and showing
  29708. *
  29709. * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
  29710. * @context Highcharts.Series
  29711. * @apioption plotOptions.series.events.legendItemClick
  29712. */
  29713. /**
  29714. * Fires when the mouse leaves the graph. One parameter, `event`, is
  29715. * passed to the function, containing common event information. If the
  29716. * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
  29717. * doesn't happen before the mouse enters another graph or leaves the
  29718. * plot area.
  29719. *
  29720. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29721. * With sticky tracking by default
  29722. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29723. * Without sticky tracking
  29724. *
  29725. * @type {Highcharts.SeriesMouseOutCallbackFunction}
  29726. * @context Highcharts.Series
  29727. * @apioption plotOptions.series.events.mouseOut
  29728. */
  29729. /**
  29730. * Fires when the mouse enters the graph. One parameter, `event`, is
  29731. * passed to the function, containing common event information.
  29732. *
  29733. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29734. * With sticky tracking by default
  29735. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29736. * Without sticky tracking
  29737. *
  29738. * @type {Highcharts.SeriesMouseOverCallbackFunction}
  29739. * @context Highcharts.Series
  29740. * @apioption plotOptions.series.events.mouseOver
  29741. */
  29742. /**
  29743. * Fires when the series is shown after chart generation time, either
  29744. * by clicking the legend item or by calling `.show()`.
  29745. *
  29746. * @sample {highcharts} highcharts/plotoptions/series-events-show/
  29747. * Alert when the series is shown by clicking the legend item.
  29748. *
  29749. * @type {Highcharts.SeriesShowCallbackFunction}
  29750. * @since 1.2.0
  29751. * @context Highcharts.Series
  29752. * @apioption plotOptions.series.events.show
  29753. */
  29754. /**
  29755. * Options for the point markers of line and scatter-like series. Properties
  29756. * like `fillColor`, `lineColor` and `lineWidth` define the visual
  29757. * appearance of the markers. The `symbol` option defines the shape. Other
  29758. * series types, like column series, don't have markers, but have visual
  29759. * options on the series level instead.
  29760. *
  29761. * In styled mode, the markers can be styled with the `.highcharts-point`,
  29762. * `.highcharts-point-hover` and `.highcharts-point-select` class names.
  29763. *
  29764. * @declare Highcharts.PointMarkerOptionsObject
  29765. *
  29766. * @sample {highmaps} maps/demo/mappoint-mapmarker
  29767. * Using the mapmarker symbol for points
  29768. *
  29769. * @private
  29770. */
  29771. marker: {
  29772. /**
  29773. * Enable or disable the point marker. If `undefined`, the markers
  29774. * are hidden when the data is dense, and shown for more widespread
  29775. * data points.
  29776. *
  29777. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
  29778. * Disabled markers
  29779. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
  29780. * Disabled in normal state but enabled on hover
  29781. * @sample {highstock} stock/plotoptions/series-marker/
  29782. * Enabled markers
  29783. *
  29784. * @type {boolean}
  29785. * @default {highcharts} undefined
  29786. * @default {highstock} false
  29787. * @apioption plotOptions.series.marker.enabled
  29788. */
  29789. /**
  29790. * The threshold for how dense the point markers should be before
  29791. * they are hidden, given that `enabled` is not defined. The number
  29792. * indicates the horizontal distance between the two closest points
  29793. * in the series, as multiples of the `marker.radius`. In other
  29794. * words, the default value of 2 means points are hidden if
  29795. * overlapping horizontally.
  29796. *
  29797. * @sample highcharts/plotoptions/series-marker-enabledthreshold
  29798. * A higher threshold
  29799. *
  29800. * @since 6.0.5
  29801. */
  29802. enabledThreshold: 2,
  29803. /**
  29804. * The fill color of the point marker. When `undefined`, the series'
  29805. * or point's color is used.
  29806. *
  29807. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29808. * White fill
  29809. *
  29810. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29811. * @apioption plotOptions.series.marker.fillColor
  29812. */
  29813. /**
  29814. * Image markers only. Set the image width explicitly. When using
  29815. * this option, a `width` must also be set.
  29816. *
  29817. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29818. * Fixed width and height
  29819. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29820. * Fixed width and height
  29821. *
  29822. * @type {number}
  29823. * @since 4.0.4
  29824. * @apioption plotOptions.series.marker.height
  29825. */
  29826. /**
  29827. * The color of the point marker's outline. When `undefined`, the
  29828. * series' or point's color is used.
  29829. *
  29830. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29831. * Inherit from series color (undefined)
  29832. *
  29833. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29834. */
  29835. lineColor: "#ffffff" /* Palette.backgroundColor */,
  29836. /**
  29837. * The width of the point marker's outline.
  29838. *
  29839. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29840. * 2px blue marker
  29841. */
  29842. lineWidth: 0,
  29843. /**
  29844. * The radius of the point marker.
  29845. *
  29846. * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
  29847. * Bigger markers
  29848. *
  29849. * @default {highstock} 2
  29850. * @default {highcharts} 4
  29851. *
  29852. */
  29853. radius: 4,
  29854. /**
  29855. * A predefined shape or symbol for the marker. When undefined, the
  29856. * symbol is pulled from options.symbols. Other possible values are
  29857. * `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
  29858. * `'triangle-down'`.
  29859. *
  29860. * Additionally, the URL to a graphic can be given on this form:
  29861. * `'url(graphic.png)'`. Note that for the image to be applied to
  29862. * exported charts, its URL needs to be accessible by the export
  29863. * server.
  29864. *
  29865. * Custom callbacks for symbol path generation can also be added to
  29866. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  29867. * used by its method name, as shown in the demo.
  29868. *
  29869. * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
  29870. * Predefined, graphic and custom markers
  29871. * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
  29872. * Predefined, graphic and custom markers
  29873. * @sample {highmaps} maps/demo/mappoint-mapmarker
  29874. * Using the mapmarker symbol for points
  29875. *
  29876. * @type {string}
  29877. * @apioption plotOptions.series.marker.symbol
  29878. */
  29879. /**
  29880. * Image markers only. Set the image width explicitly. When using
  29881. * this option, a `height` must also be set.
  29882. *
  29883. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29884. * Fixed width and height
  29885. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29886. * Fixed width and height
  29887. *
  29888. * @type {number}
  29889. * @since 4.0.4
  29890. * @apioption plotOptions.series.marker.width
  29891. */
  29892. /**
  29893. * States for a single point marker.
  29894. *
  29895. * @declare Highcharts.PointStatesOptionsObject
  29896. */
  29897. states: {
  29898. /**
  29899. * The normal state of a single point marker. Currently only
  29900. * used for setting animation when returning to normal state
  29901. * from hover.
  29902. *
  29903. * @declare Highcharts.PointStatesNormalOptionsObject
  29904. */
  29905. normal: {
  29906. /**
  29907. * Animation when returning to normal state after hovering.
  29908. *
  29909. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  29910. */
  29911. animation: true
  29912. },
  29913. /**
  29914. * The hover state for a single point marker.
  29915. *
  29916. * @declare Highcharts.PointStatesHoverOptionsObject
  29917. */
  29918. hover: {
  29919. /**
  29920. * Animation when hovering over the marker.
  29921. *
  29922. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  29923. */
  29924. animation: {
  29925. /** @internal */
  29926. duration: 150
  29927. },
  29928. /**
  29929. * Enable or disable the point marker.
  29930. *
  29931. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
  29932. * Disabled hover state
  29933. */
  29934. enabled: true,
  29935. /**
  29936. * The fill color of the marker in hover state. When
  29937. * `undefined`, the series' or point's fillColor for normal
  29938. * state is used.
  29939. *
  29940. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29941. * @apioption plotOptions.series.marker.states.hover.fillColor
  29942. */
  29943. /**
  29944. * The color of the point marker's outline. When
  29945. * `undefined`, the series' or point's lineColor for normal
  29946. * state is used.
  29947. *
  29948. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
  29949. * White fill color, black line color
  29950. *
  29951. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29952. * @apioption plotOptions.series.marker.states.hover.lineColor
  29953. */
  29954. /**
  29955. * The width of the point marker's outline. When
  29956. * `undefined`, the series' or point's lineWidth for normal
  29957. * state is used.
  29958. *
  29959. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
  29960. * 3px line width
  29961. *
  29962. * @type {number}
  29963. * @apioption plotOptions.series.marker.states.hover.lineWidth
  29964. */
  29965. /**
  29966. * The radius of the point marker. In hover state, it
  29967. * defaults to the normal state's radius + 2 as per the
  29968. * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
  29969. * option.
  29970. *
  29971. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
  29972. * 10px radius
  29973. *
  29974. * @type {number}
  29975. * @apioption plotOptions.series.marker.states.hover.radius
  29976. */
  29977. /**
  29978. * The number of pixels to increase the radius of the
  29979. * hovered point.
  29980. *
  29981. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29982. * 5 pixels greater radius on hover
  29983. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29984. * 5 pixels greater radius on hover
  29985. *
  29986. * @since 4.0.3
  29987. */
  29988. radiusPlus: 2,
  29989. /**
  29990. * The additional line width for a hovered point.
  29991. *
  29992. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29993. * 2 pixels wider on hover
  29994. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29995. * 2 pixels wider on hover
  29996. *
  29997. * @since 4.0.3
  29998. */
  29999. lineWidthPlus: 1
  30000. },
  30001. /**
  30002. * The appearance of the point marker when selected. In order to
  30003. * allow a point to be selected, set the
  30004. * `series.allowPointSelect` option to true.
  30005. *
  30006. * @declare Highcharts.PointStatesSelectOptionsObject
  30007. */
  30008. select: {
  30009. /**
  30010. * Enable or disable visible feedback for selection.
  30011. *
  30012. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
  30013. * Disabled select state
  30014. *
  30015. * @type {boolean}
  30016. * @default true
  30017. * @apioption plotOptions.series.marker.states.select.enabled
  30018. */
  30019. /**
  30020. * The radius of the point marker. In hover state, it
  30021. * defaults to the normal state's radius + 2.
  30022. *
  30023. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
  30024. * 10px radius for selected points
  30025. *
  30026. * @type {number}
  30027. * @apioption plotOptions.series.marker.states.select.radius
  30028. */
  30029. /**
  30030. * The fill color of the point marker.
  30031. *
  30032. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
  30033. * Solid red discs for selected points
  30034. *
  30035. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30036. */
  30037. fillColor: "#cccccc" /* Palette.neutralColor20 */,
  30038. /**
  30039. * The color of the point marker's outline. When
  30040. * `undefined`, the series' or point's color is used.
  30041. *
  30042. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
  30043. * Red line color for selected points
  30044. *
  30045. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30046. */
  30047. lineColor: "#000000" /* Palette.neutralColor100 */,
  30048. /**
  30049. * The width of the point marker's outline.
  30050. *
  30051. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
  30052. * 3px line width for selected points
  30053. */
  30054. lineWidth: 2
  30055. }
  30056. }
  30057. },
  30058. /**
  30059. * Properties for each single point.
  30060. *
  30061. * @declare Highcharts.PlotSeriesPointOptions
  30062. *
  30063. * @private
  30064. */
  30065. point: {
  30066. /**
  30067. * Fires when a point is clicked. One parameter, `event`, is passed
  30068. * to the function, containing common event information.
  30069. *
  30070. * If the `series.allowPointSelect` option is true, the default
  30071. * action for the point's click event is to toggle the point's
  30072. * select state. Returning `false` cancels this action.
  30073. *
  30074. * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
  30075. * Click marker to alert values
  30076. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
  30077. * Click column
  30078. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
  30079. * Go to URL
  30080. * @sample {highmaps} maps/plotoptions/series-point-events-click/
  30081. * Click marker to display values
  30082. * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
  30083. * Go to URL
  30084. *
  30085. * @type {Highcharts.PointClickCallbackFunction}
  30086. * @context Highcharts.Point
  30087. * @apioption plotOptions.series.point.events.click
  30088. */
  30089. /**
  30090. * Fires when the mouse leaves the area close to the point. One
  30091. * parameter, `event`, is passed to the function, containing common
  30092. * event information.
  30093. *
  30094. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  30095. * Show values in the chart's corner on mouse over
  30096. *
  30097. * @type {Highcharts.PointMouseOutCallbackFunction}
  30098. * @context Highcharts.Point
  30099. * @apioption plotOptions.series.point.events.mouseOut
  30100. */
  30101. /**
  30102. * Fires when the mouse enters the area close to the point. One
  30103. * parameter, `event`, is passed to the function, containing common
  30104. * event information.
  30105. *
  30106. * Returning `false` cancels the default behavior, which is to show a
  30107. * tooltip for the point.
  30108. *
  30109. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  30110. * Show values in the chart's corner on mouse over
  30111. *
  30112. * @type {Highcharts.PointMouseOverCallbackFunction}
  30113. * @context Highcharts.Point
  30114. * @apioption plotOptions.series.point.events.mouseOver
  30115. */
  30116. /**
  30117. * Fires when the point is removed using the `.remove()` method. One
  30118. * parameter, `event`, is passed to the function. Returning `false`
  30119. * cancels the operation.
  30120. *
  30121. * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
  30122. * Remove point and confirm
  30123. *
  30124. * @type {Highcharts.PointRemoveCallbackFunction}
  30125. * @since 1.2.0
  30126. * @context Highcharts.Point
  30127. * @apioption plotOptions.series.point.events.remove
  30128. */
  30129. /**
  30130. * Fires when the point is selected either programmatically or
  30131. * following a click on the point. One parameter, `event`, is passed
  30132. * to the function. Returning `false` cancels the operation.
  30133. *
  30134. * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
  30135. * Report the last selected point
  30136. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30137. * Report select and unselect
  30138. *
  30139. * @type {Highcharts.PointSelectCallbackFunction}
  30140. * @since 1.2.0
  30141. * @context Highcharts.Point
  30142. * @apioption plotOptions.series.point.events.select
  30143. */
  30144. /**
  30145. * Fires when the point is unselected either programmatically or
  30146. * following a click on the point. One parameter, `event`, is passed
  30147. * to the function.
  30148. * Returning `false` cancels the operation.
  30149. *
  30150. * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
  30151. * Report the last unselected point
  30152. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30153. * Report select and unselect
  30154. *
  30155. * @type {Highcharts.PointUnselectCallbackFunction}
  30156. * @since 1.2.0
  30157. * @context Highcharts.Point
  30158. * @apioption plotOptions.series.point.events.unselect
  30159. */
  30160. /**
  30161. * Fires when the point is updated programmatically through the
  30162. * `.update()` method. One parameter, `event`, is passed to the
  30163. * function. The new point options can be accessed through
  30164. * `event.options`. Returning `false` cancels the operation.
  30165. *
  30166. * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
  30167. * Confirm point updating
  30168. *
  30169. * @type {Highcharts.PointUpdateCallbackFunction}
  30170. * @since 1.2.0
  30171. * @context Highcharts.Point
  30172. * @apioption plotOptions.series.point.events.update
  30173. */
  30174. /**
  30175. * Events for each single point.
  30176. *
  30177. * @declare Highcharts.PointEventsOptionsObject
  30178. */
  30179. events: {}
  30180. },
  30181. /**
  30182. * Options for the series data labels, appearing next to each data
  30183. * point.
  30184. *
  30185. * Since v6.2.0, multiple data labels can be applied to each single
  30186. * point by defining them as an array of configs.
  30187. *
  30188. * In styled mode, the data labels can be styled with the
  30189. * `.highcharts-data-label-box` and `.highcharts-data-label` class names
  30190. * ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
  30191. *
  30192. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
  30193. * Data labels enabled
  30194. * @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
  30195. * Multiple data labels on a bar series
  30196. * @sample {highcharts} highcharts/css/series-datalabels
  30197. * Styled mode example
  30198. * @sample {highmaps} maps/demo/color-axis
  30199. * Choropleth map with data labels
  30200. * @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
  30201. * Using data labels as map markers
  30202. *
  30203. * @type {*|Array<*>}
  30204. * @product highcharts highstock highmaps gantt
  30205. *
  30206. * @private
  30207. */
  30208. dataLabels: {
  30209. /**
  30210. * Enable or disable the initial animation when a series is displayed
  30211. * for the `dataLabels`. The animation can also be set as a
  30212. * configuration object. Please note that this option only applies to
  30213. * the initial animation.
  30214. *
  30215. * For other animations, see [chart.animation](#chart.animation) and the
  30216. * animation parameter under the API methods. The following properties
  30217. * are supported:
  30218. *
  30219. * - `defer`: The animation delay time in milliseconds.
  30220. *
  30221. * @sample {highcharts} highcharts/plotoptions/animation-defer/
  30222. * Animation defer settings
  30223. *
  30224. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30225. * @since 8.2.0
  30226. * @apioption plotOptions.series.dataLabels.animation
  30227. */
  30228. animation: {},
  30229. /**
  30230. * The animation delay time in milliseconds. Set to `0` to render the
  30231. * data labels immediately. As `undefined` inherits defer time from the
  30232. * [series.animation.defer](#plotOptions.series.animation.defer).
  30233. *
  30234. * @type {number}
  30235. * @since 8.2.0
  30236. * @apioption plotOptions.series.dataLabels.animation.defer
  30237. */
  30238. /**
  30239. * The alignment of the data label compared to the point. If `right`,
  30240. * the right side of the label should be touching the point. For points
  30241. * with an extent, like columns, the alignments also dictates how to
  30242. * align it inside the box, as given with the
  30243. * [inside](#plotOptions.column.dataLabels.inside) option. Can be one of
  30244. * `left`, `center` or `right`.
  30245. *
  30246. * @sample {highcharts}
  30247. * highcharts/plotoptions/series-datalabels-align-left/ Left
  30248. * aligned
  30249. * @sample {highcharts}
  30250. * highcharts/plotoptions/bar-datalabels-align-inside-bar/ Data
  30251. * labels inside the bar
  30252. *
  30253. * @type {Highcharts.AlignValue|null}
  30254. */
  30255. align: 'center',
  30256. /**
  30257. * Whether to allow data labels to overlap. To make the labels less
  30258. * sensitive for overlapping, the
  30259. * [dataLabels.padding](#plotOptions.series.dataLabels.padding)
  30260. * can be set to 0.
  30261. *
  30262. * @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
  30263. * Don't allow overlap
  30264. *
  30265. * @type {boolean}
  30266. * @default false
  30267. * @since 4.1.0
  30268. * @apioption plotOptions.series.dataLabels.allowOverlap
  30269. */
  30270. /**
  30271. * The background color or gradient for the data label. Setting it to
  30272. * `auto` will use the point's color.
  30273. *
  30274. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30275. * Data labels box options
  30276. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30277. * Data labels box options
  30278. * @sample {highmaps} maps/demo/mappoint-datalabels-mapmarker
  30279. * Data labels as map markers
  30280. *
  30281. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30282. * @since 2.2.1
  30283. * @apioption plotOptions.series.dataLabels.backgroundColor
  30284. */
  30285. /**
  30286. * The border color for the data label. Setting it to `auto` will use
  30287. * the point's color. Defaults to `undefined`.
  30288. *
  30289. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30290. * Data labels box options
  30291. *
  30292. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30293. * @since 2.2.1
  30294. * @apioption plotOptions.series.dataLabels.borderColor
  30295. */
  30296. /**
  30297. * The border radius in pixels for the data label.
  30298. *
  30299. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30300. * Data labels box options
  30301. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30302. * Data labels box options
  30303. *
  30304. * @type {number}
  30305. * @default 0
  30306. * @since 2.2.1
  30307. * @apioption plotOptions.series.dataLabels.borderRadius
  30308. */
  30309. /**
  30310. * The border width in pixels for the data label.
  30311. *
  30312. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30313. * Data labels box options
  30314. *
  30315. * @type {number}
  30316. * @default 0
  30317. * @since 2.2.1
  30318. * @apioption plotOptions.series.dataLabels.borderWidth
  30319. */
  30320. borderWidth: 0,
  30321. /**
  30322. * A class name for the data label. Particularly in styled mode,
  30323. * this can be used to give each series' or point's data label
  30324. * unique styling. In addition to this option, a default color class
  30325. * name is added so that we can give the labels a contrast text
  30326. * shadow.
  30327. *
  30328. * @sample {highcharts} highcharts/css/data-label-contrast/
  30329. * Contrast text shadow
  30330. * @sample {highcharts} highcharts/css/series-datalabels/
  30331. * Styling by CSS
  30332. *
  30333. * @type {string}
  30334. * @since 5.0.0
  30335. * @apioption plotOptions.series.dataLabels.className
  30336. */
  30337. /**
  30338. * This options is deprecated.
  30339. * Use [style.color](#plotOptions.series.dataLabels.style) instead.
  30340. *
  30341. * The text color for the data labels. Defaults to `undefined`. For
  30342. * certain series types, like column or map, the data labels can be
  30343. * drawn inside the points. In this case the data label will be
  30344. * drawn with maximum contrast by default. Additionally, it will be
  30345. * given a `text-outline` style with the opposite color, to further
  30346. * increase the contrast. This can be overridden by setting the
  30347. * `text-outline` style to `none` in the `dataLabels.style` option.
  30348. *
  30349. * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
  30350. * Red data labels
  30351. * @sample {highmaps} maps/demo/color-axis/
  30352. * White data labels
  30353. *
  30354. * @see [style.color](#plotOptions.series.dataLabels.style)
  30355. *
  30356. * @type {Highcharts.ColorType}
  30357. * @deprecated 10.3
  30358. * @apioption plotOptions.series.dataLabels.color
  30359. */
  30360. /**
  30361. * Whether to hide data labels that are outside the plot area. By
  30362. * default, the data label is moved inside the plot area according
  30363. * to the
  30364. * [overflow](#plotOptions.series.dataLabels.overflow)
  30365. * option.
  30366. *
  30367. * @type {boolean}
  30368. * @default true
  30369. * @since 2.3.3
  30370. * @apioption plotOptions.series.dataLabels.crop
  30371. */
  30372. /**
  30373. * Whether to defer displaying the data labels until the initial
  30374. * series animation has finished. Setting to `false` renders the
  30375. * data label immediately. If set to `true` inherits the defer
  30376. * time set in [plotOptions.series.animation](#plotOptions.series.animation).
  30377. *
  30378. * @since 4.0.0
  30379. * @type {boolean}
  30380. * @product highcharts highstock gantt
  30381. */
  30382. defer: true,
  30383. /**
  30384. * Enable or disable the data labels.
  30385. *
  30386. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
  30387. * Data labels enabled
  30388. * @sample {highmaps} maps/demo/color-axis/
  30389. * Data labels enabled
  30390. *
  30391. * @type {boolean}
  30392. * @default false
  30393. * @apioption plotOptions.series.dataLabels.enabled
  30394. */
  30395. /**
  30396. * A declarative filter to control of which data labels to display.
  30397. * The declarative filter is designed for use when callback
  30398. * functions are not available, like when the chart options require
  30399. * a pure JSON structure or for use with graphical editors. For
  30400. * programmatic control, use the `formatter` instead, and return
  30401. * `undefined` to disable a single data label.
  30402. *
  30403. * @example
  30404. * filter: {
  30405. * property: 'percentage',
  30406. * operator: '>',
  30407. * value: 4
  30408. * }
  30409. *
  30410. * @sample {highcharts} highcharts/demo/pie-monochrome
  30411. * Data labels filtered by percentage
  30412. *
  30413. * @declare Highcharts.DataLabelsFilterOptionsObject
  30414. * @since 6.0.3
  30415. * @apioption plotOptions.series.dataLabels.filter
  30416. */
  30417. /**
  30418. * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
  30419. * `==`, and `===`.
  30420. *
  30421. * @type {string}
  30422. * @validvalue [">", "<", ">=", "<=", "==", "==="]
  30423. * @apioption plotOptions.series.dataLabels.filter.operator
  30424. */
  30425. /**
  30426. * The point property to filter by. Point options are passed
  30427. * directly to properties, additionally there are `y` value,
  30428. * `percentage` and others listed under {@link Highcharts.Point}
  30429. * members.
  30430. *
  30431. * @type {string}
  30432. * @apioption plotOptions.series.dataLabels.filter.property
  30433. */
  30434. /**
  30435. * The value to compare against.
  30436. *
  30437. * @type {number}
  30438. * @apioption plotOptions.series.dataLabels.filter.value
  30439. */
  30440. /**
  30441. * A
  30442. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  30443. * for the data label. Available variables are the same as for
  30444. * `formatter`.
  30445. *
  30446. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  30447. * Add a unit
  30448. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format-subexpression/
  30449. * Complex logic in the format string
  30450. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30451. * Formatted value in the data label
  30452. *
  30453. * @type {string}
  30454. * @default y
  30455. * @default point.value
  30456. * @since 3.0
  30457. * @apioption plotOptions.series.dataLabels.format
  30458. */
  30459. // eslint-disable-next-line valid-jsdoc
  30460. /**
  30461. * Callback JavaScript function to format the data label. Note that if a
  30462. * `format` is defined, the format takes precedence and the formatter is
  30463. * ignored.
  30464. *
  30465. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30466. * Formatted value
  30467. *
  30468. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30469. */
  30470. formatter: function () {
  30471. const { numberFormatter } = this.series.chart;
  30472. return typeof this.y !== 'number' ?
  30473. '' : numberFormatter(this.y, -1);
  30474. },
  30475. /**
  30476. * For points with an extent, like columns or map areas, whether to
  30477. * align the data label inside the box or to the actual value point.
  30478. * Defaults to `false` in most cases, `true` in stacked columns.
  30479. *
  30480. * @type {boolean}
  30481. * @since 3.0
  30482. * @apioption plotOptions.series.dataLabels.inside
  30483. */
  30484. /**
  30485. * Format for points with the value of null. Works analogously to
  30486. * [format](#plotOptions.series.dataLabels.format). `nullFormat` can
  30487. * be applied only to series which support displaying null points
  30488. * i.e `heatmap` or `tilemap`. Does not work with series that don't
  30489. * display null points, like `line`, `column`, `bar` or `pie`.
  30490. *
  30491. * @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
  30492. * Format data label for null points in heat map
  30493. *
  30494. * @type {boolean|string}
  30495. * @since 7.1.0
  30496. * @apioption plotOptions.series.dataLabels.nullFormat
  30497. */
  30498. /**
  30499. * Callback JavaScript function that defines formatting for points
  30500. * with the value of null. Works analogously to
  30501. * [formatter](#plotOptions.series.dataLabels.formatter).
  30502. * `nullFormatter` can be applied only to series which support
  30503. * displaying null points i.e `heatmap` or `tilemap`. Does not work
  30504. * with series that don't display null points, like `line`, `column`,
  30505. * `bar` or `pie`.
  30506. *
  30507. * @sample {highcharts} highcharts/plotoptions/series-datalabels-nullformat/
  30508. * Format data label for null points in heat map
  30509. *
  30510. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30511. * @since 7.1.0
  30512. * @apioption plotOptions.series.dataLabels.nullFormatter
  30513. */
  30514. /**
  30515. * How to handle data labels that flow outside the plot area. The
  30516. * default is `"justify"`, which aligns them inside the plot area.
  30517. * For columns and bars, this means it will be moved inside the bar.
  30518. * To display data labels outside the plot area, set `crop` to
  30519. * `false` and `overflow` to `"allow"`.
  30520. *
  30521. * @type {Highcharts.DataLabelsOverflowValue}
  30522. * @default justify
  30523. * @since 3.0.6
  30524. * @apioption plotOptions.series.dataLabels.overflow
  30525. */
  30526. /**
  30527. * When either the `borderWidth` or the `backgroundColor` is set,
  30528. * this is the padding within the box.
  30529. *
  30530. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30531. * Data labels box options
  30532. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30533. * Data labels box options
  30534. *
  30535. * @since 2.2.1
  30536. */
  30537. padding: 5,
  30538. /**
  30539. * Aligns data labels relative to points. If `center` alignment is
  30540. * not possible, it defaults to `right`.
  30541. *
  30542. * @type {Highcharts.AlignValue}
  30543. * @default center
  30544. * @apioption plotOptions.series.dataLabels.position
  30545. */
  30546. /**
  30547. * Text rotation in degrees. Note that due to a more complex
  30548. * structure, backgrounds, borders and padding will be lost on a
  30549. * rotated data label.
  30550. *
  30551. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30552. * Vertical labels
  30553. *
  30554. * @type {number}
  30555. * @default 0
  30556. * @apioption plotOptions.series.dataLabels.rotation
  30557. */
  30558. /**
  30559. * The shadow of the box. Works best with `borderWidth` or
  30560. * `backgroundColor`. Since 2.3 the shadow can be an object
  30561. * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
  30562. * and `width`.
  30563. *
  30564. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30565. * Data labels box options
  30566. *
  30567. * @type {boolean|Highcharts.ShadowOptionsObject}
  30568. * @default false
  30569. * @since 2.2.1
  30570. * @apioption plotOptions.series.dataLabels.shadow
  30571. */
  30572. /**
  30573. * The name of a symbol to use for the border around the label.
  30574. * Symbols are predefined functions on the Renderer object.
  30575. *
  30576. * @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
  30577. * A callout for annotations
  30578. *
  30579. * @type {string}
  30580. * @default square
  30581. * @since 4.1.2
  30582. * @apioption plotOptions.series.dataLabels.shape
  30583. */
  30584. /**
  30585. * Styles for the label. The default `color` setting is
  30586. * `"contrast"`, which is a pseudo color that Highcharts picks up
  30587. * and applies the maximum contrast to the underlying point item,
  30588. * for example the bar in a bar chart.
  30589. *
  30590. * The `textOutline` is a pseudo property that applies an outline of
  30591. * the given width with the given color, which by default is the
  30592. * maximum contrast to the text. So a bright text color will result
  30593. * in a black text outline for maximum readability on a mixed
  30594. * background. In some cases, especially with grayscale text, the
  30595. * text outline doesn't work well, in which cases it can be disabled
  30596. * by setting it to `"none"`. When `useHTML` is true, the
  30597. * `textOutline` will not be picked up. In this, case, the same
  30598. * effect can be acheived through the `text-shadow` CSS property.
  30599. *
  30600. * For some series types, where each point has an extent, like for
  30601. * example tree maps, the data label may overflow the point. There
  30602. * are two strategies for handling overflow. By default, the text
  30603. * will wrap to multiple lines. The other strategy is to set
  30604. * `style.textOverflow` to `ellipsis`, which will keep the text on
  30605. * one line plus it will break inside long words.
  30606. *
  30607. * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
  30608. * Bold labels
  30609. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow/
  30610. * Long labels truncated with an ellipsis in a pie
  30611. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap/
  30612. * Long labels are wrapped in a pie
  30613. * @sample {highmaps} maps/demo/color-axis/
  30614. * Bold labels
  30615. *
  30616. * @type {Highcharts.CSSObject}
  30617. * @since 4.1.0
  30618. * @apioption plotOptions.series.dataLabels.style
  30619. */
  30620. style: {
  30621. /** @internal */
  30622. fontSize: '0.7em',
  30623. /** @internal */
  30624. fontWeight: 'bold',
  30625. /** @internal */
  30626. color: 'contrast',
  30627. /** @internal */
  30628. textOutline: '1px contrast'
  30629. },
  30630. /**
  30631. * Options for a label text which should follow marker's shape.
  30632. * Border and background are disabled for a label that follows a
  30633. * path.
  30634. *
  30635. * **Note:** Only SVG-based renderer supports this option. Setting
  30636. * `useHTML` to true will disable this option.
  30637. *
  30638. * @declare Highcharts.DataLabelsTextPathOptionsObject
  30639. * @since 7.1.0
  30640. * @apioption plotOptions.series.dataLabels.textPath
  30641. */
  30642. /**
  30643. * Presentation attributes for the text path.
  30644. *
  30645. * @type {Highcharts.SVGAttributes}
  30646. * @since 7.1.0
  30647. * @apioption plotOptions.series.dataLabels.textPath.attributes
  30648. */
  30649. /**
  30650. * Enable or disable `textPath` option for link's or marker's data
  30651. * labels.
  30652. *
  30653. * @type {boolean}
  30654. * @since 7.1.0
  30655. * @apioption plotOptions.series.dataLabels.textPath.enabled
  30656. */
  30657. /**
  30658. * Whether to
  30659. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  30660. * to render the labels.
  30661. *
  30662. * @type {boolean}
  30663. * @default false
  30664. * @apioption plotOptions.series.dataLabels.useHTML
  30665. */
  30666. /**
  30667. * The vertical alignment of a data label. Can be one of `top`,
  30668. * `middle` or `bottom`. The default value depends on the data, for
  30669. * instance in a column chart, the label is above positive values
  30670. * and below negative values.
  30671. *
  30672. * @type {Highcharts.VerticalAlignValue|null}
  30673. * @since 2.3.3
  30674. */
  30675. verticalAlign: 'bottom',
  30676. /**
  30677. * The x position offset of the label relative to the point in
  30678. * pixels.
  30679. *
  30680. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30681. * Vertical and positioned
  30682. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  30683. * Data labels inside the bar
  30684. */
  30685. x: 0,
  30686. /**
  30687. * The z index of the data labels. Use a `zIndex` of 6 to display it above
  30688. * the series, or use a `zIndex` of 2 to display it behind the series.
  30689. *
  30690. * @type {number}
  30691. * @default 6
  30692. * @since 2.3.5
  30693. * @apioption plotOptions.series.dataLabels.zIndex
  30694. */
  30695. /**
  30696. * The y position offset of the label relative to the point in
  30697. * pixels.
  30698. *
  30699. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30700. * Vertical and positioned
  30701. */
  30702. y: 0
  30703. },
  30704. /**
  30705. * When the series contains less points than the crop threshold, all
  30706. * points are drawn, even if the points fall outside the visible plot
  30707. * area at the current zoom. The advantage of drawing all points
  30708. * (including markers and columns), is that animation is performed on
  30709. * updates. On the other hand, when the series contains more points than
  30710. * the crop threshold, the series data is cropped to only contain points
  30711. * that fall within the plot area. The advantage of cropping away
  30712. * invisible points is to increase performance on large series.
  30713. *
  30714. * @since 2.2
  30715. * @product highcharts highstock
  30716. *
  30717. * @private
  30718. */
  30719. cropThreshold: 300,
  30720. /**
  30721. * Opacity of a series parts: line, fill (e.g. area) and dataLabels.
  30722. *
  30723. * @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
  30724. *
  30725. * @since 7.1.0
  30726. *
  30727. * @private
  30728. */
  30729. opacity: 1,
  30730. /**
  30731. * The width of each point on the x axis. For example in a column chart
  30732. * with one value each day, the pointRange would be 1 day (= 24 * 3600
  30733. * * 1000 milliseconds). This is normally computed automatically, but
  30734. * this option can be used to override the automatic value.
  30735. *
  30736. * @product highstock
  30737. *
  30738. * @private
  30739. */
  30740. pointRange: 0,
  30741. /**
  30742. * When this is true, the series will not cause the Y axis to cross
  30743. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  30744. * unless the data actually crosses the plane.
  30745. *
  30746. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  30747. * 3 will make the Y axis show negative values according to the
  30748. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  30749. * at 0.
  30750. *
  30751. * @since 4.1.9
  30752. * @product highcharts highstock
  30753. *
  30754. * @private
  30755. */
  30756. softThreshold: true,
  30757. /**
  30758. * @declare Highcharts.SeriesStatesOptionsObject
  30759. *
  30760. * @private
  30761. */
  30762. states: {
  30763. /**
  30764. * The normal state of a series, or for point items in column, pie
  30765. * and similar series. Currently only used for setting animation
  30766. * when returning to normal state from hover.
  30767. *
  30768. * @declare Highcharts.SeriesStatesNormalOptionsObject
  30769. */
  30770. normal: {
  30771. /**
  30772. * Animation when returning to normal state after hovering.
  30773. *
  30774. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30775. */
  30776. animation: true
  30777. },
  30778. /**
  30779. * Options for the hovered series. These settings override the
  30780. * normal state options when a series is moused over or touched.
  30781. *
  30782. * @declare Highcharts.SeriesStatesHoverOptionsObject
  30783. */
  30784. hover: {
  30785. /**
  30786. * Enable separate styles for the hovered series to visualize
  30787. * that the user hovers either the series itself or the legend.
  30788. *
  30789. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
  30790. * Line
  30791. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
  30792. * Column
  30793. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
  30794. * Pie
  30795. *
  30796. * @type {boolean}
  30797. * @default true
  30798. * @since 1.2
  30799. * @apioption plotOptions.series.states.hover.enabled
  30800. */
  30801. /**
  30802. * Animation setting for hovering the graph in line-type series.
  30803. *
  30804. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30805. * @since 5.0.8
  30806. * @product highcharts highstock
  30807. */
  30808. animation: {
  30809. /**
  30810. * The duration of the hover animation in milliseconds. By
  30811. * default the hover state animates quickly in, and slowly
  30812. * back to normal.
  30813. *
  30814. * @internal
  30815. */
  30816. duration: 150
  30817. },
  30818. /**
  30819. * Pixel width of the graph line. By default this property is
  30820. * undefined, and the `lineWidthPlus` property dictates how much
  30821. * to increase the linewidth from normal state.
  30822. *
  30823. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
  30824. * 5px line on hover
  30825. *
  30826. * @type {number}
  30827. * @product highcharts highstock
  30828. * @apioption plotOptions.series.states.hover.lineWidth
  30829. */
  30830. /**
  30831. * The additional line width for the graph of a hovered series.
  30832. *
  30833. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  30834. * 5 pixels wider
  30835. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  30836. * 5 pixels wider
  30837. *
  30838. * @since 4.0.3
  30839. * @product highcharts highstock
  30840. */
  30841. lineWidthPlus: 1,
  30842. /**
  30843. * In Highcharts 1.0, the appearance of all markers belonging
  30844. * to the hovered series. For settings on the hover state of the
  30845. * individual point, see
  30846. * [marker.states.hover](#plotOptions.series.marker.states.hover).
  30847. *
  30848. * @deprecated
  30849. *
  30850. * @extends plotOptions.series.marker
  30851. * @excluding states, symbol
  30852. * @product highcharts highstock
  30853. */
  30854. marker: {
  30855. // lineWidth: base + 1,
  30856. // radius: base + 1
  30857. },
  30858. /**
  30859. * Options for the halo appearing around the hovered point in
  30860. * line-type series as well as outside the hovered slice in pie
  30861. * charts. By default the halo is filled by the current point or
  30862. * series color with an opacity of 0.25\. The halo can be
  30863. * disabled by setting the `halo` option to `null`.
  30864. *
  30865. * In styled mode, the halo is styled with the
  30866. * `.highcharts-halo` class, with colors inherited from
  30867. * `.highcharts-color-{n}`.
  30868. *
  30869. * @sample {highcharts} highcharts/plotoptions/halo/
  30870. * Halo options
  30871. * @sample {highstock} highcharts/plotoptions/halo/
  30872. * Halo options
  30873. *
  30874. * @declare Highcharts.SeriesStatesHoverHaloOptionsObject
  30875. * @type {null|*}
  30876. * @since 4.0
  30877. * @product highcharts highstock
  30878. */
  30879. halo: {
  30880. /**
  30881. * A collection of SVG attributes to override the appearance
  30882. * of the halo, for example `fill`, `stroke` and
  30883. * `stroke-width`.
  30884. *
  30885. * @type {Highcharts.SVGAttributes}
  30886. * @since 4.0
  30887. * @product highcharts highstock
  30888. * @apioption plotOptions.series.states.hover.halo.attributes
  30889. */
  30890. /**
  30891. * The pixel size of the halo. For point markers this is the
  30892. * radius of the halo. For pie slices it is the width of the
  30893. * halo outside the slice. For bubbles it defaults to 5 and
  30894. * is the width of the halo outside the bubble.
  30895. *
  30896. * @since 4.0
  30897. * @product highcharts highstock
  30898. */
  30899. size: 10,
  30900. /**
  30901. * Opacity for the halo unless a specific fill is overridden
  30902. * using the `attributes` setting. Note that Highcharts is
  30903. * only able to apply opacity to colors of hex or rgb(a)
  30904. * formats.
  30905. *
  30906. * @since 4.0
  30907. * @product highcharts highstock
  30908. */
  30909. opacity: 0.25
  30910. }
  30911. },
  30912. /**
  30913. * Specific options for point in selected states, after being
  30914. * selected by
  30915. * [allowPointSelect](#plotOptions.series.allowPointSelect)
  30916. * or programmatically.
  30917. *
  30918. * @sample maps/plotoptions/series-allowpointselect/
  30919. * Allow point select demo
  30920. *
  30921. * @declare Highcharts.SeriesStatesSelectOptionsObject
  30922. * @extends plotOptions.series.states.hover
  30923. * @excluding brightness
  30924. */
  30925. select: {
  30926. animation: {
  30927. /** @internal */
  30928. duration: 0
  30929. }
  30930. },
  30931. /**
  30932. * The opposite state of a hover for series.
  30933. *
  30934. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30935. * Disabled inactive state
  30936. *
  30937. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  30938. */
  30939. inactive: {
  30940. /**
  30941. * Enable or disable the inactive state for a series
  30942. *
  30943. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30944. * Disabled inactive state
  30945. *
  30946. * @type {boolean}
  30947. * @default true
  30948. * @apioption plotOptions.series.states.inactive.enabled
  30949. */
  30950. /**
  30951. * The animation for entering the inactive state.
  30952. *
  30953. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  30954. */
  30955. animation: {
  30956. /** @internal */
  30957. duration: 150
  30958. },
  30959. /**
  30960. * Opacity of series elements (dataLabels, line, area).
  30961. *
  30962. * @type {number}
  30963. */
  30964. opacity: 0.2
  30965. }
  30966. },
  30967. /**
  30968. * Sticky tracking of mouse events. When true, the `mouseOut` event on a
  30969. * series isn't triggered until the mouse moves over another series, or
  30970. * out of the plot area. When false, the `mouseOut` event on a series is
  30971. * triggered when the mouse leaves the area around the series' graph or
  30972. * markers. This also implies the tooltip when not shared. When
  30973. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  30974. * will be hidden when moving the mouse between series. Defaults to true
  30975. * for line and area type series, but to false for columns, pies etc.
  30976. *
  30977. * **Note:** The boost module will force this option because of
  30978. * technical limitations.
  30979. *
  30980. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
  30981. * True by default
  30982. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
  30983. * False
  30984. *
  30985. * @default {highcharts} true
  30986. * @default {highstock} true
  30987. * @default {highmaps} false
  30988. * @since 2.0
  30989. *
  30990. * @private
  30991. */
  30992. stickyTracking: true,
  30993. /**
  30994. * A configuration object for the tooltip rendering of each single
  30995. * series. Properties are inherited from [tooltip](#tooltip), but only
  30996. * the following properties can be defined on a series level.
  30997. *
  30998. * @declare Highcharts.SeriesTooltipOptionsObject
  30999. * @since 2.3
  31000. * @extends tooltip
  31001. * @excluding animation, backgroundColor, borderColor, borderRadius,
  31002. * borderWidth, className, crosshairs, enabled, formatter,
  31003. * headerShape, hideDelay, outside, padding, positioner,
  31004. * shadow, shape, shared, snap, split, stickOnContact,
  31005. * style, useHTML
  31006. * @apioption plotOptions.series.tooltip
  31007. */
  31008. /**
  31009. * When a series contains a data array that is longer than this, only
  31010. * one dimensional arrays of numbers, or two dimensional arrays with
  31011. * x and y values are allowed. Also, only the first point is tested,
  31012. * and the rest are assumed to be the same format. This saves expensive
  31013. * data checking and indexing in long series. Set it to `0` disable.
  31014. *
  31015. * Note:
  31016. * In boost mode turbo threshold is forced. Only array of numbers or
  31017. * two dimensional arrays are allowed.
  31018. *
  31019. * @since 2.2
  31020. * @product highcharts highstock gantt
  31021. *
  31022. * @private
  31023. */
  31024. turboThreshold: 1000,
  31025. /**
  31026. * An array defining zones within a series. Zones can be applied to the
  31027. * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
  31028. * option. The zone definitions have to be in ascending order regarding
  31029. * to the value.
  31030. *
  31031. * In styled mode, the color zones are styled with the
  31032. * `.highcharts-zone-{n}` class, or custom classed from the `className`
  31033. * option
  31034. * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
  31035. *
  31036. * @see [zoneAxis](#plotOptions.series.zoneAxis)
  31037. *
  31038. * @sample {highcharts} highcharts/series/color-zones-simple/
  31039. * Color zones
  31040. * @sample {highstock} highcharts/series/color-zones-simple/
  31041. * Color zones
  31042. *
  31043. * @declare Highcharts.SeriesZonesOptionsObject
  31044. * @type {Array<*>}
  31045. * @since 4.1.0
  31046. * @product highcharts highstock
  31047. * @apioption plotOptions.series.zones
  31048. */
  31049. /**
  31050. * Styled mode only. A custom class name for the zone.
  31051. *
  31052. * @sample highcharts/css/color-zones/
  31053. * Zones styled by class name
  31054. *
  31055. * @type {string}
  31056. * @since 5.0.0
  31057. * @apioption plotOptions.series.zones.className
  31058. */
  31059. /**
  31060. * Defines the color of the series.
  31061. *
  31062. * @see [series color](#plotOptions.series.color)
  31063. *
  31064. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31065. * @since 4.1.0
  31066. * @product highcharts highstock
  31067. * @apioption plotOptions.series.zones.color
  31068. */
  31069. /**
  31070. * A name for the dash style to use for the graph.
  31071. *
  31072. * @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  31073. *
  31074. * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
  31075. * Dashed line indicates prognosis
  31076. *
  31077. * @type {Highcharts.DashStyleValue}
  31078. * @since 4.1.0
  31079. * @product highcharts highstock
  31080. * @apioption plotOptions.series.zones.dashStyle
  31081. */
  31082. /**
  31083. * Defines the fill color for the series (in area type series)
  31084. *
  31085. * @see [fillColor](#plotOptions.area.fillColor)
  31086. *
  31087. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  31088. * @since 4.1.0
  31089. * @product highcharts highstock
  31090. * @apioption plotOptions.series.zones.fillColor
  31091. */
  31092. /**
  31093. * The value up to where the zone extends, if undefined the zones
  31094. * stretches to the last value in the series.
  31095. *
  31096. * @type {number}
  31097. * @since 4.1.0
  31098. * @product highcharts highstock
  31099. * @apioption plotOptions.series.zones.value
  31100. */
  31101. /**
  31102. * When using dual or multiple color axes, this number defines which
  31103. * colorAxis the particular series is connected to. It refers to
  31104. * either the
  31105. * {@link #colorAxis.id|axis id}
  31106. * or the index of the axis in the colorAxis array, with 0 being the
  31107. * first. Set this option to false to prevent a series from connecting
  31108. * to the default color axis.
  31109. *
  31110. * Since v7.2.0 the option can also be an axis id or an axis index
  31111. * instead of a boolean flag.
  31112. *
  31113. * @sample highcharts/coloraxis/coloraxis-with-pie/
  31114. * Color axis with pie series
  31115. * @sample highcharts/coloraxis/multiple-coloraxis/
  31116. * Multiple color axis
  31117. *
  31118. * @type {number|string|boolean}
  31119. * @default 0
  31120. * @product highcharts highstock highmaps
  31121. * @apioption plotOptions.series.colorAxis
  31122. */
  31123. /**
  31124. * Determines what data value should be used to calculate point color
  31125. * if `colorAxis` is used. Requires to set `min` and `max` if some
  31126. * custom point property is used or if approximation for data grouping
  31127. * is set to `'sum'`.
  31128. *
  31129. * @sample highcharts/coloraxis/custom-color-key/
  31130. * Custom color key
  31131. * @sample highcharts/coloraxis/color-key-with-stops/
  31132. * Custom colorKey with color axis stops
  31133. * @sample highcharts/coloraxis/changed-default-color-key/
  31134. * Changed default color key
  31135. *
  31136. * @type {string}
  31137. * @default y
  31138. * @since 7.2.0
  31139. * @product highcharts highstock highmaps
  31140. * @apioption plotOptions.series.colorKey
  31141. */
  31142. /**
  31143. * What type of legend symbol to render for this series. Can be one of
  31144. * `lineMarker` or `rectangle`.
  31145. *
  31146. * @validvalue ["lineMarker", "rectangle"]
  31147. *
  31148. * @sample {highcharts} highcharts/series/legend-symbol/
  31149. * Change the legend symbol
  31150. *
  31151. * @type {string}
  31152. * @default rectangle
  31153. * @since 11.0.1
  31154. * @apioption plotOptions.series.legendSymbol
  31155. */
  31156. /**
  31157. * Determines whether the series should look for the nearest point
  31158. * in both dimensions or just the x-dimension when hovering the series.
  31159. * Defaults to `'xy'` for scatter series and `'x'` for most other
  31160. * series. If the data has duplicate x-values, it is recommended to
  31161. * set this to `'xy'` to allow hovering over all points.
  31162. *
  31163. * Applies only to series types using nearest neighbor search (not
  31164. * direct hover) for tooltip.
  31165. *
  31166. * @sample {highcharts} highcharts/series/findnearestpointby/
  31167. * Different hover behaviors
  31168. * @sample {highstock} highcharts/series/findnearestpointby/
  31169. * Different hover behaviors
  31170. * @sample {highmaps} highcharts/series/findnearestpointby/
  31171. * Different hover behaviors
  31172. *
  31173. * @since 5.0.10
  31174. * @validvalue ["x", "xy"]
  31175. *
  31176. * @private
  31177. */
  31178. findNearestPointBy: 'x'
  31179. };
  31180. /* *
  31181. *
  31182. * Default Export
  31183. *
  31184. * */
  31185. return seriesDefaults;
  31186. });
  31187. _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) {
  31188. /* *
  31189. *
  31190. * (c) 2010-2021 Torstein Honsi
  31191. *
  31192. * License: www.highcharts.com/license
  31193. *
  31194. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  31195. *
  31196. * */
  31197. const { defaultOptions } = D;
  31198. const { extendClass, merge } = U;
  31199. /* *
  31200. *
  31201. * Namespace
  31202. *
  31203. * */
  31204. var SeriesRegistry;
  31205. (function (SeriesRegistry) {
  31206. /* *
  31207. *
  31208. * Properties
  31209. *
  31210. * */
  31211. /**
  31212. * @internal
  31213. * @todo Move `Globals.seriesTypes` code to her.
  31214. */
  31215. SeriesRegistry.seriesTypes = H.seriesTypes;
  31216. /* *
  31217. *
  31218. * Functions
  31219. *
  31220. * */
  31221. /**
  31222. * Registers class pattern of a series.
  31223. *
  31224. * @private
  31225. */
  31226. function registerSeriesType(seriesType, SeriesClass) {
  31227. const defaultPlotOptions = defaultOptions.plotOptions || {}, seriesOptions = SeriesClass.defaultOptions, seriesProto = SeriesClass.prototype;
  31228. seriesProto.type = seriesType;
  31229. if (!seriesProto.pointClass) {
  31230. seriesProto.pointClass = Point;
  31231. }
  31232. if (seriesOptions) {
  31233. defaultPlotOptions[seriesType] = seriesOptions;
  31234. }
  31235. SeriesRegistry.seriesTypes[seriesType] = SeriesClass;
  31236. }
  31237. SeriesRegistry.registerSeriesType = registerSeriesType;
  31238. /**
  31239. * Old factory to create new series prototypes.
  31240. *
  31241. * @deprecated
  31242. * @function Highcharts.seriesType
  31243. *
  31244. * @param {string} type
  31245. * The series type name.
  31246. *
  31247. * @param {string} parent
  31248. * The parent series type name. Use `line` to inherit from the basic
  31249. * {@link Series} object.
  31250. *
  31251. * @param {Highcharts.SeriesOptionsType|Highcharts.Dictionary<*>} options
  31252. * The additional default options that are merged with the parent's options.
  31253. *
  31254. * @param {Highcharts.Dictionary<*>} [props]
  31255. * The properties (functions and primitives) to set on the new prototype.
  31256. *
  31257. * @param {Highcharts.Dictionary<*>} [pointProps]
  31258. * Members for a series-specific extension of the {@link Point} prototype if
  31259. * needed.
  31260. *
  31261. * @return {Highcharts.Series}
  31262. * The newly created prototype as extended from {@link Series} or its
  31263. * derivatives.
  31264. */
  31265. function seriesType(type, parent, options, seriesProto, pointProto) {
  31266. const defaultPlotOptions = defaultOptions.plotOptions || {};
  31267. parent = parent || '';
  31268. // Merge the options
  31269. defaultPlotOptions[type] = merge(defaultPlotOptions[parent], options);
  31270. // Create the class
  31271. registerSeriesType(type, extendClass(SeriesRegistry.seriesTypes[parent] || function () { }, seriesProto));
  31272. SeriesRegistry.seriesTypes[type].prototype.type = type;
  31273. // Create the point class if needed
  31274. if (pointProto) {
  31275. SeriesRegistry.seriesTypes[type].prototype.pointClass = extendClass(Point, pointProto);
  31276. }
  31277. return SeriesRegistry.seriesTypes[type];
  31278. }
  31279. SeriesRegistry.seriesType = seriesType;
  31280. })(SeriesRegistry || (SeriesRegistry = {}));
  31281. /* *
  31282. *
  31283. * Default Export
  31284. *
  31285. * */
  31286. return SeriesRegistry;
  31287. });
  31288. _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) {
  31289. /* *
  31290. *
  31291. * (c) 2010-2021 Torstein Honsi
  31292. *
  31293. * License: www.highcharts.com/license
  31294. *
  31295. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  31296. *
  31297. * */
  31298. const { animObject, setAnimation } = A;
  31299. const { defaultOptions } = D;
  31300. const { registerEventOptions } = F;
  31301. const { hasTouch, svg, win } = H;
  31302. const { seriesTypes } = SeriesRegistry;
  31303. 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;
  31304. /* *
  31305. *
  31306. * Class
  31307. *
  31308. * */
  31309. /**
  31310. * This is the base series prototype that all other series types inherit from.
  31311. * A new series is initialized either through the
  31312. * [series](https://api.highcharts.com/highcharts/series)
  31313. * option structure, or after the chart is initialized, through
  31314. * {@link Highcharts.Chart#addSeries}.
  31315. *
  31316. * The object can be accessed in a number of ways. All series and point event
  31317. * handlers give a reference to the `series` object. The chart object has a
  31318. * {@link Highcharts.Chart#series|series} property that is a collection of all
  31319. * the chart's series. The point objects and axis objects also have the same
  31320. * reference.
  31321. *
  31322. * Another way to reference the series programmatically is by `id`. Add an id
  31323. * in the series configuration options, and get the series object by
  31324. * {@link Highcharts.Chart#get}.
  31325. *
  31326. * Configuration options for the series are given in three levels. Options for
  31327. * all series in a chart are given in the
  31328. * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
  31329. * object. Then options for all series of a specific type
  31330. * are given in the plotOptions of that type, for example `plotOptions.line`.
  31331. * Next, options for one single series are given in the series array, or as
  31332. * arguments to `chart.addSeries`.
  31333. *
  31334. * The data in the series is stored in various arrays.
  31335. *
  31336. * - First, `series.options.data` contains all the original config options for
  31337. * each point whether added by options or methods like `series.addPoint`.
  31338. *
  31339. * - Next, `series.data` contains those values converted to points, but in case
  31340. * the series data length exceeds the `cropThreshold`, or if the data is
  31341. * grouped, `series.data` doesn't contain all the points. It only contains the
  31342. * points that have been created on demand.
  31343. *
  31344. * - Then there's `series.points` that contains all currently visible point
  31345. * objects. In case of cropping, the cropped-away points are not part of this
  31346. * array. The `series.points` array starts at `series.cropStart` compared to
  31347. * `series.data` and `series.options.data`. If however the series data is
  31348. * grouped, these can't be correlated one to one.
  31349. *
  31350. * - `series.xData` and `series.processedXData` contain clean x values,
  31351. * equivalent to `series.data` and `series.points`.
  31352. *
  31353. * - `series.yData` and `series.processedYData` contain clean y values,
  31354. * equivalent to `series.data` and `series.points`.
  31355. *
  31356. * @class
  31357. * @name Highcharts.Series
  31358. *
  31359. * @param {Highcharts.Chart} chart
  31360. * The chart instance.
  31361. *
  31362. * @param {Highcharts.SeriesOptionsType|object} options
  31363. * The series options.
  31364. */
  31365. class Series {
  31366. constructor() {
  31367. /* *
  31368. *
  31369. * Static Properties
  31370. *
  31371. * */
  31372. this._i = void 0;
  31373. this.chart = void 0;
  31374. this.data = void 0;
  31375. this.eventOptions = void 0;
  31376. this.eventsToUnbind = void 0;
  31377. this.index = void 0;
  31378. this.linkedSeries = void 0;
  31379. this.options = void 0;
  31380. this.points = void 0;
  31381. this.processedXData = void 0;
  31382. this.processedYData = void 0;
  31383. this.tooltipOptions = void 0;
  31384. this.userOptions = void 0;
  31385. this.xAxis = void 0;
  31386. this.yAxis = void 0;
  31387. this.zones = void 0;
  31388. /** eslint-enable valid-jsdoc */
  31389. }
  31390. /* *
  31391. *
  31392. * Functions
  31393. *
  31394. * */
  31395. /* eslint-disable valid-jsdoc */
  31396. init(chart, userOptions) {
  31397. fireEvent(this, 'init', { options: userOptions });
  31398. const series = this, chartSeries = chart.series;
  31399. // The 'eventsToUnbind' property moved from prototype into the
  31400. // Series init to avoid reference to the same array between
  31401. // the different series and charts. #12959, #13937
  31402. this.eventsToUnbind = [];
  31403. /**
  31404. * Read only. The chart that the series belongs to.
  31405. *
  31406. * @name Highcharts.Series#chart
  31407. * @type {Highcharts.Chart}
  31408. */
  31409. series.chart = chart;
  31410. /**
  31411. * Read only. The series' type, like "line", "area", "column" etc.
  31412. * The type in the series options anc can be altered using
  31413. * {@link Series#update}.
  31414. *
  31415. * @name Highcharts.Series#type
  31416. * @type {string}
  31417. */
  31418. /**
  31419. * Read only. The series' current options. To update, use
  31420. * {@link Series#update}.
  31421. *
  31422. * @name Highcharts.Series#options
  31423. * @type {Highcharts.SeriesOptionsType}
  31424. */
  31425. series.options = series.setOptions(userOptions);
  31426. const options = series.options;
  31427. series.linkedSeries = [];
  31428. // bind the axes
  31429. series.bindAxes();
  31430. extend(series, {
  31431. /**
  31432. * The series name as given in the options. Defaults to
  31433. * "Series {n}".
  31434. *
  31435. * @name Highcharts.Series#name
  31436. * @type {string}
  31437. */
  31438. name: options.name,
  31439. state: '',
  31440. /**
  31441. * Read only. The series' visibility state as set by {@link
  31442. * Series#show}, {@link Series#hide}, or in the initial
  31443. * configuration.
  31444. *
  31445. * @name Highcharts.Series#visible
  31446. * @type {boolean}
  31447. */
  31448. visible: options.visible !== false,
  31449. /**
  31450. * Read only. The series' selected state as set by {@link
  31451. * Highcharts.Series#select}.
  31452. *
  31453. * @name Highcharts.Series#selected
  31454. * @type {boolean}
  31455. */
  31456. selected: options.selected === true // false by default
  31457. });
  31458. registerEventOptions(this, options);
  31459. const events = options.events;
  31460. if ((events && events.click) ||
  31461. (options.point &&
  31462. options.point.events &&
  31463. options.point.events.click) ||
  31464. options.allowPointSelect) {
  31465. chart.runTrackerClick = true;
  31466. }
  31467. series.getColor();
  31468. series.getSymbol();
  31469. // Initialize the parallel data arrays
  31470. series.parallelArrays.forEach(function (key) {
  31471. if (!series[key + 'Data']) {
  31472. series[key + 'Data'] = [];
  31473. }
  31474. });
  31475. // Mark cartesian
  31476. if (series.isCartesian) {
  31477. chart.hasCartesianSeries = true;
  31478. }
  31479. // Get the index and register the series in the chart. The index is
  31480. // one more than the current latest series index (#5960).
  31481. let lastSeries;
  31482. if (chartSeries.length) {
  31483. lastSeries = chartSeries[chartSeries.length - 1];
  31484. }
  31485. series._i = pick(lastSeries && lastSeries._i, -1) + 1;
  31486. series.opacity = series.options.opacity;
  31487. // Insert the series and re-order all series above the insertion
  31488. // point.
  31489. chart.orderItems('series', insertItem(this, chartSeries));
  31490. // Set options for series with sorting and set data later.
  31491. if (options.dataSorting && options.dataSorting.enabled) {
  31492. series.setDataSortingOptions();
  31493. }
  31494. else if (!series.points && !series.data) {
  31495. series.setData(options.data, false);
  31496. }
  31497. fireEvent(this, 'afterInit');
  31498. }
  31499. /**
  31500. * Check whether the series item is itself or inherits from a certain
  31501. * series type.
  31502. *
  31503. * @function Highcharts.Series#is
  31504. * @param {string} type The type of series to check for, can be either
  31505. * featured or custom series types. For example `column`, `pie`,
  31506. * `ohlc` etc.
  31507. *
  31508. * @return {boolean}
  31509. * True if this item is or inherits from the given type.
  31510. */
  31511. is(type) {
  31512. return seriesTypes[type] && this instanceof seriesTypes[type];
  31513. }
  31514. /**
  31515. * Set the xAxis and yAxis properties of cartesian series, and register
  31516. * the series in the `axis.series` array.
  31517. *
  31518. * @private
  31519. * @function Highcharts.Series#bindAxes
  31520. */
  31521. bindAxes() {
  31522. const series = this, seriesOptions = series.options, chart = series.chart;
  31523. let axisOptions;
  31524. fireEvent(this, 'bindAxes', null, function () {
  31525. // repeat for xAxis and yAxis
  31526. (series.axisTypes || []).forEach(function (coll) {
  31527. // loop through the chart's axis objects
  31528. chart[coll].forEach(function (axis) {
  31529. axisOptions = axis.options;
  31530. // apply if the series xAxis or yAxis option mathches
  31531. // the number of the axis, or if undefined, use the
  31532. // first axis
  31533. if (pick(seriesOptions[coll], 0) === axis.index ||
  31534. (typeof seriesOptions[coll] !==
  31535. 'undefined' &&
  31536. seriesOptions[coll] === axisOptions.id)) {
  31537. // register this series in the axis.series lookup
  31538. insertItem(series, axis.series);
  31539. // set this series.xAxis or series.yAxis reference
  31540. /**
  31541. * Read only. The unique xAxis object associated
  31542. * with the series.
  31543. *
  31544. * @name Highcharts.Series#xAxis
  31545. * @type {Highcharts.Axis}
  31546. */
  31547. /**
  31548. * Read only. The unique yAxis object associated
  31549. * with the series.
  31550. *
  31551. * @name Highcharts.Series#yAxis
  31552. * @type {Highcharts.Axis}
  31553. */
  31554. series[coll] = axis;
  31555. // mark dirty for redraw
  31556. axis.isDirty = true;
  31557. }
  31558. });
  31559. // The series needs an X and an Y axis
  31560. if (!series[coll] &&
  31561. series.optionalAxis !== coll) {
  31562. error(18, true, chart);
  31563. }
  31564. });
  31565. });
  31566. fireEvent(this, 'afterBindAxes');
  31567. }
  31568. /**
  31569. * For simple series types like line and column, the data values are
  31570. * held in arrays like xData and yData for quick lookup to find extremes
  31571. * and more. For multidimensional series like bubble and map, this can
  31572. * be extended with arrays like zData and valueData by adding to the
  31573. * `series.parallelArrays` array.
  31574. *
  31575. * @private
  31576. * @function Highcharts.Series#updateParallelArrays
  31577. */
  31578. updateParallelArrays(point, i, iArgs) {
  31579. const series = point.series, fn = isNumber(i) ?
  31580. // Insert the value in the given position
  31581. function (key) {
  31582. const val = key === 'y' && series.toYData ?
  31583. series.toYData(point) :
  31584. point[key];
  31585. series[key + 'Data'][i] = val;
  31586. } :
  31587. // Apply the method specified in i with the following
  31588. // arguments as arguments
  31589. function (key) {
  31590. Array.prototype[i].apply(series[key + 'Data'], iArgs);
  31591. };
  31592. series.parallelArrays.forEach(fn);
  31593. }
  31594. /**
  31595. * Define hasData functions for series. These return true if there
  31596. * are data points on this series within the plot area.
  31597. *
  31598. * @private
  31599. * @function Highcharts.Series#hasData
  31600. */
  31601. hasData() {
  31602. return ((this.visible &&
  31603. typeof this.dataMax !== 'undefined' &&
  31604. typeof this.dataMin !== 'undefined') || ( // #3703
  31605. this.visible &&
  31606. this.yData &&
  31607. this.yData.length > 0) // #9758
  31608. );
  31609. }
  31610. /**
  31611. * Return an auto incremented x value based on the pointStart and
  31612. * pointInterval options. This is only used if an x value is not given
  31613. * for the point that calls autoIncrement.
  31614. *
  31615. * @private
  31616. * @function Highcharts.Series#autoIncrement
  31617. */
  31618. autoIncrement(x) {
  31619. const options = this.options, pointIntervalUnit = options.pointIntervalUnit, relativeXValue = options.relativeXValue, time = this.chart.time;
  31620. let xIncrement = this.xIncrement, date, pointInterval;
  31621. xIncrement = pick(xIncrement, options.pointStart, 0);
  31622. this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
  31623. if (relativeXValue && isNumber(x)) {
  31624. pointInterval *= x;
  31625. }
  31626. // Added code for pointInterval strings
  31627. if (pointIntervalUnit) {
  31628. date = new time.Date(xIncrement);
  31629. if (pointIntervalUnit === 'day') {
  31630. time.set('Date', date, time.get('Date', date) + pointInterval);
  31631. }
  31632. else if (pointIntervalUnit === 'month') {
  31633. time.set('Month', date, time.get('Month', date) + pointInterval);
  31634. }
  31635. else if (pointIntervalUnit === 'year') {
  31636. time.set('FullYear', date, time.get('FullYear', date) + pointInterval);
  31637. }
  31638. pointInterval = date.getTime() - xIncrement;
  31639. }
  31640. if (relativeXValue && isNumber(x)) {
  31641. return xIncrement + pointInterval;
  31642. }
  31643. this.xIncrement = xIncrement + pointInterval;
  31644. return xIncrement;
  31645. }
  31646. /**
  31647. * Internal function to set properties for series if data sorting is
  31648. * enabled.
  31649. *
  31650. * @private
  31651. * @function Highcharts.Series#setDataSortingOptions
  31652. */
  31653. setDataSortingOptions() {
  31654. const options = this.options;
  31655. extend(this, {
  31656. requireSorting: false,
  31657. sorted: false,
  31658. enabledDataSorting: true,
  31659. allowDG: false
  31660. });
  31661. // To allow unsorted data for column series.
  31662. if (!defined(options.pointRange)) {
  31663. options.pointRange = 1;
  31664. }
  31665. }
  31666. /**
  31667. * Set the series options by merging from the options tree. Called
  31668. * internally on initializing and updating series. This function will
  31669. * not redraw the series. For API usage, use {@link Series#update}.
  31670. * @private
  31671. * @function Highcharts.Series#setOptions
  31672. * @param {Highcharts.SeriesOptionsType} itemOptions
  31673. * The series options.
  31674. * @emits Highcharts.Series#event:afterSetOptions
  31675. */
  31676. setOptions(itemOptions) {
  31677. var _a,
  31678. _b;
  31679. const chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, userOptions = chart.userOptions || {}, seriesUserOptions = merge(itemOptions), styledMode = chart.styledMode, e = {
  31680. plotOptions: plotOptions,
  31681. userOptions: seriesUserOptions
  31682. };
  31683. let zone;
  31684. fireEvent(this, 'setOptions', e);
  31685. // These may be modified by the event
  31686. const typeOptions = e.plotOptions[this.type], userPlotOptions = (userOptions.plotOptions || {}), userPlotOptionsSeries = userPlotOptions.series || {}, defaultPlotOptionsType = (defaultOptions.plotOptions[this.type] || {}), userPlotOptionsType = userPlotOptions[this.type] || {};
  31687. // use copy to prevent undetected changes (#9762)
  31688. /**
  31689. * Contains series options by the user without defaults.
  31690. * @name Highcharts.Series#userOptions
  31691. * @type {Highcharts.SeriesOptionsType}
  31692. */
  31693. this.userOptions = e.userOptions;
  31694. const options = merge(typeOptions, plotOptions.series,
  31695. // #3881, chart instance plotOptions[type] should trump
  31696. // plotOptions.series
  31697. userPlotOptionsType, seriesUserOptions);
  31698. // The tooltip options are merged between global and series specific
  31699. // options. Importance order asscendingly:
  31700. // globals: (1)tooltip, (2)plotOptions.series,
  31701. // (3)plotOptions[this.type]
  31702. // init userOptions with possible later updates: 4-6 like 1-3 and
  31703. // (7)this series options
  31704. this.tooltipOptions = merge(defaultOptions.tooltip, // 1
  31705. (_a = defaultOptions.plotOptions.series) === null || _a === void 0 ? void 0 : _a.tooltip, // 2
  31706. defaultPlotOptionsType === null || defaultPlotOptionsType === void 0 ? void 0 : defaultPlotOptionsType.tooltip, // 3
  31707. chart.userOptions.tooltip, // 4
  31708. (_b = userPlotOptions.series) === null || _b === void 0 ? void 0 : _b.tooltip, // 5
  31709. userPlotOptionsType.tooltip, // 6
  31710. seriesUserOptions.tooltip // 7
  31711. );
  31712. // When shared tooltip, stickyTracking is true by default,
  31713. // unless user says otherwise.
  31714. this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptionsType.stickyTracking, userPlotOptionsSeries.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
  31715. true :
  31716. options.stickyTracking));
  31717. // Delete marker object if not allowed (#1125)
  31718. if (typeOptions.marker === null) {
  31719. delete options.marker;
  31720. }
  31721. // Handle color zones
  31722. this.zoneAxis = options.zoneAxis;
  31723. const zones = this.zones = (options.zones || []).slice();
  31724. if ((options.negativeColor || options.negativeFillColor) &&
  31725. !options.zones) {
  31726. zone = {
  31727. value: options[this.zoneAxis + 'Threshold'] ||
  31728. options.threshold ||
  31729. 0,
  31730. className: 'highcharts-negative'
  31731. };
  31732. if (!styledMode) {
  31733. zone.color = options.negativeColor;
  31734. zone.fillColor = options.negativeFillColor;
  31735. }
  31736. zones.push(zone);
  31737. }
  31738. if (zones.length) { // Push one extra zone for the rest
  31739. if (defined(zones[zones.length - 1].value)) {
  31740. zones.push(styledMode ? {} : {
  31741. color: this.color,
  31742. fillColor: this.fillColor
  31743. });
  31744. }
  31745. }
  31746. fireEvent(this, 'afterSetOptions', { options: options });
  31747. return options;
  31748. }
  31749. /**
  31750. * Return series name in "Series {Number}" format or the one defined by
  31751. * a user. This method can be simply overridden as series name format
  31752. * can vary (e.g. technical indicators).
  31753. *
  31754. * @function Highcharts.Series#getName
  31755. *
  31756. * @return {string}
  31757. * The series name.
  31758. */
  31759. getName() {
  31760. // #4119
  31761. return pick(this.options.name, 'Series ' + (this.index + 1));
  31762. }
  31763. /**
  31764. * @private
  31765. * @function Highcharts.Series#getCyclic
  31766. */
  31767. getCyclic(prop, value, defaults) {
  31768. const chart = this.chart, indexName = `${prop}Index`, counterName = `${prop}Counter`, len = (
  31769. // Symbol count
  31770. (defaults === null || defaults === void 0 ? void 0 : defaults.length) ||
  31771. // Color count
  31772. chart.options.chart.colorCount);
  31773. let i, setting;
  31774. if (!value) {
  31775. // Pick up either the colorIndex option, or the series.colorIndex
  31776. // after Series.update()
  31777. setting = pick(prop === 'color' ? this.options.colorIndex : void 0, this[indexName]);
  31778. if (defined(setting)) { // after Series.update()
  31779. i = setting;
  31780. }
  31781. else {
  31782. // #6138
  31783. if (!chart.series.length) {
  31784. chart[counterName] = 0;
  31785. }
  31786. i = chart[counterName] % len;
  31787. chart[counterName] += 1;
  31788. }
  31789. if (defaults) {
  31790. value = defaults[i];
  31791. }
  31792. }
  31793. // Set the colorIndex
  31794. if (typeof i !== 'undefined') {
  31795. this[indexName] = i;
  31796. }
  31797. this[prop] = value;
  31798. }
  31799. /**
  31800. * Get the series' color based on either the options or pulled from
  31801. * global options.
  31802. *
  31803. * @private
  31804. * @function Highcharts.Series#getColor
  31805. */
  31806. getColor() {
  31807. if (this.chart.styledMode) {
  31808. this.getCyclic('color');
  31809. }
  31810. else if (this.options.colorByPoint) {
  31811. this.color = "#cccccc" /* Palette.neutralColor20 */;
  31812. }
  31813. else {
  31814. this.getCyclic('color', this.options.color ||
  31815. defaultOptions.plotOptions[this.type].color, this.chart.options.colors);
  31816. }
  31817. }
  31818. /**
  31819. * Get all points' instances created for this series.
  31820. *
  31821. * @private
  31822. * @function Highcharts.Series#getPointsCollection
  31823. */
  31824. getPointsCollection() {
  31825. return (this.hasGroupedData ? this.points : this.data) || [];
  31826. }
  31827. /**
  31828. * Get the series' symbol based on either the options or pulled from
  31829. * global options.
  31830. *
  31831. * @private
  31832. * @function Highcharts.Series#getSymbol
  31833. */
  31834. getSymbol() {
  31835. const seriesMarkerOption = this.options.marker;
  31836. this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
  31837. }
  31838. /**
  31839. * Finds the index of an existing point that matches the given point
  31840. * options.
  31841. *
  31842. * @private
  31843. * @function Highcharts.Series#findPointIndex
  31844. * @param {Highcharts.PointOptionsObject} optionsObject
  31845. * The options of the point.
  31846. * @param {number} fromIndex
  31847. * The index to start searching from, used for optimizing series with
  31848. * required sorting.
  31849. * @return {number|undefined}
  31850. * Returns the index of a matching point, or undefined if no match is found.
  31851. */
  31852. findPointIndex(optionsObject, fromIndex) {
  31853. const id = optionsObject.id, x = optionsObject.x, oldData = this.points, dataSorting = this.options.dataSorting;
  31854. let matchingPoint, matchedById, pointIndex;
  31855. if (id) {
  31856. const item = this.chart.get(id);
  31857. if (item instanceof Point) {
  31858. matchingPoint = item;
  31859. }
  31860. }
  31861. else if (this.linkedParent ||
  31862. this.enabledDataSorting ||
  31863. this.options.relativeXValue) {
  31864. let matcher = (oldPoint) => !oldPoint.touched &&
  31865. oldPoint.index === optionsObject.index;
  31866. if (dataSorting && dataSorting.matchByName) {
  31867. matcher = (oldPoint) => !oldPoint.touched &&
  31868. oldPoint.name === optionsObject.name;
  31869. }
  31870. else if (this.options.relativeXValue) {
  31871. matcher = (oldPoint) => !oldPoint.touched &&
  31872. oldPoint.options.x === optionsObject.x;
  31873. }
  31874. matchingPoint = find(oldData, matcher);
  31875. // Add unmatched point as a new point
  31876. if (!matchingPoint) {
  31877. return void 0;
  31878. }
  31879. }
  31880. if (matchingPoint) {
  31881. pointIndex = matchingPoint && matchingPoint.index;
  31882. if (typeof pointIndex !== 'undefined') {
  31883. matchedById = true;
  31884. }
  31885. }
  31886. // Search for the same X in the existing data set
  31887. if (typeof pointIndex === 'undefined' && isNumber(x)) {
  31888. pointIndex = this.xData.indexOf(x, fromIndex);
  31889. }
  31890. // Reduce pointIndex if data is cropped
  31891. if (pointIndex !== -1 &&
  31892. typeof pointIndex !== 'undefined' &&
  31893. this.cropped) {
  31894. pointIndex = (pointIndex >= this.cropStart) ?
  31895. pointIndex - this.cropStart : pointIndex;
  31896. }
  31897. if (!matchedById &&
  31898. isNumber(pointIndex) &&
  31899. oldData[pointIndex] && oldData[pointIndex].touched) {
  31900. pointIndex = void 0;
  31901. }
  31902. return pointIndex;
  31903. }
  31904. /**
  31905. * Internal function called from setData. If the point count is the same
  31906. * as it was, or if there are overlapping X values, just run
  31907. * Point.update which is cheaper, allows animation, and keeps references
  31908. * to points. This also allows adding or removing points if the X-es
  31909. * don't match.
  31910. *
  31911. * @private
  31912. * @function Highcharts.Series#updateData
  31913. */
  31914. updateData(data, animation) {
  31915. const options = this.options, dataSorting = options.dataSorting, oldData = this.points, pointsToAdd = [], requireSorting = this.requireSorting, equalLength = data.length === oldData.length;
  31916. let hasUpdatedByKey, i, point, lastIndex, succeeded = true;
  31917. this.xIncrement = null;
  31918. // Iterate the new data
  31919. data.forEach(function (pointOptions, i) {
  31920. const optionsObject = (defined(pointOptions) &&
  31921. this.pointClass.prototype.optionsToObject.call({ series: this }, pointOptions)) || {};
  31922. let pointIndex;
  31923. // Get the x of the new data point
  31924. const x = optionsObject.x, id = optionsObject.id;
  31925. if (id || isNumber(x)) {
  31926. pointIndex = this.findPointIndex(optionsObject, lastIndex);
  31927. // Matching X not found
  31928. // or used already due to ununique x values (#8995),
  31929. // add point (but later)
  31930. if (pointIndex === -1 ||
  31931. typeof pointIndex === 'undefined') {
  31932. pointsToAdd.push(pointOptions);
  31933. // Matching X found, update
  31934. }
  31935. else if (oldData[pointIndex] &&
  31936. pointOptions !== options.data[pointIndex]) {
  31937. oldData[pointIndex].update(pointOptions, false, null, false);
  31938. // Mark it touched, below we will remove all points that
  31939. // are not touched.
  31940. oldData[pointIndex].touched = true;
  31941. // Speed optimize by only searching after last known
  31942. // index. Performs ~20% bettor on large data sets.
  31943. if (requireSorting) {
  31944. lastIndex = pointIndex + 1;
  31945. }
  31946. // Point exists, no changes, don't remove it
  31947. }
  31948. else if (oldData[pointIndex]) {
  31949. oldData[pointIndex].touched = true;
  31950. }
  31951. // If the length is equal and some of the nodes had a
  31952. // match in the same position, we don't want to remove
  31953. // non-matches.
  31954. if (!equalLength ||
  31955. i !== pointIndex ||
  31956. (dataSorting && dataSorting.enabled) ||
  31957. this.hasDerivedData) {
  31958. hasUpdatedByKey = true;
  31959. }
  31960. }
  31961. else {
  31962. // Gather all points that are not matched
  31963. pointsToAdd.push(pointOptions);
  31964. }
  31965. }, this);
  31966. // Remove points that don't exist in the updated data set
  31967. if (hasUpdatedByKey) {
  31968. i = oldData.length;
  31969. while (i--) {
  31970. point = oldData[i];
  31971. if (point && !point.touched && point.remove) {
  31972. point.remove(false, animation);
  31973. }
  31974. }
  31975. // If we did not find keys (ids or x-values), and the length is the
  31976. // same, update one-to-one
  31977. }
  31978. else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
  31979. data.forEach(function (point, i) {
  31980. // .update doesn't exist on a linked, hidden series (#3709)
  31981. // (#10187)
  31982. if (point !== oldData[i].y && !oldData[i].destroyed) {
  31983. oldData[i].update(point, false, null, false);
  31984. }
  31985. });
  31986. // Don't add new points since those configs are used above
  31987. pointsToAdd.length = 0;
  31988. // Did not succeed in updating data
  31989. }
  31990. else {
  31991. succeeded = false;
  31992. }
  31993. oldData.forEach(function (point) {
  31994. if (point) {
  31995. point.touched = false;
  31996. }
  31997. });
  31998. if (!succeeded) {
  31999. return false;
  32000. }
  32001. // Add new points
  32002. pointsToAdd.forEach(function (point) {
  32003. this.addPoint(point, false, null, null, false);
  32004. }, this);
  32005. if (this.xIncrement === null &&
  32006. this.xData &&
  32007. this.xData.length) {
  32008. this.xIncrement = arrayMax(this.xData);
  32009. this.autoIncrement();
  32010. }
  32011. return true;
  32012. }
  32013. /**
  32014. * Apply a new set of data to the series and optionally redraw it. The
  32015. * new data array is passed by reference (except in case of
  32016. * `updatePoints`), and may later be mutated when updating the chart
  32017. * data.
  32018. *
  32019. * Note the difference in behaviour when setting the same amount of
  32020. * points, or a different amount of points, as handled by the
  32021. * `updatePoints` parameter.
  32022. *
  32023. * @sample highcharts/members/series-setdata/
  32024. * Set new data from a button
  32025. * @sample highcharts/members/series-setdata-pie/
  32026. * Set data in a pie
  32027. * @sample stock/members/series-setdata/
  32028. * Set new data in Highcharts Stock
  32029. * @sample maps/members/series-setdata/
  32030. * Set new data in Highmaps
  32031. *
  32032. * @function Highcharts.Series#setData
  32033. *
  32034. * @param {Array<Highcharts.PointOptionsType>} data
  32035. * Takes an array of data in the same format as described under
  32036. * `series.{type}.data` for the given series type, for example a
  32037. * line series would take data in the form described under
  32038. * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
  32039. *
  32040. * @param {boolean} [redraw=true]
  32041. * Whether to redraw the chart after the series is altered. If
  32042. * doing more operations on the chart, it is a good idea to set
  32043. * redraw to false and call {@link Chart#redraw} after.
  32044. *
  32045. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  32046. * When the updated data is the same length as the existing data,
  32047. * points will be updated by default, and animation visualizes
  32048. * how the points are changed. Set false to disable animation, or
  32049. * a configuration object to set duration or easing.
  32050. *
  32051. * @param {boolean} [updatePoints=true]
  32052. * When this is true, points will be updated instead of replaced
  32053. * whenever possible. This occurs a) when the updated data is the
  32054. * same length as the existing data, b) when points are matched
  32055. * by their id's, or c) when points can be matched by X values.
  32056. * This allows updating with animation and performs better. In
  32057. * this case, the original array is not passed by reference. Set
  32058. * `false` to prevent.
  32059. */
  32060. setData(data, redraw = true, animation, updatePoints) {
  32061. var _a;
  32062. 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;
  32063. let i, pt, updatedData, indexOfX = 0, indexOfY = 1, firstPoint = null, copiedData;
  32064. if (!chart.options.chart.allowMutatingData) { // #4259
  32065. // Remove old reference
  32066. if (options.data) {
  32067. delete series.options.data;
  32068. }
  32069. if (series.userOptions.data) {
  32070. delete series.userOptions.data;
  32071. }
  32072. copiedData = merge(true, data);
  32073. }
  32074. data = copiedData || data || [];
  32075. const dataLength = data.length;
  32076. if (dataSorting && dataSorting.enabled) {
  32077. data = this.sortData(data);
  32078. }
  32079. // First try to run Point.update which is cheaper, allows animation,
  32080. // and keeps references to points.
  32081. if (chart.options.chart.allowMutatingData &&
  32082. updatePoints !== false &&
  32083. dataLength &&
  32084. oldDataLength &&
  32085. !series.cropped &&
  32086. !series.hasGroupedData &&
  32087. series.visible &&
  32088. // Soft updating has no benefit in boost, and causes JS error
  32089. // (#8355)
  32090. !series.boosted) {
  32091. updatedData = this.updateData(data, animation);
  32092. }
  32093. if (!updatedData) {
  32094. // Reset properties
  32095. series.xIncrement = null;
  32096. series.colorCounter = 0; // for series with colorByPoint (#1547)
  32097. // Update parallel arrays
  32098. this.parallelArrays.forEach(function (key) {
  32099. series[key + 'Data'].length = 0;
  32100. });
  32101. // In turbo mode, only one- or twodimensional arrays of numbers
  32102. // are allowed. The first value is tested, and we assume that
  32103. // all the rest are defined the same way. Although the 'for'
  32104. // loops are similar, they are repeated inside each if-else
  32105. // conditional for max performance.
  32106. if (turboThreshold && dataLength > turboThreshold) {
  32107. firstPoint = series.getFirstValidPoint(data);
  32108. if (isNumber(firstPoint)) { // assume all points are numbers
  32109. for (i = 0; i < dataLength; i++) {
  32110. xData[i] = this.autoIncrement();
  32111. yData[i] = data[i];
  32112. }
  32113. // Assume all points are arrays when first point is
  32114. }
  32115. else if (isArray(firstPoint)) {
  32116. if (valueCount) { // [x, low, high] or [x, o, h, l, c]
  32117. if (firstPoint.length === valueCount) {
  32118. for (i = 0; i < dataLength; i++) {
  32119. xData[i] = this.autoIncrement();
  32120. yData[i] = data[i];
  32121. }
  32122. }
  32123. else {
  32124. for (i = 0; i < dataLength; i++) {
  32125. pt = data[i];
  32126. xData[i] = pt[0];
  32127. yData[i] =
  32128. pt.slice(1, valueCount + 1);
  32129. }
  32130. }
  32131. }
  32132. else { // [x, y]
  32133. if (keys) {
  32134. indexOfX = keys.indexOf('x');
  32135. indexOfY = keys.indexOf('y');
  32136. indexOfX = indexOfX >= 0 ? indexOfX : 0;
  32137. indexOfY = indexOfY >= 0 ? indexOfY : 1;
  32138. }
  32139. if (firstPoint.length === 1) {
  32140. indexOfY = 0;
  32141. }
  32142. if (indexOfX === indexOfY) {
  32143. for (i = 0; i < dataLength; i++) {
  32144. xData[i] = this.autoIncrement();
  32145. yData[i] = data[i][indexOfY];
  32146. }
  32147. }
  32148. else {
  32149. for (i = 0; i < dataLength; i++) {
  32150. pt = data[i];
  32151. xData[i] = pt[indexOfX];
  32152. yData[i] = pt[indexOfY];
  32153. }
  32154. }
  32155. }
  32156. }
  32157. else {
  32158. // Highcharts expects configs to be numbers or arrays in
  32159. // turbo mode
  32160. error(12, false, chart);
  32161. }
  32162. }
  32163. else {
  32164. for (i = 0; i < dataLength; i++) {
  32165. pt = { series: series };
  32166. series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
  32167. series.updateParallelArrays(pt, i);
  32168. }
  32169. }
  32170. // Forgetting to cast strings to numbers is a common caveat when
  32171. // handling CSV or JSON
  32172. if (yData && isString(yData[0])) {
  32173. error(14, true, chart);
  32174. }
  32175. series.data = [];
  32176. series.options.data = series.userOptions.data = data;
  32177. // destroy old points
  32178. i = oldDataLength;
  32179. while (i--) {
  32180. (_a = oldData[i]) === null || _a === void 0 ? void 0 : _a.destroy();
  32181. }
  32182. // reset minRange (#878)
  32183. if (xAxis) {
  32184. xAxis.minRange = xAxis.userMinRange;
  32185. }
  32186. // redraw
  32187. series.isDirty = chart.isDirtyBox = true;
  32188. series.isDirtyData = !!oldData;
  32189. animation = false;
  32190. }
  32191. // Typically for pie series, points need to be processed and
  32192. // generated prior to rendering the legend
  32193. if (options.legendType === 'point') {
  32194. this.processData();
  32195. this.generatePoints();
  32196. }
  32197. if (redraw) {
  32198. chart.redraw(animation);
  32199. }
  32200. }
  32201. /**
  32202. * Internal function to sort series data
  32203. *
  32204. * @private
  32205. * @function Highcharts.Series#sortData
  32206. * @param {Array<Highcharts.PointOptionsType>} data
  32207. * Force data grouping.
  32208. */
  32209. sortData(data) {
  32210. const series = this, options = series.options, dataSorting = options.dataSorting, sortKey = dataSorting.sortKey || 'y', getPointOptionsObject = function (series, pointOptions) {
  32211. return (defined(pointOptions) &&
  32212. series.pointClass.prototype.optionsToObject.call({
  32213. series: series
  32214. }, pointOptions)) || {};
  32215. };
  32216. data.forEach(function (pointOptions, i) {
  32217. data[i] = getPointOptionsObject(series, pointOptions);
  32218. data[i].index = i;
  32219. }, this);
  32220. // Sorting
  32221. const sortedData = data.concat().sort((a, b) => {
  32222. const aValue = getNestedProperty(sortKey, a);
  32223. const bValue = getNestedProperty(sortKey, b);
  32224. return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
  32225. });
  32226. // Set x value depending on the position in the array
  32227. sortedData.forEach(function (point, i) {
  32228. point.x = i;
  32229. }, this);
  32230. // Set the same x for linked series points if they don't have their
  32231. // own sorting
  32232. if (series.linkedSeries) {
  32233. series.linkedSeries.forEach(function (linkedSeries) {
  32234. const options = linkedSeries.options, seriesData = options.data;
  32235. if ((!options.dataSorting ||
  32236. !options.dataSorting.enabled) &&
  32237. seriesData) {
  32238. seriesData.forEach(function (pointOptions, i) {
  32239. seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
  32240. if (data[i]) {
  32241. seriesData[i].x = data[i].x;
  32242. seriesData[i].index = i;
  32243. }
  32244. });
  32245. linkedSeries.setData(seriesData, false);
  32246. }
  32247. });
  32248. }
  32249. return data;
  32250. }
  32251. /**
  32252. * Internal function to process the data by cropping away unused data
  32253. * points if the series is longer than the crop threshold. This saves
  32254. * computing time for large series.
  32255. *
  32256. * @private
  32257. * @function Highcharts.Series#getProcessedData
  32258. * @param {boolean} [forceExtremesFromAll]
  32259. * Force getting extremes of a total series data range.
  32260. */
  32261. getProcessedData(forceExtremesFromAll) {
  32262. const series = this, xAxis = series.xAxis, options = series.options, cropThreshold = options.cropThreshold, getExtremesFromAll = forceExtremesFromAll ||
  32263. series.getExtremesFromAll ||
  32264. options.getExtremesFromAll, // #4599
  32265. logarithmic = xAxis === null || xAxis === void 0 ? void 0 : xAxis.logarithmic, isCartesian = series.isCartesian;
  32266. let croppedData, cropped, cropStart = 0, xExtremes, min, max,
  32267. // copied during slice operation:
  32268. processedXData = series.xData, processedYData = series.yData, updatingNames = false;
  32269. const dataLength = processedXData.length;
  32270. if (xAxis) {
  32271. // corrected for log axis (#3053)
  32272. xExtremes = xAxis.getExtremes();
  32273. min = xExtremes.min;
  32274. max = xExtremes.max;
  32275. updatingNames = !!(xAxis.categories && !xAxis.names.length);
  32276. }
  32277. // optionally filter out points outside the plot area
  32278. if (isCartesian &&
  32279. series.sorted &&
  32280. !getExtremesFromAll &&
  32281. (!cropThreshold ||
  32282. dataLength > cropThreshold ||
  32283. series.forceCrop)) {
  32284. // it's outside current extremes
  32285. if (processedXData[dataLength - 1] < min ||
  32286. processedXData[0] > max) {
  32287. processedXData = [];
  32288. processedYData = [];
  32289. // only crop if it's actually spilling out
  32290. }
  32291. else if (series.yData && (processedXData[0] < min ||
  32292. processedXData[dataLength - 1] > max)) {
  32293. croppedData = this.cropData(series.xData, series.yData, min, max);
  32294. processedXData = croppedData.xData;
  32295. processedYData = croppedData.yData;
  32296. cropStart = croppedData.start;
  32297. cropped = true;
  32298. }
  32299. }
  32300. // Find the closest distance between processed points
  32301. const closestPointRange = getClosestDistance([
  32302. logarithmic ?
  32303. processedXData.map(logarithmic.log2lin) :
  32304. processedXData
  32305. ],
  32306. // Unsorted data is not supported by the line tooltip, as well as
  32307. // data grouping and navigation in Stock charts (#725) and width
  32308. // calculation of columns (#1900). Avoid warning during the
  32309. // premature processing pass in updateNames (#16104).
  32310. () => (series.requireSorting &&
  32311. !updatingNames &&
  32312. error(15, false, series.chart)));
  32313. return {
  32314. xData: processedXData,
  32315. yData: processedYData,
  32316. cropped: cropped,
  32317. cropStart: cropStart,
  32318. closestPointRange: closestPointRange
  32319. };
  32320. }
  32321. /**
  32322. * Internal function to apply processed data.
  32323. * In Highcharts Stock, this function is extended to provide data grouping.
  32324. *
  32325. * @private
  32326. * @function Highcharts.Series#processData
  32327. * @param {boolean} [force]
  32328. * Force data grouping.
  32329. */
  32330. processData(force) {
  32331. const series = this, xAxis = series.xAxis;
  32332. // If the series data or axes haven't changed, don't go through
  32333. // this. Return false to pass the message on to override methods
  32334. // like in data grouping.
  32335. if (series.isCartesian &&
  32336. !series.isDirty &&
  32337. !xAxis.isDirty &&
  32338. !series.yAxis.isDirty &&
  32339. !force) {
  32340. return false;
  32341. }
  32342. const processedData = series.getProcessedData();
  32343. // Record the properties
  32344. series.cropped = processedData.cropped; // undefined or true
  32345. series.cropStart = processedData.cropStart;
  32346. series.processedXData = processedData.xData;
  32347. series.processedYData = processedData.yData;
  32348. series.closestPointRange = (series.basePointRange = processedData.closestPointRange);
  32349. fireEvent(series, 'afterProcessData');
  32350. }
  32351. /**
  32352. * Iterate over xData and crop values between min and max. Returns
  32353. * object containing crop start/end cropped xData with corresponding
  32354. * part of yData, dataMin and dataMax within the cropped range.
  32355. *
  32356. * @private
  32357. * @function Highcharts.Series#cropData
  32358. */
  32359. cropData(xData, yData, min, max, cropShoulder) {
  32360. const dataLength = xData.length;
  32361. let i, j, cropStart = 0, cropEnd = dataLength;
  32362. // line-type series need one point outside
  32363. cropShoulder = pick(cropShoulder, this.cropShoulder);
  32364. // iterate up to find slice start
  32365. for (i = 0; i < dataLength; i++) {
  32366. if (xData[i] >= min) {
  32367. cropStart = Math.max(0, i - cropShoulder);
  32368. break;
  32369. }
  32370. }
  32371. // proceed to find slice end
  32372. for (j = i; j < dataLength; j++) {
  32373. if (xData[j] > max) {
  32374. cropEnd = j + cropShoulder;
  32375. break;
  32376. }
  32377. }
  32378. return {
  32379. xData: xData.slice(cropStart, cropEnd),
  32380. yData: yData.slice(cropStart, cropEnd),
  32381. start: cropStart,
  32382. end: cropEnd
  32383. };
  32384. }
  32385. /**
  32386. * Generate the data point after the data has been processed by cropping
  32387. * away unused points and optionally grouped in Highcharts Stock.
  32388. *
  32389. * @private
  32390. * @function Highcharts.Series#generatePoints
  32391. */
  32392. generatePoints() {
  32393. 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 &&
  32394. options.dataGrouping.groupAll ?
  32395. cropStart :
  32396. 0);
  32397. let dataLength, cursor, point, i, data = series.data;
  32398. if (!data && !hasGroupedData) {
  32399. const arr = [];
  32400. arr.length = dataOptions.length;
  32401. data = series.data = arr;
  32402. }
  32403. if (keys && hasGroupedData) {
  32404. // grouped data has already applied keys (#6590)
  32405. series.options.keys = false;
  32406. }
  32407. for (i = 0; i < processedDataLength; i++) {
  32408. cursor = cropStart + i;
  32409. if (!hasGroupedData) {
  32410. point = data[cursor];
  32411. // #970:
  32412. if (!point &&
  32413. typeof dataOptions[cursor] !== 'undefined') {
  32414. data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
  32415. }
  32416. }
  32417. else {
  32418. // splat the y data in case of ohlc data array
  32419. point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
  32420. point.dataGroup = series.groupMap[groupCropStartIndex + i];
  32421. if (point.dataGroup.options) {
  32422. point.options = point.dataGroup.options;
  32423. extend(point, point.dataGroup.options);
  32424. // Collision of props and options (#9770)
  32425. delete point.dataLabels;
  32426. }
  32427. }
  32428. if (point) { // #6279
  32429. /**
  32430. * Contains the point's index in the `Series.points` array.
  32431. *
  32432. * @name Highcharts.Point#index
  32433. * @type {number}
  32434. * @readonly
  32435. */
  32436. // For faster access in Point.update
  32437. point.index = hasGroupedData ?
  32438. (groupCropStartIndex + i) : cursor;
  32439. points[i] = point;
  32440. }
  32441. }
  32442. // restore keys options (#6590)
  32443. series.options.keys = keys;
  32444. // Hide cropped-away points - this only runs when the number of
  32445. // points is above cropThreshold, or when swithching view from
  32446. // non-grouped data to grouped data (#637)
  32447. if (data &&
  32448. (processedDataLength !== (dataLength = data.length) ||
  32449. hasGroupedData)) {
  32450. for (i = 0; i < dataLength; i++) {
  32451. // when has grouped data, clear all points
  32452. if (i === cropStart && !hasGroupedData) {
  32453. i += processedDataLength;
  32454. }
  32455. if (data[i]) {
  32456. data[i].destroyElements();
  32457. data[i].plotX = void 0; // #1003
  32458. }
  32459. }
  32460. }
  32461. /**
  32462. * Read only. An array containing those values converted to points.
  32463. * In case the series data length exceeds the `cropThreshold`, or if
  32464. * the data is grouped, `series.data` doesn't contain all the
  32465. * points. Also, in case a series is hidden, the `data` array may be
  32466. * empty. To access raw values, `series.options.data` will always be
  32467. * up to date. `Series.data` only contains the points that have been
  32468. * created on demand. To modify the data, use
  32469. * {@link Highcharts.Series#setData} or
  32470. * {@link Highcharts.Point#update}.
  32471. *
  32472. * @see Series.points
  32473. *
  32474. * @name Highcharts.Series#data
  32475. * @type {Array<Highcharts.Point>}
  32476. */
  32477. series.data = data;
  32478. /**
  32479. * An array containing all currently visible point objects. In case
  32480. * of cropping, the cropped-away points are not part of this array.
  32481. * The `series.points` array starts at `series.cropStart` compared
  32482. * to `series.data` and `series.options.data`. If however the series
  32483. * data is grouped, these can't be correlated one to one. To modify
  32484. * the data, use {@link Highcharts.Series#setData} or
  32485. * {@link Highcharts.Point#update}.
  32486. *
  32487. * @name Highcharts.Series#points
  32488. * @type {Array<Highcharts.Point>}
  32489. */
  32490. series.points = points;
  32491. fireEvent(this, 'afterGeneratePoints');
  32492. }
  32493. /**
  32494. * Get current X extremes for the visible data.
  32495. *
  32496. * @private
  32497. * @function Highcharts.Series#getXExtremes
  32498. * @param {Array<number>} xData
  32499. * The data to inspect. Defaults to the current data within the visible
  32500. * range.
  32501. */
  32502. getXExtremes(xData) {
  32503. return {
  32504. min: arrayMin(xData),
  32505. max: arrayMax(xData)
  32506. };
  32507. }
  32508. /**
  32509. * Calculate Y extremes for the visible data. The result is returned
  32510. * as an object with `dataMin` and `dataMax` properties.
  32511. *
  32512. * @private
  32513. * @function Highcharts.Series#getExtremes
  32514. * @param {Array<number>} [yData]
  32515. * The data to inspect. Defaults to the current data within the visible
  32516. * range.
  32517. * @param {boolean} [forceExtremesFromAll]
  32518. * Force getting extremes of a total series data range.
  32519. */
  32520. getExtremes(yData, forceExtremesFromAll) {
  32521. const xAxis = this.xAxis, yAxis = this.yAxis, xData = this.processedXData || this.xData, activeYData = [],
  32522. // Handle X outside the viewed area. This does not work with
  32523. // non-sorted data like scatter (#7639).
  32524. shoulder = this.requireSorting ? this.cropShoulder : 0, positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false;
  32525. // #2117, need to compensate for log X axis
  32526. let xExtremes, validValue, withinRange, x, y, i, j, xMin = 0, xMax = 0, activeCounter = 0;
  32527. yData = yData || this.stackedYData || this.processedYData || [];
  32528. const yDataLength = yData.length;
  32529. if (xAxis) {
  32530. xExtremes = xAxis.getExtremes();
  32531. xMin = xExtremes.min;
  32532. xMax = xExtremes.max;
  32533. }
  32534. for (i = 0; i < yDataLength; i++) {
  32535. x = xData[i];
  32536. y = yData[i];
  32537. // For points within the visible range, including the first
  32538. // point outside the visible range (#7061), consider y extremes.
  32539. validValue = ((isNumber(y) || isArray(y)) &&
  32540. ((y.length || y > 0) || !positiveValuesOnly));
  32541. withinRange = (forceExtremesFromAll ||
  32542. this.getExtremesFromAll ||
  32543. this.options.getExtremesFromAll ||
  32544. this.cropped ||
  32545. !xAxis || // for colorAxis support
  32546. ((xData[i + shoulder] || x) >= xMin &&
  32547. (xData[i - shoulder] || x) <= xMax));
  32548. if (validValue && withinRange) {
  32549. j = y.length;
  32550. if (j) { // array, like ohlc or range data
  32551. while (j--) {
  32552. if (isNumber(y[j])) { // #7380, #11513
  32553. activeYData[activeCounter++] = y[j];
  32554. }
  32555. }
  32556. }
  32557. else {
  32558. activeYData[activeCounter++] = y;
  32559. }
  32560. }
  32561. }
  32562. const dataExtremes = {
  32563. activeYData,
  32564. dataMin: arrayMin(activeYData),
  32565. dataMax: arrayMax(activeYData)
  32566. };
  32567. fireEvent(this, 'afterGetExtremes', { dataExtremes });
  32568. return dataExtremes;
  32569. }
  32570. /**
  32571. * Set the current data extremes as `dataMin` and `dataMax` on the
  32572. * Series item. Use this only when the series properties should be
  32573. * updated.
  32574. *
  32575. * @private
  32576. * @function Highcharts.Series#applyExtremes
  32577. */
  32578. applyExtremes() {
  32579. const dataExtremes = this.getExtremes();
  32580. /**
  32581. * Contains the minimum value of the series' data point. Some series
  32582. * types like `networkgraph` do not support this property as they
  32583. * lack a `y`-value.
  32584. * @name Highcharts.Series#dataMin
  32585. * @type {number|undefined}
  32586. * @readonly
  32587. */
  32588. this.dataMin = dataExtremes.dataMin;
  32589. /**
  32590. * Contains the maximum value of the series' data point. Some series
  32591. * types like `networkgraph` do not support this property as they
  32592. * lack a `y`-value.
  32593. * @name Highcharts.Series#dataMax
  32594. * @type {number|undefined}
  32595. * @readonly
  32596. */
  32597. this.dataMax = dataExtremes.dataMax;
  32598. return dataExtremes;
  32599. }
  32600. /**
  32601. * Find and return the first non null point in the data
  32602. *
  32603. * @private
  32604. * @function Highcharts.Series.getFirstValidPoint
  32605. * @param {Array<Highcharts.PointOptionsType>} data
  32606. * Array of options for points
  32607. */
  32608. getFirstValidPoint(data) {
  32609. const dataLength = data.length;
  32610. let i = 0, firstPoint = null;
  32611. while (firstPoint === null && i < dataLength) {
  32612. firstPoint = data[i];
  32613. i++;
  32614. }
  32615. return firstPoint;
  32616. }
  32617. /**
  32618. * Translate data points from raw data values to chart specific
  32619. * positioning data needed later in the `drawPoints` and `drawGraph`
  32620. * functions. This function can be overridden in plugins and custom
  32621. * series type implementations.
  32622. *
  32623. * @function Highcharts.Series#translate
  32624. *
  32625. * @emits Highcharts.Series#events:translate
  32626. */
  32627. translate() {
  32628. var _a;
  32629. if (!this.processedXData) { // hidden series
  32630. this.processData();
  32631. }
  32632. this.generatePoints();
  32633. 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
  32634. dynamicallyPlaced = Boolean(pointPlacement), threshold = options.threshold, stackThreshold = options.startFromThreshold ? threshold : 0;
  32635. let i, plotX, lastPlotX, stackIndicator, closestPointRangePx = Number.MAX_VALUE;
  32636. /**
  32637. * Plotted coordinates need to be within a limited range. Drawing
  32638. * too far outside the viewport causes various rendering issues
  32639. * (#3201, #3923, #7555).
  32640. * @private
  32641. */
  32642. function limitedRange(val) {
  32643. return clamp(val, -1e5, 1e5);
  32644. }
  32645. // Translate each point
  32646. for (i = 0; i < dataLength; i++) {
  32647. const point = points[i], xValue = point.x;
  32648. let stackItem, stackValues, yValue = point.y, lowValue = point.low;
  32649. const stacks = stacking && ((_a = yAxis.stacking) === null || _a === void 0 ? void 0 : _a.stacks[(series.negStacks &&
  32650. yValue <
  32651. (stackThreshold ? 0 : threshold) ?
  32652. '-' :
  32653. '') + series.stackKey]);
  32654. plotX = xAxis.translate(// #3923
  32655. xValue, false, false, false, true, pointPlacement);
  32656. /**
  32657. * The translated X value for the point in terms of pixels. Relative
  32658. * to the X axis position if the series has one, otherwise relative
  32659. * to the plot area. Depending on the series type this value might
  32660. * not be defined.
  32661. * @name Highcharts.Point#plotX
  32662. * @type {number|undefined}
  32663. */
  32664. point.plotX = isNumber(plotX) ? correctFloat(// #5236
  32665. limitedRange(plotX) // #3923
  32666. ) : void 0;
  32667. // Calculate the bottom y value for stacked series
  32668. if (stacking &&
  32669. series.visible &&
  32670. stacks &&
  32671. stacks[xValue]) {
  32672. stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
  32673. if (!point.isNull && stackIndicator.key) {
  32674. stackItem = stacks[xValue];
  32675. stackValues = stackItem.points[stackIndicator.key];
  32676. }
  32677. if (stackItem && isArray(stackValues)) {
  32678. lowValue = stackValues[0];
  32679. yValue = stackValues[1];
  32680. if (lowValue === stackThreshold &&
  32681. stackIndicator.key === stacks[xValue].base) {
  32682. lowValue = pick(isNumber(threshold) ? threshold : yAxis.min);
  32683. }
  32684. // #1200, #1232
  32685. if (yAxis.positiveValuesOnly &&
  32686. defined(lowValue) &&
  32687. lowValue <= 0) {
  32688. lowValue = void 0;
  32689. }
  32690. point.total = point.stackTotal = pick(stackItem.total);
  32691. point.percentage = defined(point.y) && stackItem.total ?
  32692. (point.y / stackItem.total * 100) : void 0;
  32693. point.stackY = yValue;
  32694. // in case of variwide series (where widths of points are
  32695. // different in most cases), stack labels are positioned
  32696. // wrongly, so the call of the setOffset is omited here and
  32697. // labels are correctly positioned later, at the end of the
  32698. // variwide's translate function (#10962)
  32699. if (!series.irregularWidths) {
  32700. stackItem.setOffset(series.pointXOffset || 0, series.barW || 0, void 0, void 0, void 0, series.xAxis);
  32701. }
  32702. }
  32703. }
  32704. // Set translated yBottom or remove it
  32705. point.yBottom = defined(lowValue) ?
  32706. limitedRange(yAxis.translate(lowValue, false, true, false, true)) :
  32707. void 0;
  32708. // General hook, used for Highcharts Stock compare and cumulative
  32709. if (series.dataModify) {
  32710. yValue = series.dataModify.modifyValue(yValue, i);
  32711. }
  32712. // Set the the plotY value, reset it for redraws #3201, #18422
  32713. let plotY;
  32714. if (isNumber(yValue) && point.plotX !== void 0) {
  32715. plotY = yAxis.translate(yValue, false, true, false, true);
  32716. plotY = isNumber(plotY) ? limitedRange(plotY) : void 0;
  32717. }
  32718. /**
  32719. * The translated Y value for the point in terms of pixels. Relative
  32720. * to the Y axis position if the series has one, otherwise relative
  32721. * to the plot area. Depending on the series type this value might
  32722. * not be defined.
  32723. * @name Highcharts.Point#plotY
  32724. * @type {number|undefined}
  32725. */
  32726. point.plotY = plotY;
  32727. point.isInside = this.isPointInside(point);
  32728. // Set client related positions for mouse tracking
  32729. point.clientX = dynamicallyPlaced ?
  32730. correctFloat(xAxis.translate(xValue, false, false, false, true, pointPlacement)) :
  32731. plotX; // #1514, #5383, #5518
  32732. // Negative points #19028
  32733. point.negative = (point.y || 0) < (threshold || 0);
  32734. // some API data
  32735. point.category = pick(categories && categories[point.x], point.x);
  32736. // Determine auto enabling of markers (#3635, #5099)
  32737. if (!point.isNull && point.visible !== false) {
  32738. if (typeof lastPlotX !== 'undefined') {
  32739. closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
  32740. }
  32741. lastPlotX = plotX;
  32742. }
  32743. // Find point zone
  32744. point.zone = this.zones.length ? point.getZone() : void 0;
  32745. // Animate new points with data sorting
  32746. if (!point.graphic && series.group && enabledDataSorting) {
  32747. point.isNew = true;
  32748. }
  32749. }
  32750. series.closestPointRangePx = closestPointRangePx;
  32751. fireEvent(this, 'afterTranslate');
  32752. }
  32753. /**
  32754. * Return the series points with null points filtered out.
  32755. *
  32756. * @function Highcharts.Series#getValidPoints
  32757. *
  32758. * @param {Array<Highcharts.Point>} [points]
  32759. * The points to inspect, defaults to {@link Series.points}.
  32760. *
  32761. * @param {boolean} [insideOnly=false]
  32762. * Whether to inspect only the points that are inside the visible view.
  32763. *
  32764. * @param {boolean} [allowNull=false]
  32765. * Whether to allow null points to pass as valid points.
  32766. *
  32767. * @return {Array<Highcharts.Point>}
  32768. * The valid points.
  32769. */
  32770. getValidPoints(points, insideOnly, allowNull) {
  32771. const chart = this.chart;
  32772. // #3916, #5029, #5085
  32773. return (points || this.points || []).filter(function (point) {
  32774. const { plotX, plotY } = point,
  32775. // Undefined plotY is treated as null when negative values
  32776. // in log axis (#18422)
  32777. asNull = !allowNull && (point.isNull || !isNumber(plotY));
  32778. if (asNull || (insideOnly && !chart.isInsidePlot(plotX, plotY, { inverted: chart.inverted }))) {
  32779. return false;
  32780. }
  32781. return point.visible !== false;
  32782. });
  32783. }
  32784. /**
  32785. * Get the clipping for the series. Could be called for a series to
  32786. * initiate animating the clip or to set the final clip (only width
  32787. * and x).
  32788. *
  32789. * @private
  32790. * @function Highcharts.Series#getClip
  32791. */
  32792. getClipBox() {
  32793. const { chart, xAxis, yAxis } = this;
  32794. // If no axes on the series, use global clipBox
  32795. const seriesBox = merge(chart.clipBox);
  32796. // Otherwise, use clipBox.width which is corrected for plotBorderWidth
  32797. // and clipOffset
  32798. if (xAxis && xAxis.len !== chart.plotSizeX) {
  32799. seriesBox.width = xAxis.len;
  32800. }
  32801. if (yAxis && yAxis.len !== chart.plotSizeY) {
  32802. seriesBox.height = yAxis.len;
  32803. }
  32804. return seriesBox;
  32805. }
  32806. /**
  32807. * Get the shared clip key, creating it if it doesn't exist.
  32808. *
  32809. * @private
  32810. * @function Highcharts.Series#getSharedClipKey
  32811. */
  32812. getSharedClipKey() {
  32813. this.sharedClipKey = (this.options.xAxis || 0) + ',' +
  32814. (this.options.yAxis || 0);
  32815. return this.sharedClipKey;
  32816. }
  32817. /**
  32818. * Set the clipping for the series. For animated series the clip is later
  32819. * modified.
  32820. *
  32821. * @private
  32822. * @function Highcharts.Series#setClip
  32823. */
  32824. setClip() {
  32825. const { chart, group, markerGroup } = this, sharedClips = chart.sharedClips, renderer = chart.renderer, clipBox = this.getClipBox(), sharedClipKey = this.getSharedClipKey(); // #4526
  32826. let clipRect = sharedClips[sharedClipKey];
  32827. // If a clipping rectangle for the same set of axes does not exist,
  32828. // create it
  32829. if (!clipRect) {
  32830. sharedClips[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
  32831. // When setting chart size, or when the series is rendered again before
  32832. // starting animating, in compliance to a responsive rule
  32833. }
  32834. else {
  32835. clipRect.animate(clipBox);
  32836. }
  32837. if (group) {
  32838. // When clip is false, reset to no clip after animation
  32839. group.clip(this.options.clip === false ? void 0 : clipRect);
  32840. }
  32841. // Unclip temporary animation clip
  32842. if (markerGroup) {
  32843. markerGroup.clip();
  32844. }
  32845. }
  32846. /**
  32847. * Animate in the series. Called internally twice. First with the `init`
  32848. * parameter set to true, which sets up the initial state of the
  32849. * animation. Then when ready, it is called with the `init` parameter
  32850. * undefined, in order to perform the actual animation.
  32851. *
  32852. * @function Highcharts.Series#animate
  32853. *
  32854. * @param {boolean} [init]
  32855. * Initialize the animation.
  32856. */
  32857. animate(init) {
  32858. const { chart, group, markerGroup } = this, inverted = chart.inverted, animation = animObject(this.options.animation),
  32859. // The key for temporary animation clips
  32860. animationClipKey = [
  32861. this.getSharedClipKey(),
  32862. animation.duration,
  32863. animation.easing,
  32864. animation.defer
  32865. ].join(',');
  32866. let animationClipRect = chart.sharedClips[animationClipKey], markerAnimationClipRect = chart.sharedClips[animationClipKey + 'm'];
  32867. // Initialize the animation. Set up the clipping rectangle.
  32868. if (init && group) {
  32869. const clipBox = this.getClipBox();
  32870. // Create temporary animation clips
  32871. if (!animationClipRect) {
  32872. clipBox.width = 0;
  32873. if (inverted) {
  32874. clipBox.x = chart.plotHeight;
  32875. }
  32876. animationClipRect = chart.renderer.clipRect(clipBox);
  32877. chart.sharedClips[animationClipKey] = animationClipRect;
  32878. // The marker clip box. The number 99 is a safe margin to avoid
  32879. // markers being clipped during animation.
  32880. const markerClipBox = {
  32881. x: inverted ? -99 : -99,
  32882. y: inverted ? -99 : -99,
  32883. width: inverted ? chart.plotWidth + 199 : 99,
  32884. height: inverted ? 99 : chart.plotHeight + 199
  32885. };
  32886. markerAnimationClipRect = chart.renderer.clipRect(markerClipBox);
  32887. chart.sharedClips[animationClipKey + 'm'] = markerAnimationClipRect;
  32888. }
  32889. else {
  32890. // When height changes during animation, typically due to
  32891. // responsive settings
  32892. animationClipRect.attr('height', clipBox.height);
  32893. }
  32894. group.clip(animationClipRect);
  32895. if (markerGroup) {
  32896. markerGroup.clip(markerAnimationClipRect);
  32897. }
  32898. // Run the animation
  32899. }
  32900. else if (animationClipRect &&
  32901. // Only first series in this pane
  32902. !animationClipRect.hasClass('highcharts-animating')) {
  32903. const finalBox = this.getClipBox(), step = animation.step;
  32904. // Only do this when there are actually markers
  32905. if (markerGroup && markerGroup.element.childNodes.length) {
  32906. // To provide as smooth animation as possible, update the marker
  32907. // group clipping in steps of the main group animation
  32908. animation.step = function (val, fx) {
  32909. if (step) {
  32910. step.apply(fx, arguments);
  32911. }
  32912. if (fx.prop === 'width' &&
  32913. markerAnimationClipRect &&
  32914. markerAnimationClipRect.element) {
  32915. markerAnimationClipRect.attr(inverted ? 'height' : 'width', val + 99);
  32916. }
  32917. };
  32918. }
  32919. animationClipRect
  32920. .addClass('highcharts-animating')
  32921. .animate(finalBox, animation);
  32922. }
  32923. }
  32924. /**
  32925. * This runs after animation to land on the final plot clipping.
  32926. *
  32927. * @private
  32928. * @function Highcharts.Series#afterAnimate
  32929. *
  32930. * @emits Highcharts.Series#event:afterAnimate
  32931. */
  32932. afterAnimate() {
  32933. this.setClip();
  32934. // Destroy temporary clip rectangles that are no longer in use
  32935. objectEach(this.chart.sharedClips, (clip, key, sharedClips) => {
  32936. if (clip && !this.chart.container.querySelector(`[clip-path="url(#${clip.id})"]`)) {
  32937. clip.destroy();
  32938. delete sharedClips[key];
  32939. }
  32940. });
  32941. this.finishedAnimating = true;
  32942. fireEvent(this, 'afterAnimate');
  32943. }
  32944. /**
  32945. * Draw the markers for line-like series types, and columns or other
  32946. * graphical representation for {@link Point} objects for other series
  32947. * types. The resulting element is typically stored as
  32948. * {@link Point.graphic}, and is created on the first call and updated
  32949. * and moved on subsequent calls.
  32950. *
  32951. * @function Highcharts.Series#drawPoints
  32952. */
  32953. drawPoints(points = this.points) {
  32954. 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,
  32955. // Use larger or equal as radius is null in bubbles (#6321)
  32956. series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
  32957. seriesMarkerOptions.radius));
  32958. let i, point, graphic, verb, pointMarkerOptions, hasPointMarker, markerAttribs;
  32959. if (seriesMarkerOptions.enabled !== false ||
  32960. series._hasPointMarkers) {
  32961. for (i = 0; i < points.length; i++) {
  32962. point = points[i];
  32963. graphic = point.graphic;
  32964. verb = graphic ? 'animate' : 'attr';
  32965. pointMarkerOptions = point.marker || {};
  32966. hasPointMarker = !!point.marker;
  32967. const shouldDrawMarker = ((globallyEnabled &&
  32968. typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
  32969. // only draw the point if y is defined
  32970. if (shouldDrawMarker) {
  32971. // Shortcuts
  32972. const symbol = pick(pointMarkerOptions.symbol, series.symbol, 'rect');
  32973. markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
  32974. // Set starting position for point sliding animation.
  32975. if (series.enabledDataSorting) {
  32976. point.startXPos = xAxis.reversed ?
  32977. -(markerAttribs.width || 0) :
  32978. xAxis.width;
  32979. }
  32980. const isInside = point.isInside !== false;
  32981. if (!graphic &&
  32982. isInside &&
  32983. ((markerAttribs.width || 0) > 0 || point.hasImage)) {
  32984. /**
  32985. * SVG graphic representing the point in the chart. In
  32986. * some cases it may be a hidden graphic to improve
  32987. * accessibility.
  32988. *
  32989. * Typically this is a simple shape, like a `rect`
  32990. * for column charts or `path` for line markers, but
  32991. * for some complex series types like boxplot or 3D
  32992. * charts, the graphic may be a `g` element
  32993. * containing other shapes. The graphic is generated
  32994. * the first time {@link Series#drawPoints} runs,
  32995. * and updated and moved on subsequent runs.
  32996. *
  32997. * @see Highcharts.Point#graphics
  32998. *
  32999. * @name Highcharts.Point#graphic
  33000. * @type {Highcharts.SVGElement|undefined}
  33001. */
  33002. point.graphic = graphic = chart.renderer
  33003. .symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
  33004. pointMarkerOptions :
  33005. seriesMarkerOptions)
  33006. .add(markerGroup);
  33007. // Sliding animation for new points
  33008. if (series.enabledDataSorting &&
  33009. chart.hasRendered) {
  33010. graphic.attr({
  33011. x: point.startXPos
  33012. });
  33013. verb = 'animate';
  33014. }
  33015. }
  33016. if (graphic && verb === 'animate') { // update
  33017. // Since the marker group isn't clipped, each
  33018. // individual marker must be toggled
  33019. graphic[isInside ? 'show' : 'hide'](isInside)
  33020. .animate(markerAttribs);
  33021. }
  33022. // Presentational attributes
  33023. if (graphic) {
  33024. const pointAttr = series.pointAttribs(point, ((styledMode || !point.selected) ?
  33025. void 0 :
  33026. 'select'));
  33027. if (!styledMode) {
  33028. graphic[verb](pointAttr);
  33029. }
  33030. else if (colorAxis) { // #14114
  33031. graphic['css']({
  33032. fill: pointAttr.fill
  33033. });
  33034. }
  33035. }
  33036. if (graphic) {
  33037. graphic.addClass(point.getClassName(), true);
  33038. }
  33039. }
  33040. else if (graphic) {
  33041. point.graphic = graphic.destroy(); // #1269
  33042. }
  33043. }
  33044. }
  33045. }
  33046. /**
  33047. * Get non-presentational attributes for a point. Used internally for
  33048. * both styled mode and classic. Can be overridden for different series
  33049. * types.
  33050. *
  33051. * @see Series#pointAttribs
  33052. *
  33053. * @function Highcharts.Series#markerAttribs
  33054. *
  33055. * @param {Highcharts.Point} point
  33056. * The Point to inspect.
  33057. *
  33058. * @param {string} [state]
  33059. * The state, can be either `hover`, `select` or undefined.
  33060. *
  33061. * @return {Highcharts.SVGAttributes}
  33062. * A hash containing those attributes that are not settable from CSS.
  33063. */
  33064. markerAttribs(point, state) {
  33065. const seriesOptions = this.options, seriesMarkerOptions = seriesOptions.marker, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
  33066. seriesMarkerOptions.symbol), attribs = {};
  33067. let seriesStateOptions, pointStateOptions, radius = pick(pointMarkerOptions.radius, seriesMarkerOptions && seriesMarkerOptions.radius);
  33068. // Handle hover and select states
  33069. if (state) {
  33070. seriesStateOptions = seriesMarkerOptions.states[state];
  33071. pointStateOptions = pointMarkerOptions.states &&
  33072. pointMarkerOptions.states[state];
  33073. radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius && radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
  33074. 0));
  33075. }
  33076. point.hasImage = symbol && symbol.indexOf('url') === 0;
  33077. if (point.hasImage) {
  33078. radius = 0; // and subsequently width and height is not set
  33079. }
  33080. const pos = point.pos();
  33081. if (isNumber(radius) && pos) {
  33082. attribs.x = pos[0] - radius;
  33083. attribs.y = pos[1] - radius;
  33084. if (seriesOptions.crisp) {
  33085. // Math.floor for #1843:
  33086. attribs.x = Math.floor(attribs.x);
  33087. }
  33088. }
  33089. if (radius) {
  33090. attribs.width = attribs.height = 2 * radius;
  33091. }
  33092. return attribs;
  33093. }
  33094. /**
  33095. * Internal function to get presentational attributes for each point.
  33096. * Unlike {@link Series#markerAttribs}, this function should return
  33097. * those attributes that can also be set in CSS. In styled mode,
  33098. * `pointAttribs` won't be called.
  33099. *
  33100. * @private
  33101. * @function Highcharts.Series#pointAttribs
  33102. *
  33103. * @param {Highcharts.Point} [point]
  33104. * The point instance to inspect.
  33105. *
  33106. * @param {string} [state]
  33107. * The point state, can be either `hover`, `select` or 'normal'. If
  33108. * undefined, normal state is assumed.
  33109. *
  33110. * @return {Highcharts.SVGAttributes}
  33111. * The presentational attributes to be set on the point.
  33112. */
  33113. pointAttribs(point, state) {
  33114. 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;
  33115. let seriesStateOptions, pointStateOptions, color = this.color, fill, stroke, strokeWidth = pick(pointMarkerOptions.lineWidth, seriesMarkerOptions.lineWidth), opacity = 1;
  33116. color = (pointColorOption ||
  33117. zoneColor ||
  33118. pointColor ||
  33119. color);
  33120. fill = (pointMarkerOptions.fillColor ||
  33121. seriesMarkerOptions.fillColor ||
  33122. color);
  33123. stroke = (pointMarkerOptions.lineColor ||
  33124. seriesMarkerOptions.lineColor ||
  33125. color);
  33126. // Handle hover and select states
  33127. state = state || 'normal';
  33128. if (state) {
  33129. seriesStateOptions = (seriesMarkerOptions.states[state] || {});
  33130. pointStateOptions = (pointMarkerOptions.states &&
  33131. pointMarkerOptions.states[state]) || {};
  33132. strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
  33133. fill = (pointStateOptions.fillColor ||
  33134. seriesStateOptions.fillColor ||
  33135. fill);
  33136. stroke = (pointStateOptions.lineColor ||
  33137. seriesStateOptions.lineColor ||
  33138. stroke);
  33139. opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
  33140. }
  33141. return {
  33142. 'stroke': stroke,
  33143. 'stroke-width': strokeWidth,
  33144. 'fill': fill,
  33145. 'opacity': opacity
  33146. };
  33147. }
  33148. /**
  33149. * Clear DOM objects and free up memory.
  33150. *
  33151. * @private
  33152. * @function Highcharts.Series#destroy
  33153. *
  33154. * @emits Highcharts.Series#event:destroy
  33155. */
  33156. destroy(keepEventsForUpdate) {
  33157. const series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent), data = series.data || [];
  33158. let destroy, i, point, axis;
  33159. // add event hook
  33160. fireEvent(series, 'destroy', { keepEventsForUpdate });
  33161. // remove events
  33162. this.removeEvents(keepEventsForUpdate);
  33163. // erase from axes
  33164. (series.axisTypes || []).forEach(function (AXIS) {
  33165. axis = series[AXIS];
  33166. if (axis && axis.series) {
  33167. erase(axis.series, series);
  33168. axis.isDirty = axis.forceRedraw = true;
  33169. }
  33170. });
  33171. // remove legend items
  33172. if (series.legendItem) {
  33173. series.chart.legend.destroyItem(series);
  33174. }
  33175. // destroy all points with their elements
  33176. i = data.length;
  33177. while (i--) {
  33178. point = data[i];
  33179. if (point && point.destroy) {
  33180. point.destroy();
  33181. }
  33182. }
  33183. if (series.clips) {
  33184. series.clips.forEach((clip) => clip.destroy());
  33185. }
  33186. // Clear the animation timeout if we are destroying the series
  33187. // during initial animation
  33188. U.clearTimeout(series.animationTimeout);
  33189. // Destroy all SVGElements associated to the series
  33190. objectEach(series, function (val, prop) {
  33191. // Survive provides a hook for not destroying
  33192. if (val instanceof SVGElement && !val.survive) {
  33193. // issue 134 workaround
  33194. destroy = issue134 && prop === 'group' ?
  33195. 'hide' :
  33196. 'destroy';
  33197. val[destroy]();
  33198. }
  33199. });
  33200. // remove from hoverSeries
  33201. if (chart.hoverSeries === series) {
  33202. chart.hoverSeries = void 0;
  33203. }
  33204. erase(chart.series, series);
  33205. chart.orderItems('series');
  33206. // clear all members
  33207. objectEach(series, function (val, prop) {
  33208. if (!keepEventsForUpdate || prop !== 'hcEvents') {
  33209. delete series[prop];
  33210. }
  33211. });
  33212. }
  33213. /**
  33214. * Clip the graphs into zones for colors and styling.
  33215. *
  33216. * @private
  33217. * @function Highcharts.Series#applyZones
  33218. */
  33219. applyZones() {
  33220. 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;
  33221. let translatedFrom, translatedTo, clipAttr, extremes, reversed, horiz, pxRange, pxPosMin, pxPosMax, zoneArea, zoneGraph, ignoreZones = false;
  33222. if (zones.length &&
  33223. (graph || area) &&
  33224. axis &&
  33225. typeof axis.min !== 'undefined') {
  33226. reversed = axis.reversed;
  33227. horiz = axis.horiz;
  33228. // The use of the Color Threshold assumes there are no gaps
  33229. // so it is safe to hide the original graph and area
  33230. // unless it is not waterfall series, then use showLine property
  33231. // to set lines between columns to be visible (#7862)
  33232. if (graph && !this.showLine) {
  33233. graph.hide();
  33234. }
  33235. if (area) {
  33236. area.hide();
  33237. }
  33238. // Create the clips
  33239. extremes = axis.getExtremes();
  33240. zones.forEach(function (threshold, i) {
  33241. translatedFrom = reversed ?
  33242. (horiz ? chart.plotWidth : 0) :
  33243. (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
  33244. translatedFrom = clamp(pick(translatedTo, translatedFrom), 0, plotSizeMax);
  33245. translatedTo = clamp(Math.round(axis.toPixels(pick(threshold.value, extremes.max), true) || 0), 0, plotSizeMax);
  33246. if (ignoreZones) {
  33247. translatedFrom = translatedTo =
  33248. axis.toPixels(extremes.max);
  33249. }
  33250. pxRange = Math.abs(translatedFrom - translatedTo);
  33251. pxPosMin = Math.min(translatedFrom, translatedTo);
  33252. pxPosMax = Math.max(translatedFrom, translatedTo);
  33253. if (axis.isXAxis) {
  33254. clipAttr = {
  33255. x: inverted ? pxPosMax : pxPosMin,
  33256. y: 0,
  33257. width: pxRange,
  33258. height: plotSizeMax
  33259. };
  33260. if (!horiz) {
  33261. clipAttr.x = chart.plotHeight - clipAttr.x;
  33262. }
  33263. }
  33264. else {
  33265. clipAttr = {
  33266. x: 0,
  33267. y: inverted ? pxPosMax : pxPosMin,
  33268. width: plotSizeMax,
  33269. height: pxRange
  33270. };
  33271. if (horiz) {
  33272. clipAttr.y = chart.plotWidth - clipAttr.y;
  33273. }
  33274. }
  33275. if (clips[i]) {
  33276. clips[i].animate(clipAttr);
  33277. }
  33278. else {
  33279. clips[i] = renderer.clipRect(clipAttr);
  33280. }
  33281. // when no data, graph zone is not applied and after setData
  33282. // clip was ignored. As a result, it should be applied each
  33283. // time.
  33284. zoneArea = series['zone-area-' + i];
  33285. zoneGraph = series['zone-graph-' + i];
  33286. if (graph && zoneGraph) {
  33287. zoneGraph.clip(clips[i]);
  33288. }
  33289. if (area && zoneArea) {
  33290. zoneArea.clip(clips[i]);
  33291. }
  33292. // if this zone extends out of the axis, ignore the others
  33293. ignoreZones = threshold.value > extremes.max;
  33294. // Clear translatedTo for indicators
  33295. if (series.resetZones && translatedTo === 0) {
  33296. translatedTo = void 0;
  33297. }
  33298. });
  33299. this.clips = clips;
  33300. }
  33301. else if (series.visible) {
  33302. // If zones were removed, restore graph and area
  33303. if (graph) {
  33304. graph.show();
  33305. }
  33306. if (area) {
  33307. area.show();
  33308. }
  33309. }
  33310. }
  33311. /**
  33312. * General abstraction for creating plot groups like series.group,
  33313. * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
  33314. * the group will only be adjusted to the updated plot size.
  33315. *
  33316. * @private
  33317. * @function Highcharts.Series#plotGroup
  33318. */
  33319. plotGroup(prop, name, visibility, zIndex, parent) {
  33320. let group = this[prop];
  33321. const isNew = !group, attrs = {
  33322. visibility,
  33323. zIndex: zIndex || 0.1 // Pointer logic uses this
  33324. };
  33325. // Avoid setting undefined opacity, or in styled mode
  33326. if (typeof this.opacity !== 'undefined' &&
  33327. !this.chart.styledMode && this.state !== 'inactive' // #13719
  33328. ) {
  33329. attrs.opacity = this.opacity;
  33330. }
  33331. // Generate it on first call
  33332. if (isNew) {
  33333. this[prop] = group = this.chart.renderer
  33334. .g()
  33335. .add(parent);
  33336. }
  33337. // Add the class names, and replace existing ones as response to
  33338. // Series.update (#6660)
  33339. group.addClass(('highcharts-' + name +
  33340. ' highcharts-series-' + this.index +
  33341. ' highcharts-' + this.type + '-series ' +
  33342. (defined(this.colorIndex) ?
  33343. 'highcharts-color-' + this.colorIndex + ' ' :
  33344. '') +
  33345. (this.options.className || '') +
  33346. (group.hasClass('highcharts-tracker') ?
  33347. ' highcharts-tracker' :
  33348. '')), true);
  33349. // Place it on first and subsequent (redraw) calls
  33350. group.attr(attrs)[isNew ? 'attr' : 'animate'](this.getPlotBox(name));
  33351. return group;
  33352. }
  33353. /**
  33354. * Get the translation and scale for the plot area of this series.
  33355. *
  33356. * @function Highcharts.Series#getPlotBox
  33357. */
  33358. getPlotBox(name) {
  33359. let horAxis = this.xAxis, vertAxis = this.yAxis;
  33360. const chart = this.chart, inverted = (chart.inverted &&
  33361. !chart.polar &&
  33362. horAxis &&
  33363. this.invertible !== false &&
  33364. name === 'series');
  33365. // Swap axes for inverted (#2339)
  33366. if (chart.inverted) {
  33367. horAxis = vertAxis;
  33368. vertAxis = this.xAxis;
  33369. }
  33370. return {
  33371. translateX: horAxis ? horAxis.left : chart.plotLeft,
  33372. translateY: vertAxis ? vertAxis.top : chart.plotTop,
  33373. rotation: inverted ? 90 : 0,
  33374. rotationOriginX: inverted ?
  33375. (horAxis.len - vertAxis.len) / 2 :
  33376. 0,
  33377. rotationOriginY: inverted ?
  33378. (horAxis.len + vertAxis.len) / 2 :
  33379. 0,
  33380. scaleX: inverted ? -1 : 1,
  33381. scaleY: 1
  33382. };
  33383. }
  33384. /**
  33385. * Removes the event handlers attached previously with addEvents.
  33386. * @private
  33387. * @function Highcharts.Series#removeEvents
  33388. */
  33389. removeEvents(keepEventsForUpdate) {
  33390. const series = this;
  33391. if (!keepEventsForUpdate) {
  33392. // remove all events
  33393. removeEvent(series);
  33394. }
  33395. if (series.eventsToUnbind.length) {
  33396. // remove only internal events for proper update
  33397. // #12355 - solves problem with multiple destroy events
  33398. series.eventsToUnbind.forEach(function (unbind) {
  33399. unbind();
  33400. });
  33401. series.eventsToUnbind.length = 0;
  33402. }
  33403. }
  33404. /**
  33405. * Render the graph and markers. Called internally when first rendering
  33406. * and later when redrawing the chart. This function can be extended in
  33407. * plugins, but normally shouldn't be called directly.
  33408. *
  33409. * @function Highcharts.Series#render
  33410. *
  33411. * @emits Highcharts.Series#event:afterRender
  33412. */
  33413. render() {
  33414. const series = this, chart = series.chart, options = series.options, animOptions = animObject(options.animation), visibility = series.visible ?
  33415. 'inherit' : 'hidden', // #2597
  33416. zIndex = options.zIndex, hasRendered = series.hasRendered, chartSeriesGroup = chart.seriesGroup, inverted = chart.inverted;
  33417. let animDuration = (!series.finishedAnimating) ?
  33418. animOptions.duration : 0;
  33419. fireEvent(this, 'render');
  33420. // the group
  33421. const group = series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
  33422. series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
  33423. // Initial clipping, applies to columns etc. (#3839).
  33424. if (options.clip !== false) {
  33425. series.setClip();
  33426. }
  33427. // Initialize the animation
  33428. if (series.animate && animDuration) {
  33429. series.animate(true);
  33430. }
  33431. // Draw the graph if any
  33432. if (series.drawGraph) {
  33433. series.drawGraph();
  33434. series.applyZones();
  33435. }
  33436. // Draw the points
  33437. if (series.visible) {
  33438. series.drawPoints();
  33439. }
  33440. // Draw the data labels
  33441. if (series.drawDataLabels) {
  33442. series.drawDataLabels();
  33443. }
  33444. // In pie charts, slices are added to the DOM, but actual rendering
  33445. // is postponed until labels reserved their space
  33446. if (series.redrawPoints) {
  33447. series.redrawPoints();
  33448. }
  33449. // Draw the mouse tracking area
  33450. if (series.drawTracker &&
  33451. options.enableMouseTracking) {
  33452. series.drawTracker();
  33453. }
  33454. // Run the animation
  33455. if (series.animate && animDuration) {
  33456. series.animate();
  33457. }
  33458. // Call the afterAnimate function on animation complete (but don't
  33459. // overwrite the animation.complete option which should be available
  33460. // to the user).
  33461. if (!hasRendered) {
  33462. // Additional time if defer is defined before afterAnimate
  33463. // will be triggered
  33464. if (animDuration && animOptions.defer) {
  33465. animDuration += animOptions.defer;
  33466. }
  33467. series.animationTimeout = syncTimeout(function () {
  33468. series.afterAnimate();
  33469. }, animDuration || 0);
  33470. }
  33471. // Means data is in accordance with what you see
  33472. series.isDirty = false;
  33473. // (See #322) series.isDirty = series.isDirtyData = false; // means
  33474. // data is in accordance with what you see
  33475. series.hasRendered = true;
  33476. fireEvent(series, 'afterRender');
  33477. }
  33478. /**
  33479. * Redraw the series. This function is called internally from
  33480. * `chart.redraw` and normally shouldn't be called directly.
  33481. * @private
  33482. * @function Highcharts.Series#redraw
  33483. */
  33484. redraw() {
  33485. // Cache it here as it is set to false in render, but used after
  33486. const wasDirty = this.isDirty || this.isDirtyData;
  33487. this.translate();
  33488. this.render();
  33489. if (wasDirty) { // #3868, #3945
  33490. delete this.kdTree;
  33491. }
  33492. }
  33493. /**
  33494. * Find the nearest point from a pointer event. This applies to series that
  33495. * use k-d-trees to get the nearest point. Native pointer events must be
  33496. * normalized using `Pointer.normalize`, that adds `chartX` and `chartY`
  33497. * properties.
  33498. *
  33499. * @sample highcharts/demo/synchronized-charts
  33500. * Synchronized charts with tooltips
  33501. *
  33502. * @function Highcharts.Series#searchPoint
  33503. *
  33504. * @param {Highcharts.PointerEvent} e
  33505. * The normalized pointer event
  33506. * @param {boolean} [compareX=false]
  33507. * Search only by the X value, not Y
  33508. *
  33509. * @return {Point|undefined}
  33510. * The closest point to the pointer event
  33511. */
  33512. searchPoint(e, compareX) {
  33513. const series = this, xAxis = series.xAxis, yAxis = series.yAxis, inverted = series.chart.inverted;
  33514. return this.searchKDTree({
  33515. clientX: inverted ?
  33516. xAxis.len - e.chartY + xAxis.pos :
  33517. e.chartX - xAxis.pos,
  33518. plotY: inverted ?
  33519. yAxis.len - e.chartX + yAxis.pos :
  33520. e.chartY - yAxis.pos
  33521. }, compareX, e);
  33522. }
  33523. /**
  33524. * Build the k-d-tree that is used by mouse and touch interaction to get
  33525. * the closest point. Line-like series typically have a one-dimensional
  33526. * tree where points are searched along the X axis, while scatter-like
  33527. * series typically search in two dimensions, X and Y.
  33528. *
  33529. * @private
  33530. * @function Highcharts.Series#buildKDTree
  33531. */
  33532. buildKDTree(e) {
  33533. // Prevent multiple k-d-trees from being built simultaneously
  33534. // (#6235)
  33535. this.buildingKdTree = true;
  33536. const series = this, dimensions = series.options.findNearestPointBy
  33537. .indexOf('y') > -1 ? 2 : 1;
  33538. /**
  33539. * Internal function
  33540. * @private
  33541. */
  33542. function _kdtree(points, depth, dimensions) {
  33543. const length = points && points.length;
  33544. let axis, median;
  33545. if (length) {
  33546. // alternate between the axis
  33547. axis = series.kdAxisArray[depth % dimensions];
  33548. // sort point array
  33549. points.sort(function (a, b) {
  33550. return a[axis] - b[axis];
  33551. });
  33552. median = Math.floor(length / 2);
  33553. // build and return nod
  33554. return {
  33555. point: points[median],
  33556. left: _kdtree(points.slice(0, median), depth + 1, dimensions),
  33557. right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
  33558. };
  33559. }
  33560. }
  33561. /**
  33562. * Start the recursive build process with a clone of the points
  33563. * array and null points filtered out. (#3873)
  33564. * @private
  33565. */
  33566. function startRecursive() {
  33567. series.kdTree = _kdtree(series.getValidPoints(null,
  33568. // For line-type series restrict to plot area, but
  33569. // column-type series not (#3916, #4511)
  33570. !series.directTouch), dimensions, dimensions);
  33571. series.buildingKdTree = false;
  33572. }
  33573. delete series.kdTree;
  33574. // For testing tooltips, don't build async. Also if touchstart, we
  33575. // may be dealing with click events on mobile, so don't delay
  33576. // (#6817).
  33577. syncTimeout(startRecursive, series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1);
  33578. }
  33579. /**
  33580. * @private
  33581. * @function Highcharts.Series#searchKDTree
  33582. */
  33583. searchKDTree(point, compareX, e) {
  33584. const series = this, kdX = this.kdAxisArray[0], kdY = this.kdAxisArray[1], kdComparer = compareX ? 'distX' : 'dist', kdDimensions = series.options.findNearestPointBy
  33585. .indexOf('y') > -1 ? 2 : 1;
  33586. /**
  33587. * Set the one and two dimensional distance on the point object.
  33588. * @private
  33589. */
  33590. function setDistance(p1, p2) {
  33591. const x = (defined(p1[kdX]) &&
  33592. defined(p2[kdX])) ?
  33593. Math.pow(p1[kdX] - p2[kdX], 2) :
  33594. null, y = (defined(p1[kdY]) &&
  33595. defined(p2[kdY])) ?
  33596. Math.pow(p1[kdY] - p2[kdY], 2) :
  33597. null, r = (x || 0) + (y || 0);
  33598. p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
  33599. p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
  33600. }
  33601. /**
  33602. * @private
  33603. */
  33604. function _search(search, tree, depth, dimensions) {
  33605. const point = tree.point, axis = series.kdAxisArray[depth % dimensions];
  33606. let nPoint1, nPoint2, ret = point;
  33607. setDistance(search, point);
  33608. // Pick side based on distance to splitting point
  33609. const tdist = search[axis] - point[axis], sideA = tdist < 0 ? 'left' : 'right', sideB = tdist < 0 ? 'right' : 'left';
  33610. // End of tree
  33611. if (tree[sideA]) {
  33612. nPoint1 = _search(search, tree[sideA], depth + 1, dimensions);
  33613. ret = (nPoint1[kdComparer] <
  33614. ret[kdComparer] ?
  33615. nPoint1 :
  33616. point);
  33617. }
  33618. if (tree[sideB]) {
  33619. // compare distance to current best to splitting point to
  33620. // decide whether to check side B or not
  33621. if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
  33622. nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
  33623. ret = (nPoint2[kdComparer] <
  33624. ret[kdComparer] ?
  33625. nPoint2 :
  33626. ret);
  33627. }
  33628. }
  33629. return ret;
  33630. }
  33631. if (!this.kdTree && !this.buildingKdTree) {
  33632. this.buildKDTree(e);
  33633. }
  33634. if (this.kdTree) {
  33635. return _search(point, this.kdTree, kdDimensions, kdDimensions);
  33636. }
  33637. }
  33638. /**
  33639. * @private
  33640. * @function Highcharts.Series#pointPlacementToXValue
  33641. */
  33642. pointPlacementToXValue() {
  33643. const { options: { pointPlacement, pointRange }, xAxis: axis } = this;
  33644. let factor = pointPlacement;
  33645. // Point placement is relative to each series pointRange (#5889)
  33646. if (factor === 'between') {
  33647. factor = axis.reversed ? -0.5 : 0.5; // #11955
  33648. }
  33649. return isNumber(factor) ?
  33650. factor * (pointRange || axis.pointRange) :
  33651. 0;
  33652. }
  33653. /**
  33654. * @private
  33655. * @function Highcharts.Series#isPointInside
  33656. */
  33657. isPointInside(point) {
  33658. const { chart, xAxis, yAxis } = this, isInside = (typeof point.plotY !== 'undefined' &&
  33659. typeof point.plotX !== 'undefined' &&
  33660. point.plotY >= 0 &&
  33661. point.plotY <= (yAxis ? yAxis.len : chart.plotHeight) &&
  33662. point.plotX >= 0 &&
  33663. point.plotX <= (xAxis ? xAxis.len : chart.plotWidth));
  33664. return isInside;
  33665. }
  33666. /**
  33667. * Draw the tracker object that sits above all data labels and markers to
  33668. * track mouse events on the graph or points. For the line type charts
  33669. * the tracker uses the same graphPath, but with a greater stroke width
  33670. * for better control.
  33671. * @private
  33672. */
  33673. drawTracker() {
  33674. const series = this, options = series.options, trackByArea = options.trackByArea, trackerPath = [].concat(trackByArea ?
  33675. series.areaPath :
  33676. series.graphPath),
  33677. // trackerPathLength = trackerPath.length,
  33678. chart = series.chart, pointer = chart.pointer, renderer = chart.renderer, snap = chart.options.tooltip.snap, tracker = series.tracker, onMouseOver = function (e) {
  33679. if (options.enableMouseTracking &&
  33680. chart.hoverSeries !== series) {
  33681. series.onMouseOver();
  33682. }
  33683. },
  33684. /*
  33685. * Empirical lowest possible opacities for TRACKER_FILL for an
  33686. * element to stay invisible but clickable
  33687. * IE9: 0.00000000001 (unlimited)
  33688. * IE10: 0.0001 (exporting only)
  33689. * FF: 0.00000000001 (unlimited)
  33690. * Chrome: 0.000001
  33691. * Safari: 0.000001
  33692. * Opera: 0.00000000001 (unlimited)
  33693. */
  33694. TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
  33695. let i;
  33696. // Draw the tracker
  33697. if (tracker) {
  33698. tracker.attr({ d: trackerPath });
  33699. }
  33700. else if (series.graph) { // create
  33701. series.tracker = renderer.path(trackerPath)
  33702. .attr({
  33703. visibility: series.visible ? 'inherit' : 'hidden',
  33704. zIndex: 2
  33705. })
  33706. .addClass(trackByArea ?
  33707. 'highcharts-tracker-area' :
  33708. 'highcharts-tracker-line')
  33709. .add(series.group);
  33710. if (!chart.styledMode) {
  33711. series.tracker.attr({
  33712. 'stroke-linecap': 'round',
  33713. 'stroke-linejoin': 'round',
  33714. stroke: TRACKER_FILL,
  33715. fill: trackByArea ? TRACKER_FILL : 'none',
  33716. 'stroke-width': series.graph.strokeWidth() +
  33717. (trackByArea ? 0 : 2 * snap)
  33718. });
  33719. }
  33720. // The tracker is added to the series group, which is clipped, but
  33721. // is covered by the marker group. So the marker group also needs to
  33722. // capture events.
  33723. [
  33724. series.tracker,
  33725. series.markerGroup,
  33726. series.dataLabelsGroup
  33727. ].forEach(function (tracker) {
  33728. if (tracker) {
  33729. tracker.addClass('highcharts-tracker')
  33730. .on('mouseover', onMouseOver)
  33731. .on('mouseout', function (e) {
  33732. pointer.onTrackerMouseOut(e);
  33733. });
  33734. if (options.cursor && !chart.styledMode) {
  33735. tracker.css({ cursor: options.cursor });
  33736. }
  33737. if (hasTouch) {
  33738. tracker.on('touchstart', onMouseOver);
  33739. }
  33740. }
  33741. });
  33742. }
  33743. fireEvent(this, 'afterDrawTracker');
  33744. }
  33745. /**
  33746. * Add a point to the series after render time. The point can be added at
  33747. * the end, or by giving it an X value, to the start or in the middle of the
  33748. * series.
  33749. *
  33750. * @sample highcharts/members/series-addpoint-append/
  33751. * Append point
  33752. * @sample highcharts/members/series-addpoint-append-and-shift/
  33753. * Append and shift
  33754. * @sample highcharts/members/series-addpoint-x-and-y/
  33755. * Both X and Y values given
  33756. * @sample highcharts/members/series-addpoint-pie/
  33757. * Append pie slice
  33758. * @sample stock/members/series-addpoint/
  33759. * Append 100 points in Highcharts Stock
  33760. * @sample stock/members/series-addpoint-shift/
  33761. * Append and shift in Highcharts Stock
  33762. * @sample maps/members/series-addpoint/
  33763. * Add a point in Highmaps
  33764. *
  33765. * @function Highcharts.Series#addPoint
  33766. *
  33767. * @param {Highcharts.PointOptionsType} options
  33768. * The point options. If options is a single number, a point with
  33769. * that y value is appended to the series. If it is an array, it will
  33770. * be interpreted as x and y values respectively. If it is an
  33771. * object, advanced options as outlined under `series.data` are
  33772. * applied.
  33773. *
  33774. * @param {boolean} [redraw=true]
  33775. * Whether to redraw the chart after the point is added. When adding
  33776. * more than one point, it is highly recommended that the redraw
  33777. * option be set to false, and instead {@link Chart#redraw} is
  33778. * explicitly called after the adding of points is finished.
  33779. * Otherwise, the chart will redraw after adding each point.
  33780. *
  33781. * @param {boolean} [shift=false]
  33782. * If true, a point is shifted off the start of the series as one is
  33783. * appended to the end.
  33784. *
  33785. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33786. * Whether to apply animation, and optionally animation
  33787. * configuration.
  33788. *
  33789. * @param {boolean} [withEvent=true]
  33790. * Used internally, whether to fire the series `addPoint` event.
  33791. *
  33792. * @emits Highcharts.Series#event:addPoint
  33793. */
  33794. addPoint(options, redraw, shift, animation, withEvent) {
  33795. 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;
  33796. let isInTheMiddle, i;
  33797. // Optional redraw, defaults to true
  33798. redraw = pick(redraw, true);
  33799. // Get options and push the point to xData, yData and series.options. In
  33800. // series.generatePoints the Point instance will be created on demand
  33801. // and pushed to the series.data array.
  33802. const point = { series: series };
  33803. series.pointClass.prototype.applyOptions.apply(point, [options]);
  33804. const x = point.x;
  33805. // Get the insertion point
  33806. i = xData.length;
  33807. if (series.requireSorting && x < xData[i - 1]) {
  33808. isInTheMiddle = true;
  33809. while (i && xData[i - 1] > x) {
  33810. i--;
  33811. }
  33812. }
  33813. // Insert undefined item
  33814. series.updateParallelArrays(point, 'splice', [i, 0, 0]);
  33815. // Update it
  33816. series.updateParallelArrays(point, i);
  33817. if (names && point.name) {
  33818. names[x] = point.name;
  33819. }
  33820. dataOptions.splice(i, 0, options);
  33821. if (isInTheMiddle ||
  33822. // When processedData is present we need to splice an empty slot
  33823. // into series.data, otherwise generatePoints won't pick it up.
  33824. series.processedData) {
  33825. series.data.splice(i, 0, null);
  33826. series.processData();
  33827. }
  33828. // Generate points to be added to the legend (#1329)
  33829. if (seriesOptions.legendType === 'point') {
  33830. series.generatePoints();
  33831. }
  33832. // Shift the first point off the parallel arrays
  33833. if (shift) {
  33834. if (data[0] && (data[0].remove)) {
  33835. data[0].remove(false);
  33836. }
  33837. else {
  33838. data.shift();
  33839. series.updateParallelArrays(point, 'shift');
  33840. dataOptions.shift();
  33841. }
  33842. }
  33843. // Fire event
  33844. if (withEvent !== false) {
  33845. fireEvent(series, 'addPoint', { point: point });
  33846. }
  33847. // redraw
  33848. series.isDirty = true;
  33849. series.isDirtyData = true;
  33850. if (redraw) {
  33851. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  33852. }
  33853. }
  33854. /**
  33855. * Remove a point from the series. Unlike the
  33856. * {@link Highcharts.Point#remove} method, this can also be done on a point
  33857. * that is not instanciated because it is outside the view or subject to
  33858. * Highcharts Stock data grouping.
  33859. *
  33860. * @sample highcharts/members/series-removepoint/
  33861. * Remove cropped point
  33862. *
  33863. * @function Highcharts.Series#removePoint
  33864. *
  33865. * @param {number} i
  33866. * The index of the point in the {@link Highcharts.Series.data|data}
  33867. * array.
  33868. *
  33869. * @param {boolean} [redraw=true]
  33870. * Whether to redraw the chart after the point is added. When
  33871. * removing more than one point, it is highly recommended that the
  33872. * `redraw` option be set to `false`, and instead {@link
  33873. * Highcharts.Chart#redraw} is explicitly called after the adding of
  33874. * points is finished.
  33875. *
  33876. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33877. * Whether and optionally how the series should be animated.
  33878. *
  33879. * @emits Highcharts.Point#event:remove
  33880. */
  33881. removePoint(i, redraw, animation) {
  33882. const series = this, data = series.data, point = data[i], points = series.points, chart = series.chart, remove = function () {
  33883. if (points && points.length === data.length) { // #4935
  33884. points.splice(i, 1);
  33885. }
  33886. data.splice(i, 1);
  33887. series.options.data.splice(i, 1);
  33888. series.updateParallelArrays(point || { series: series }, 'splice', [i, 1]);
  33889. if (point) {
  33890. point.destroy();
  33891. }
  33892. // redraw
  33893. series.isDirty = true;
  33894. series.isDirtyData = true;
  33895. if (redraw) {
  33896. chart.redraw();
  33897. }
  33898. };
  33899. setAnimation(animation, chart);
  33900. redraw = pick(redraw, true);
  33901. // Fire the event with a default handler of removing the point
  33902. if (point) {
  33903. point.firePointEvent('remove', null, remove);
  33904. }
  33905. else {
  33906. remove();
  33907. }
  33908. }
  33909. /**
  33910. * Remove a series and optionally redraw the chart.
  33911. *
  33912. * @sample highcharts/members/series-remove/
  33913. * Remove first series from a button
  33914. *
  33915. * @function Highcharts.Series#remove
  33916. *
  33917. * @param {boolean} [redraw=true]
  33918. * Whether to redraw the chart or wait for an explicit call to
  33919. * {@link Highcharts.Chart#redraw}.
  33920. *
  33921. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  33922. * Whether to apply animation, and optionally animation
  33923. * configuration.
  33924. *
  33925. * @param {boolean} [withEvent=true]
  33926. * Used internally, whether to fire the series `remove` event.
  33927. *
  33928. * @emits Highcharts.Series#event:remove
  33929. */
  33930. remove(redraw, animation, withEvent, keepEvents) {
  33931. const series = this, chart = series.chart;
  33932. /**
  33933. * @private
  33934. */
  33935. function remove() {
  33936. // Destroy elements
  33937. series.destroy(keepEvents);
  33938. // Redraw
  33939. chart.isDirtyLegend = chart.isDirtyBox = true;
  33940. chart.linkSeries(keepEvents);
  33941. if (pick(redraw, true)) {
  33942. chart.redraw(animation);
  33943. }
  33944. }
  33945. // Fire the event with a default handler of removing the point
  33946. if (withEvent !== false) {
  33947. fireEvent(series, 'remove', null, remove);
  33948. }
  33949. else {
  33950. remove();
  33951. }
  33952. }
  33953. /**
  33954. * Update the series with a new set of options. For a clean and precise
  33955. * handling of new options, all methods and elements from the series are
  33956. * removed, and it is initialized from scratch. Therefore, this method is
  33957. * more performance expensive than some other utility methods like {@link
  33958. * Series#setData} or {@link Series#setVisible}.
  33959. *
  33960. * Note that `Series.update` may mutate the passed `data` options.
  33961. *
  33962. * @sample highcharts/members/series-update/
  33963. * Updating series options
  33964. * @sample maps/members/series-update/
  33965. * Update series options in Highmaps
  33966. *
  33967. * @function Highcharts.Series#update
  33968. *
  33969. * @param {Highcharts.SeriesOptionsType} options
  33970. * New options that will be merged with the series' existing options.
  33971. *
  33972. * @param {boolean} [redraw=true]
  33973. * Whether to redraw the chart after the series is altered. If doing
  33974. * more operations on the chart, it is a good idea to set redraw to
  33975. * false and call {@link Chart#redraw} after.
  33976. *
  33977. * @emits Highcharts.Series#event:update
  33978. * @emits Highcharts.Series#event:afterUpdate
  33979. */
  33980. update(options, redraw) {
  33981. options = diffObjects(options, this.userOptions);
  33982. fireEvent(this, 'update', { options: options });
  33983. const series = this, chart = series.chart,
  33984. // must use user options when changing type because series.options
  33985. // is merged in with type specific plotOptions
  33986. oldOptions = series.userOptions, initialType = series.initialType || series.type, plotOptions = chart.options.plotOptions, initialSeriesProto = seriesTypes[initialType].prototype, groups = [
  33987. 'group',
  33988. 'markerGroup',
  33989. 'dataLabelsGroup',
  33990. 'transformGroup'
  33991. ],
  33992. // Animation must be enabled when calling update before the initial
  33993. // animation has first run. This happens when calling update
  33994. // directly after chart initialization, or when applying responsive
  33995. // rules (#6912).
  33996. animation = series.finishedAnimating && { animation: false }, kinds = {};
  33997. let seriesOptions, n, preserve = [
  33998. 'colorIndex',
  33999. 'eventOptions',
  34000. 'navigatorSeries',
  34001. 'symbolIndex',
  34002. 'baseSeries'
  34003. ], newType = (options.type ||
  34004. oldOptions.type ||
  34005. chart.options.chart.type);
  34006. const keepPoints = !(
  34007. // Indicators, histograms etc recalculate the data. It should be
  34008. // possible to omit this.
  34009. this.hasDerivedData ||
  34010. // New type requires new point classes
  34011. (newType && newType !== this.type) ||
  34012. // New options affecting how the data points are built
  34013. typeof options.pointStart !== 'undefined' ||
  34014. typeof options.pointInterval !== 'undefined' ||
  34015. typeof options.relativeXValue !== 'undefined' ||
  34016. options.joinBy ||
  34017. options.mapData || // #11636
  34018. // Changes to data grouping requires new points in new group
  34019. series.hasOptionChanged('dataGrouping') ||
  34020. series.hasOptionChanged('pointStart') ||
  34021. series.hasOptionChanged('pointInterval') ||
  34022. series.hasOptionChanged('pointIntervalUnit') ||
  34023. series.hasOptionChanged('keys'));
  34024. newType = newType || initialType;
  34025. if (keepPoints) {
  34026. preserve.push('data', 'isDirtyData', 'points', 'processedData', // #17057
  34027. 'processedXData', 'processedYData', 'xIncrement', 'cropped', '_hasPointMarkers', '_hasPointLabels', 'clips', // #15420
  34028. // Networkgraph (#14397)
  34029. 'nodes', 'layout',
  34030. // Treemap
  34031. 'level',
  34032. // Map specific, consider moving it to series-specific preserve-
  34033. // properties (#10617)
  34034. 'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
  34035. if (options.visible !== false) {
  34036. preserve.push('area', 'graph');
  34037. }
  34038. series.parallelArrays.forEach(function (key) {
  34039. preserve.push(key + 'Data');
  34040. });
  34041. if (options.data) {
  34042. // setData uses dataSorting options so we need to update them
  34043. // earlier
  34044. if (options.dataSorting) {
  34045. extend(series.options.dataSorting, options.dataSorting);
  34046. }
  34047. this.setData(options.data, false);
  34048. }
  34049. }
  34050. // Do the merge, with some forced options
  34051. options = merge(oldOptions, animation, {
  34052. // When oldOptions.index is null it should't be cleared.
  34053. // Otherwise navigator series will have wrong indexes (#10193).
  34054. index: typeof oldOptions.index === 'undefined' ?
  34055. series.index : oldOptions.index,
  34056. pointStart: pick(
  34057. // when updating from blank (#7933)
  34058. (plotOptions &&
  34059. plotOptions.series &&
  34060. plotOptions.series.pointStart), oldOptions.pointStart,
  34061. // when updating after addPoint
  34062. series.xData[0])
  34063. }, (!keepPoints && { data: series.options.data }), options);
  34064. // Merge does not merge arrays, but replaces them. Since points were
  34065. // updated, `series.options.data` has correct merged options, use it:
  34066. if (keepPoints && options.data) {
  34067. options.data = series.options.data;
  34068. }
  34069. // Make sure preserved properties are not destroyed (#3094)
  34070. preserve = groups.concat(preserve);
  34071. preserve.forEach(function (prop) {
  34072. preserve[prop] = series[prop];
  34073. delete series[prop];
  34074. });
  34075. let casting = false;
  34076. if (seriesTypes[newType]) {
  34077. casting = newType !== series.type;
  34078. // Destroy the series and delete all properties, it will be
  34079. // reinserted within the `init` call below
  34080. series.remove(false, false, false, true);
  34081. if (casting) {
  34082. // Modern browsers including IE11
  34083. if (Object.setPrototypeOf) {
  34084. Object.setPrototypeOf(series, seriesTypes[newType].prototype);
  34085. // Legacy (IE < 11)
  34086. }
  34087. else {
  34088. const ownEvents = Object.hasOwnProperty.call(series, 'hcEvents') && series.hcEvents;
  34089. for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
  34090. series[n] = void 0;
  34091. }
  34092. // Reinsert all methods and properties from the new type
  34093. // prototype (#2270, #3719).
  34094. extend(series, seriesTypes[newType].prototype);
  34095. // The events are tied to the prototype chain, don't copy if
  34096. // they're not the series' own
  34097. if (ownEvents) {
  34098. series.hcEvents = ownEvents;
  34099. }
  34100. else {
  34101. delete series.hcEvents;
  34102. }
  34103. }
  34104. }
  34105. }
  34106. else {
  34107. error(17, true, chart, { missingModuleFor: newType });
  34108. }
  34109. // Re-register groups (#3094) and other preserved properties
  34110. preserve.forEach(function (prop) {
  34111. series[prop] = preserve[prop];
  34112. });
  34113. series.init(chart, options);
  34114. // Remove particular elements of the points. Check `series.options`
  34115. // because we need to consider the options being set on plotOptions as
  34116. // well.
  34117. if (keepPoints && this.points) {
  34118. seriesOptions = series.options;
  34119. // What kind of elements to destroy
  34120. if (seriesOptions.visible === false) {
  34121. kinds.graphic = 1;
  34122. kinds.dataLabel = 1;
  34123. }
  34124. else if (!series._hasPointLabels) {
  34125. const { marker, dataLabels } = seriesOptions, oldMarker = oldOptions.marker || {};
  34126. // If the marker got disabled or changed its symbol, width or
  34127. // height - destroy
  34128. if (marker && (marker.enabled === false ||
  34129. oldMarker.symbol !== marker.symbol || // #10870, #15946
  34130. oldMarker.height !== marker.height || // #16274
  34131. oldMarker.width !== marker.width // #16274
  34132. )) {
  34133. kinds.graphic = 1;
  34134. }
  34135. if (dataLabels &&
  34136. dataLabels.enabled === false) {
  34137. kinds.dataLabel = 1;
  34138. }
  34139. }
  34140. for (const point of this.points) {
  34141. if (point && point.series) {
  34142. point.resolveColor();
  34143. // Destroy elements in order to recreate based on updated
  34144. // series options.
  34145. if (Object.keys(kinds).length) {
  34146. point.destroyElements(kinds);
  34147. }
  34148. if (seriesOptions.showInLegend === false &&
  34149. point.legendItem) {
  34150. chart.legend.destroyItem(point);
  34151. }
  34152. }
  34153. }
  34154. }
  34155. series.initialType = initialType;
  34156. chart.linkSeries(); // Links are lost in series.remove (#3028)
  34157. // #15383: Fire updatedData if the type has changed to keep linked
  34158. // series such as indicators updated
  34159. if (casting && series.linkedSeries.length) {
  34160. series.isDirtyData = true;
  34161. }
  34162. fireEvent(this, 'afterUpdate');
  34163. if (pick(redraw, true)) {
  34164. chart.redraw(keepPoints ? void 0 : false);
  34165. }
  34166. }
  34167. /**
  34168. * Used from within series.update
  34169. * @private
  34170. */
  34171. setName(name) {
  34172. this.name = this.options.name = this.userOptions.name = name;
  34173. this.chart.isDirtyLegend = true;
  34174. }
  34175. /**
  34176. * Check if the option has changed.
  34177. * @private
  34178. */
  34179. hasOptionChanged(optionName) {
  34180. const chart = this.chart, option = this.options[optionName], plotOptions = chart.options.plotOptions, oldOption = this.userOptions[optionName];
  34181. if (oldOption) {
  34182. return option !== oldOption;
  34183. }
  34184. return option !==
  34185. pick(plotOptions &&
  34186. plotOptions[this.type] &&
  34187. plotOptions[this.type][optionName], plotOptions &&
  34188. plotOptions.series &&
  34189. plotOptions.series[optionName], option);
  34190. }
  34191. /**
  34192. * Runs on mouse over the series graphical items.
  34193. *
  34194. * @function Highcharts.Series#onMouseOver
  34195. * @emits Highcharts.Series#event:mouseOver
  34196. */
  34197. onMouseOver() {
  34198. const series = this, chart = series.chart, hoverSeries = chart.hoverSeries, pointer = chart.pointer;
  34199. pointer.setHoverChartIndex();
  34200. // set normal state to previous series
  34201. if (hoverSeries && hoverSeries !== series) {
  34202. hoverSeries.onMouseOut();
  34203. }
  34204. // trigger the event, but to save processing time,
  34205. // only if defined
  34206. if (series.options.events.mouseOver) {
  34207. fireEvent(series, 'mouseOver');
  34208. }
  34209. // hover this
  34210. series.setState('hover');
  34211. /**
  34212. * Contains the original hovered series.
  34213. *
  34214. * @name Highcharts.Chart#hoverSeries
  34215. * @type {Highcharts.Series|null}
  34216. */
  34217. chart.hoverSeries = series;
  34218. }
  34219. /**
  34220. * Runs on mouse out of the series graphical items.
  34221. *
  34222. * @function Highcharts.Series#onMouseOut
  34223. *
  34224. * @emits Highcharts.Series#event:mouseOut
  34225. */
  34226. onMouseOut() {
  34227. // trigger the event only if listeners exist
  34228. const series = this, options = series.options, chart = series.chart, tooltip = chart.tooltip, hoverPoint = chart.hoverPoint;
  34229. // #182, set to null before the mouseOut event fires
  34230. chart.hoverSeries = null;
  34231. // trigger mouse out on the point, which must be in this series
  34232. if (hoverPoint) {
  34233. hoverPoint.onMouseOut();
  34234. }
  34235. // fire the mouse out event
  34236. if (series && options.events.mouseOut) {
  34237. fireEvent(series, 'mouseOut');
  34238. }
  34239. // hide the tooltip
  34240. if (tooltip &&
  34241. !series.stickyTracking &&
  34242. (!tooltip.shared || series.noSharedTooltip)) {
  34243. tooltip.hide();
  34244. }
  34245. // Reset all inactive states
  34246. chart.series.forEach(function (s) {
  34247. s.setState('', true);
  34248. });
  34249. }
  34250. /**
  34251. * Set the state of the series. Called internally on mouse interaction
  34252. * operations, but it can also be called directly to visually
  34253. * highlight a series.
  34254. *
  34255. * @function Highcharts.Series#setState
  34256. *
  34257. * @param {Highcharts.SeriesStateValue|""} [state]
  34258. * The new state, can be either `'hover'`, `'inactive'`, `'select'`,
  34259. * or `''` (an empty string), `'normal'` or `undefined` to set to
  34260. * normal state.
  34261. * @param {boolean} [inherit]
  34262. * Determines if state should be inherited by points too.
  34263. */
  34264. setState(state, inherit) {
  34265. const series = this, options = series.options, graph = series.graph, inactiveOtherPoints = options.inactiveOtherPoints, stateOptions = options.states,
  34266. // By default a quick animation to hover/inactive,
  34267. // slower to un-hover
  34268. stateAnimation = pick((stateOptions[state || 'normal'] &&
  34269. stateOptions[state || 'normal'].animation), series.chart.options.chart.animation);
  34270. let attribs, lineWidth = options.lineWidth, i = 0, opacity = options.opacity;
  34271. state = state || '';
  34272. if (series.state !== state) {
  34273. // Toggle class names
  34274. [
  34275. series.group,
  34276. series.markerGroup,
  34277. series.dataLabelsGroup
  34278. ].forEach(function (group) {
  34279. if (group) {
  34280. // Old state
  34281. if (series.state) {
  34282. group.removeClass('highcharts-series-' + series.state);
  34283. }
  34284. // New state
  34285. if (state) {
  34286. group.addClass('highcharts-series-' + state);
  34287. }
  34288. }
  34289. });
  34290. series.state = state;
  34291. if (!series.chart.styledMode) {
  34292. if (stateOptions[state] &&
  34293. stateOptions[state].enabled === false) {
  34294. return;
  34295. }
  34296. if (state) {
  34297. lineWidth = (stateOptions[state].lineWidth ||
  34298. lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
  34299. opacity = pick(stateOptions[state].opacity, opacity);
  34300. }
  34301. if (graph && !graph.dashstyle && isNumber(lineWidth)) {
  34302. attribs = {
  34303. 'stroke-width': lineWidth
  34304. };
  34305. // Animate the graph stroke-width.
  34306. graph.animate(attribs, stateAnimation);
  34307. while (series['zone-graph-' + i]) {
  34308. series['zone-graph-' + i].animate(attribs, stateAnimation);
  34309. i = i + 1;
  34310. }
  34311. }
  34312. // For some types (pie, networkgraph, sankey) opacity is
  34313. // resolved on a point level
  34314. if (!inactiveOtherPoints) {
  34315. [
  34316. series.group,
  34317. series.markerGroup,
  34318. series.dataLabelsGroup,
  34319. series.labelBySeries
  34320. ].forEach(function (group) {
  34321. if (group) {
  34322. group.animate({
  34323. opacity: opacity
  34324. }, stateAnimation);
  34325. }
  34326. });
  34327. }
  34328. }
  34329. }
  34330. // Don't loop over points on a series that doesn't apply inactive state
  34331. // to siblings markers (e.g. line, column)
  34332. if (inherit && inactiveOtherPoints && series.points) {
  34333. series.setAllPointsToState(state || void 0);
  34334. }
  34335. }
  34336. /**
  34337. * Set the state for all points in the series.
  34338. *
  34339. * @function Highcharts.Series#setAllPointsToState
  34340. *
  34341. * @private
  34342. *
  34343. * @param {string} [state]
  34344. * Can be either `hover` or undefined to set to normal state.
  34345. */
  34346. setAllPointsToState(state) {
  34347. this.points.forEach(function (point) {
  34348. if (point.setState) {
  34349. point.setState(state);
  34350. }
  34351. });
  34352. }
  34353. /**
  34354. * Show or hide the series.
  34355. *
  34356. * @function Highcharts.Series#setVisible
  34357. *
  34358. * @param {boolean} [visible]
  34359. * True to show the series, false to hide. If undefined, the visibility is
  34360. * toggled.
  34361. *
  34362. * @param {boolean} [redraw=true]
  34363. * Whether to redraw the chart after the series is altered. If doing more
  34364. * operations on the chart, it is a good idea to set redraw to false and
  34365. * call {@link Chart#redraw|chart.redraw()} after.
  34366. *
  34367. * @emits Highcharts.Series#event:hide
  34368. * @emits Highcharts.Series#event:show
  34369. */
  34370. setVisible(vis, redraw) {
  34371. const series = this, chart = series.chart, ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, oldVisibility = series.visible;
  34372. // if called without an argument, toggle visibility
  34373. series.visible =
  34374. vis =
  34375. series.options.visible =
  34376. series.userOptions.visible =
  34377. typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
  34378. const showOrHide = vis ? 'show' : 'hide';
  34379. // show or hide elements
  34380. [
  34381. 'group',
  34382. 'dataLabelsGroup',
  34383. 'markerGroup',
  34384. 'tracker',
  34385. 'tt'
  34386. ].forEach(function (key) {
  34387. if (series[key]) {
  34388. series[key][showOrHide]();
  34389. }
  34390. });
  34391. // hide tooltip (#1361)
  34392. if (chart.hoverSeries === series ||
  34393. (chart.hoverPoint && chart.hoverPoint.series) === series) {
  34394. series.onMouseOut();
  34395. }
  34396. if (series.legendItem) {
  34397. chart.legend.colorizeItem(series, vis);
  34398. }
  34399. // rescale or adapt to resized chart
  34400. series.isDirty = true;
  34401. // in a stack, all other series are affected
  34402. if (series.options.stacking) {
  34403. chart.series.forEach(function (otherSeries) {
  34404. if (otherSeries.options.stacking && otherSeries.visible) {
  34405. otherSeries.isDirty = true;
  34406. }
  34407. });
  34408. }
  34409. // show or hide linked series
  34410. series.linkedSeries.forEach(function (otherSeries) {
  34411. otherSeries.setVisible(vis, false);
  34412. });
  34413. if (ignoreHiddenSeries) {
  34414. chart.isDirtyBox = true;
  34415. }
  34416. fireEvent(series, showOrHide);
  34417. if (redraw !== false) {
  34418. chart.redraw();
  34419. }
  34420. }
  34421. /**
  34422. * Show the series if hidden.
  34423. *
  34424. * @sample highcharts/members/series-hide/
  34425. * Toggle visibility from a button
  34426. *
  34427. * @function Highcharts.Series#show
  34428. * @emits Highcharts.Series#event:show
  34429. */
  34430. show() {
  34431. this.setVisible(true);
  34432. }
  34433. /**
  34434. * Hide the series if visible. If the
  34435. * [chart.ignoreHiddenSeries](https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries)
  34436. * option is true, the chart is redrawn without this series.
  34437. *
  34438. * @sample highcharts/members/series-hide/
  34439. * Toggle visibility from a button
  34440. *
  34441. * @function Highcharts.Series#hide
  34442. * @emits Highcharts.Series#event:hide
  34443. */
  34444. hide() {
  34445. this.setVisible(false);
  34446. }
  34447. /**
  34448. * Select or unselect the series. This means its
  34449. * {@link Highcharts.Series.selected|selected}
  34450. * property is set, the checkbox in the legend is toggled and when selected,
  34451. * the series is returned by the {@link Highcharts.Chart#getSelectedSeries}
  34452. * function.
  34453. *
  34454. * @sample highcharts/members/series-select/
  34455. * Select a series from a button
  34456. *
  34457. * @function Highcharts.Series#select
  34458. *
  34459. * @param {boolean} [selected]
  34460. * True to select the series, false to unselect. If undefined, the selection
  34461. * state is toggled.
  34462. *
  34463. * @emits Highcharts.Series#event:select
  34464. * @emits Highcharts.Series#event:unselect
  34465. */
  34466. select(selected) {
  34467. const series = this;
  34468. series.selected =
  34469. selected =
  34470. this.options.selected = (typeof selected === 'undefined' ?
  34471. !series.selected :
  34472. selected);
  34473. if (series.checkbox) {
  34474. series.checkbox.checked = selected;
  34475. }
  34476. fireEvent(series, selected ? 'select' : 'unselect');
  34477. }
  34478. /**
  34479. * Checks if a tooltip should be shown for a given point.
  34480. *
  34481. * @private
  34482. */
  34483. shouldShowTooltip(plotX, plotY, options = {}) {
  34484. options.series = this;
  34485. options.visiblePlotOnly = true;
  34486. return this.chart.isInsidePlot(plotX, plotY, options);
  34487. }
  34488. /**
  34489. * Draws the legend symbol based on the legendSymbol user option.
  34490. *
  34491. * @private
  34492. */
  34493. drawLegendSymbol(legend, item) {
  34494. var _a;
  34495. (_a = LegendSymbol[this.options.legendSymbol || 'rectangle']) === null || _a === void 0 ? void 0 : _a.call(this, legend, item);
  34496. }
  34497. }
  34498. Series.defaultOptions = SeriesDefaults;
  34499. /**
  34500. * Registry of all available series types.
  34501. *
  34502. * @name Highcharts.Series.types
  34503. * @type {Highcharts.Dictionary<typeof_Highcharts.Series>}
  34504. */
  34505. Series.types = SeriesRegistry.seriesTypes;
  34506. /* *
  34507. *
  34508. * Static Functions
  34509. *
  34510. * */
  34511. /**
  34512. * Registers a series class to be accessible via `Series.types`.
  34513. *
  34514. * @function Highcharts.Series.registerType
  34515. *
  34516. * @param {string} seriesType
  34517. * The series type as an identifier string in lower case.
  34518. *
  34519. * @param {Function} SeriesClass
  34520. * The series class as a class pattern or a constructor function with
  34521. * prototype.
  34522. */
  34523. Series.registerType = SeriesRegistry.registerSeriesType;
  34524. extend(Series.prototype, {
  34525. axisTypes: ['xAxis', 'yAxis'],
  34526. coll: 'series',
  34527. colorCounter: 0,
  34528. cropShoulder: 1,
  34529. directTouch: false,
  34530. isCartesian: true,
  34531. kdAxisArray: ['clientX', 'plotY'],
  34532. // each point's x and y values are stored in this.xData and this.yData:
  34533. parallelArrays: ['x', 'y'],
  34534. pointClass: Point,
  34535. requireSorting: true,
  34536. // requires the data to be sorted:
  34537. sorted: true
  34538. });
  34539. /* *
  34540. *
  34541. * Registry
  34542. *
  34543. * */
  34544. SeriesRegistry.series = Series;
  34545. /* *
  34546. *
  34547. * Default Export
  34548. *
  34549. * */
  34550. /* *
  34551. *
  34552. * API Declarations
  34553. *
  34554. * */
  34555. /**
  34556. * This is a placeholder type of the possible series options for
  34557. * [Highcharts](../highcharts/series), [Highcharts Stock](../highstock/series),
  34558. * [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
  34559. *
  34560. * In TypeScript is this dynamically generated to reference all possible types
  34561. * of series options.
  34562. *
  34563. * @ignore-declaration
  34564. * @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
  34565. */
  34566. /**
  34567. * Options for `dataSorting`.
  34568. *
  34569. * @interface Highcharts.DataSortingOptionsObject
  34570. * @since 8.0.0
  34571. */ /**
  34572. * Enable or disable data sorting for the series.
  34573. * @name Highcharts.DataSortingOptionsObject#enabled
  34574. * @type {boolean|undefined}
  34575. */ /**
  34576. * Whether to allow matching points by name in an update.
  34577. * @name Highcharts.DataSortingOptionsObject#matchByName
  34578. * @type {boolean|undefined}
  34579. */ /**
  34580. * Determines what data value should be used to sort by.
  34581. * @name Highcharts.DataSortingOptionsObject#sortKey
  34582. * @type {string|undefined}
  34583. */
  34584. /**
  34585. * Function callback when a series has been animated.
  34586. *
  34587. * @callback Highcharts.SeriesAfterAnimateCallbackFunction
  34588. *
  34589. * @param {Highcharts.Series} this
  34590. * The series where the event occured.
  34591. *
  34592. * @param {Highcharts.SeriesAfterAnimateEventObject} event
  34593. * Event arguments.
  34594. */
  34595. /**
  34596. * Event information regarding completed animation of a series.
  34597. *
  34598. * @interface Highcharts.SeriesAfterAnimateEventObject
  34599. */ /**
  34600. * Animated series.
  34601. * @name Highcharts.SeriesAfterAnimateEventObject#target
  34602. * @type {Highcharts.Series}
  34603. */ /**
  34604. * Event type.
  34605. * @name Highcharts.SeriesAfterAnimateEventObject#type
  34606. * @type {"afterAnimate"}
  34607. */
  34608. /**
  34609. * Function callback when the checkbox next to the series' name in the legend is
  34610. * clicked.
  34611. *
  34612. * @callback Highcharts.SeriesCheckboxClickCallbackFunction
  34613. *
  34614. * @param {Highcharts.Series} this
  34615. * The series where the event occured.
  34616. *
  34617. * @param {Highcharts.SeriesCheckboxClickEventObject} event
  34618. * Event arguments.
  34619. */
  34620. /**
  34621. * Event information regarding check of a series box.
  34622. *
  34623. * @interface Highcharts.SeriesCheckboxClickEventObject
  34624. */ /**
  34625. * Whether the box has been checked.
  34626. * @name Highcharts.SeriesCheckboxClickEventObject#checked
  34627. * @type {boolean}
  34628. */ /**
  34629. * Related series.
  34630. * @name Highcharts.SeriesCheckboxClickEventObject#item
  34631. * @type {Highcharts.Series}
  34632. */ /**
  34633. * Related series.
  34634. * @name Highcharts.SeriesCheckboxClickEventObject#target
  34635. * @type {Highcharts.Series}
  34636. */ /**
  34637. * Event type.
  34638. * @name Highcharts.SeriesCheckboxClickEventObject#type
  34639. * @type {"checkboxClick"}
  34640. */
  34641. /**
  34642. * Function callback when a series is clicked. Return false to cancel toogle
  34643. * actions.
  34644. *
  34645. * @callback Highcharts.SeriesClickCallbackFunction
  34646. *
  34647. * @param {Highcharts.Series} this
  34648. * The series where the event occured.
  34649. *
  34650. * @param {Highcharts.SeriesClickEventObject} event
  34651. * Event arguments.
  34652. */
  34653. /**
  34654. * Common information for a click event on a series.
  34655. *
  34656. * @interface Highcharts.SeriesClickEventObject
  34657. * @extends global.Event
  34658. */ /**
  34659. * Nearest point on the graph.
  34660. * @name Highcharts.SeriesClickEventObject#point
  34661. * @type {Highcharts.Point}
  34662. */
  34663. /**
  34664. * Gets fired when the series is hidden after chart generation time, either by
  34665. * clicking the legend item or by calling `.hide()`.
  34666. *
  34667. * @callback Highcharts.SeriesHideCallbackFunction
  34668. *
  34669. * @param {Highcharts.Series} this
  34670. * The series where the event occured.
  34671. *
  34672. * @param {global.Event} event
  34673. * The event that occured.
  34674. */
  34675. /**
  34676. * The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
  34677. * graph.
  34678. *
  34679. * @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
  34680. */
  34681. /**
  34682. * Gets fired when the legend item belonging to the series is clicked. The
  34683. * default action is to toggle the visibility of the series. This can be
  34684. * prevented by returning `false` or calling `event.preventDefault()`.
  34685. *
  34686. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  34687. *
  34688. * @param {Highcharts.Series} this
  34689. * The series where the event occured.
  34690. *
  34691. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  34692. * The event that occured.
  34693. */
  34694. /**
  34695. * Information about the event.
  34696. *
  34697. * @interface Highcharts.SeriesLegendItemClickEventObject
  34698. */ /**
  34699. * Related browser event.
  34700. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  34701. * @type {global.PointerEvent}
  34702. */ /**
  34703. * Prevent the default action of toggle the visibility of the series.
  34704. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  34705. * @type {Function}
  34706. */ /**
  34707. * Related series.
  34708. * @name Highcharts.SeriesCheckboxClickEventObject#target
  34709. * @type {Highcharts.Series}
  34710. */ /**
  34711. * Event type.
  34712. * @name Highcharts.SeriesCheckboxClickEventObject#type
  34713. * @type {"checkboxClick"}
  34714. */
  34715. /**
  34716. * Gets fired when the mouse leaves the graph.
  34717. *
  34718. * @callback Highcharts.SeriesMouseOutCallbackFunction
  34719. *
  34720. * @param {Highcharts.Series} this
  34721. * Series where the event occured.
  34722. *
  34723. * @param {global.PointerEvent} event
  34724. * Event that occured.
  34725. */
  34726. /**
  34727. * Gets fired when the mouse enters the graph.
  34728. *
  34729. * @callback Highcharts.SeriesMouseOverCallbackFunction
  34730. *
  34731. * @param {Highcharts.Series} this
  34732. * Series where the event occured.
  34733. *
  34734. * @param {global.PointerEvent} event
  34735. * Event that occured.
  34736. */
  34737. /**
  34738. * Translation and scale for the plot area of a series.
  34739. *
  34740. * @interface Highcharts.SeriesPlotBoxObject
  34741. */ /**
  34742. * @name Highcharts.SeriesPlotBoxObject#scaleX
  34743. * @type {number}
  34744. */ /**
  34745. * @name Highcharts.SeriesPlotBoxObject#scaleY
  34746. * @type {number}
  34747. */ /**
  34748. * @name Highcharts.SeriesPlotBoxObject#translateX
  34749. * @type {number}
  34750. */ /**
  34751. * @name Highcharts.SeriesPlotBoxObject#translateY
  34752. * @type {number}
  34753. */
  34754. /**
  34755. * Gets fired when the series is shown after chart generation time, either by
  34756. * clicking the legend item or by calling `.show()`.
  34757. *
  34758. * @callback Highcharts.SeriesShowCallbackFunction
  34759. *
  34760. * @param {Highcharts.Series} this
  34761. * Series where the event occured.
  34762. *
  34763. * @param {global.Event} event
  34764. * Event that occured.
  34765. */
  34766. /**
  34767. * Possible key values for the series state options.
  34768. *
  34769. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
  34770. */
  34771. ''; // detach doclets above
  34772. /* *
  34773. *
  34774. * API Options
  34775. *
  34776. * */
  34777. /**
  34778. * Series options for specific data and the data itself. In TypeScript you
  34779. * have to cast the series options to specific series types, to get all
  34780. * possible options for a series.
  34781. *
  34782. * @example
  34783. * // TypeScript example
  34784. * Highcharts.chart('container', {
  34785. * series: [{
  34786. * color: '#06C',
  34787. * data: [[0, 1], [2, 3]]
  34788. * } as Highcharts.SeriesLineOptions ]
  34789. * });
  34790. *
  34791. * @type {Array<*>}
  34792. * @apioption series
  34793. */
  34794. /**
  34795. * An id for the series. This can be used after render time to get a pointer
  34796. * to the series object through `chart.get()`.
  34797. *
  34798. * @sample {highcharts} highcharts/plotoptions/series-id/
  34799. * Get series by id
  34800. *
  34801. * @type {string}
  34802. * @since 1.2.0
  34803. * @apioption series.id
  34804. */
  34805. /**
  34806. * The index of the series in the chart, affecting the internal index in the
  34807. * `chart.series` array, the visible Z index as well as the order in the
  34808. * legend.
  34809. *
  34810. * @type {number}
  34811. * @since 2.3.0
  34812. * @apioption series.index
  34813. */
  34814. /**
  34815. * The sequential index of the series in the legend.
  34816. *
  34817. * @see [legend.reversed](#legend.reversed),
  34818. * [yAxis.reversedStacks](#yAxis.reversedStacks)
  34819. *
  34820. * @sample {highcharts|highstock} highcharts/series/legendindex/
  34821. * Legend in opposite order
  34822. *
  34823. * @type {number}
  34824. * @apioption series.legendIndex
  34825. */
  34826. /**
  34827. * The name of the series as shown in the legend, tooltip etc.
  34828. *
  34829. * @sample {highcharts} highcharts/series/name/
  34830. * Series name
  34831. * @sample {highmaps} maps/demo/category-map/
  34832. * Series name
  34833. *
  34834. * @type {string}
  34835. * @apioption series.name
  34836. */
  34837. /**
  34838. * This option allows grouping series in a stacked chart. The stack option
  34839. * can be a string or anything else, as long as the grouped series' stack
  34840. * options match each other after conversion into a string.
  34841. *
  34842. * @sample {highcharts} highcharts/series/stack/
  34843. * Stacked and grouped columns
  34844. *
  34845. * @type {number|string}
  34846. * @since 2.1
  34847. * @product highcharts highstock
  34848. * @apioption series.stack
  34849. */
  34850. /**
  34851. * The type of series, for example `line` or `column`. By default, the
  34852. * series type is inherited from [chart.type](#chart.type), so unless the
  34853. * chart is a combination of series types, there is no need to set it on the
  34854. * series level.
  34855. *
  34856. * @sample {highcharts} highcharts/series/type/
  34857. * Line and column in the same chart
  34858. * @sample highcharts/series/type-dynamic/
  34859. * Dynamic types with button selector
  34860. * @sample {highmaps} maps/demo/mapline-mappoint/
  34861. * Multiple types in the same map
  34862. *
  34863. * @type {string}
  34864. * @apioption series.type
  34865. */
  34866. /**
  34867. * When using dual or multiple x axes, this number defines which xAxis the
  34868. * particular series is connected to. It refers to either the
  34869. * {@link #xAxis.id|axis id}
  34870. * or the index of the axis in the xAxis array, with 0 being the first.
  34871. *
  34872. * @type {number|string}
  34873. * @default 0
  34874. * @product highcharts highstock
  34875. * @apioption series.xAxis
  34876. */
  34877. /**
  34878. * When using dual or multiple y axes, this number defines which yAxis the
  34879. * particular series is connected to. It refers to either the
  34880. * {@link #yAxis.id|axis id}
  34881. * or the index of the axis in the yAxis array, with 0 being the first.
  34882. *
  34883. * @sample {highcharts} highcharts/series/yaxis/
  34884. * Apply the column series to the secondary Y axis
  34885. *
  34886. * @type {number|string}
  34887. * @default 0
  34888. * @product highcharts highstock
  34889. * @apioption series.yAxis
  34890. */
  34891. /**
  34892. * Define the visual z index of the series.
  34893. *
  34894. * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
  34895. * With no z index, the series defined last are on top
  34896. * @sample {highcharts} highcharts/plotoptions/series-zindex/
  34897. * With a z index, the series with the highest z index is on top
  34898. * @sample {highstock} highcharts/plotoptions/series-zindex-default/
  34899. * With no z index, the series defined last are on top
  34900. * @sample {highstock} highcharts/plotoptions/series-zindex/
  34901. * With a z index, the series with the highest z index is on top
  34902. *
  34903. * @type {number}
  34904. * @product highcharts highstock
  34905. * @apioption series.zIndex
  34906. */
  34907. ''; // include precedent doclets in transpilat
  34908. return Series;
  34909. });
  34910. _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) {
  34911. /* *
  34912. *
  34913. * (c) 2010-2021 Torstein Honsi
  34914. *
  34915. * License: www.highcharts.com/license
  34916. *
  34917. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34918. *
  34919. * */
  34920. const { animate, animObject, setAnimation } = A;
  34921. const { defaultOptions, defaultTime } = D;
  34922. const { numberFormat } = Templating;
  34923. const { registerEventOptions } = Foundation;
  34924. const { charts, doc, marginNames, svg, win } = H;
  34925. const { seriesTypes } = SeriesRegistry;
  34926. 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;
  34927. /* *
  34928. *
  34929. * Class
  34930. *
  34931. * */
  34932. /* eslint-disable no-invalid-this, valid-jsdoc */
  34933. /**
  34934. * The Chart class. The recommended constructor is {@link Highcharts#chart}.
  34935. *
  34936. * @example
  34937. * let chart = Highcharts.chart('container', {
  34938. * title: {
  34939. * text: 'My chart'
  34940. * },
  34941. * series: [{
  34942. * data: [1, 3, 2, 4]
  34943. * }]
  34944. * })
  34945. *
  34946. * @class
  34947. * @name Highcharts.Chart
  34948. *
  34949. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  34950. * The DOM element to render to, or its id.
  34951. *
  34952. * @param {Highcharts.Options} options
  34953. * The chart options structure.
  34954. *
  34955. * @param {Highcharts.ChartCallbackFunction} [callback]
  34956. * Function to run when the chart has loaded and and all external images
  34957. * are loaded. Defining a
  34958. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  34959. * handler is equivalent.
  34960. */
  34961. class Chart {
  34962. /**
  34963. * Factory function for basic charts.
  34964. *
  34965. * @example
  34966. * // Render a chart in to div#container
  34967. * let chart = Highcharts.chart('container', {
  34968. * title: {
  34969. * text: 'My chart'
  34970. * },
  34971. * series: [{
  34972. * data: [1, 3, 2, 4]
  34973. * }]
  34974. * });
  34975. *
  34976. * @function Highcharts.chart
  34977. *
  34978. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  34979. * The DOM element to render to, or its id.
  34980. *
  34981. * @param {Highcharts.Options} options
  34982. * The chart options structure.
  34983. *
  34984. * @param {Highcharts.ChartCallbackFunction} [callback]
  34985. * Function to run when the chart has loaded and and all external images are
  34986. * loaded. Defining a
  34987. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  34988. * handler is equivalent.
  34989. *
  34990. * @return {Highcharts.Chart}
  34991. * Returns the Chart object.
  34992. */
  34993. static chart(a, b, c) {
  34994. return new Chart(a, b, c);
  34995. }
  34996. constructor(a, b, c) {
  34997. this.axes = void 0;
  34998. this.axisOffset = void 0;
  34999. this.zoomBool = void 0;
  35000. this.bounds = void 0;
  35001. this.chartHeight = void 0;
  35002. this.chartWidth = void 0;
  35003. this.clipBox = void 0;
  35004. this.colorCounter = void 0;
  35005. this.container = void 0;
  35006. this.eventOptions = void 0;
  35007. this.index = void 0;
  35008. this.isResizing = void 0;
  35009. this.labelCollectors = void 0;
  35010. this.margin = void 0;
  35011. this.numberFormatter = void 0;
  35012. this.options = void 0;
  35013. this.plotBox = void 0;
  35014. this.plotHeight = void 0;
  35015. this.plotLeft = void 0;
  35016. this.plotTop = void 0;
  35017. this.plotWidth = void 0;
  35018. this.pointCount = void 0;
  35019. this.pointer = void 0;
  35020. this.renderer = void 0;
  35021. this.renderTo = void 0;
  35022. this.series = void 0;
  35023. this.sharedClips = {};
  35024. this.spacing = void 0;
  35025. this.spacingBox = void 0;
  35026. this.symbolCounter = void 0;
  35027. this.time = void 0;
  35028. this.titleOffset = void 0;
  35029. this.userOptions = void 0;
  35030. this.xAxis = void 0;
  35031. this.yAxis = void 0;
  35032. this.zooming = void 0;
  35033. this.getArgs(a, b, c);
  35034. }
  35035. /* *
  35036. *
  35037. * Functions
  35038. *
  35039. * */
  35040. /**
  35041. * Handle the arguments passed to the constructor.
  35042. *
  35043. * @private
  35044. * @function Highcharts.Chart#getArgs
  35045. *
  35046. * @param {...Array<*>} arguments
  35047. * All arguments for the constructor.
  35048. *
  35049. * @emits Highcharts.Chart#event:init
  35050. * @emits Highcharts.Chart#event:afterInit
  35051. */
  35052. getArgs(a, b, c) {
  35053. // Remove the optional first argument, renderTo, and
  35054. // set it on this.
  35055. if (isString(a) || a.nodeName) {
  35056. this.renderTo = a;
  35057. this.init(b, c);
  35058. }
  35059. else {
  35060. this.init(a, b);
  35061. }
  35062. }
  35063. /*
  35064. 是否可选择
  35065. */
  35066. // setIsSelect(x){
  35067. // if(x){
  35068. // const chart = this, options = chart.options.chart, zooming = options.zooming;
  35069. // 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) });
  35070. // }else {
  35071. // const chart = this, options = chart.options.chart, zooming = options.zooming;
  35072. // 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) });
  35073. // }
  35074. // }
  35075. /**
  35076. * Function setting zoom options after chart init and after chart update.
  35077. * Offers support for deprecated options.
  35078. *
  35079. * @private
  35080. * @function Highcharts.Chart#setZoomOptions
  35081. */
  35082. setZoomOptions() {
  35083. console.log("setZoomOptions")
  35084. const chart = this, options = chart.options.chart, zooming = options.zooming;
  35085. 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) });
  35086. }
  35087. /**
  35088. * Overridable function that initializes the chart. The constructor's
  35089. * arguments are passed on directly.
  35090. *
  35091. * @function Highcharts.Chart#init
  35092. *
  35093. * @param {Highcharts.Options} userOptions
  35094. * Custom options.
  35095. *
  35096. * @param {Function} [callback]
  35097. * Function to run when the chart has loaded and and all external
  35098. * images are loaded.
  35099. *
  35100. *
  35101. * @emits Highcharts.Chart#event:init
  35102. * @emits Highcharts.Chart#event:afterInit
  35103. */
  35104. init(userOptions, callback) {
  35105. // Fire the event with a default function
  35106. fireEvent(this, 'init', { args: arguments }, function () {
  35107. const options = merge(defaultOptions, userOptions), // do the merge
  35108. optionsChart = options.chart;
  35109. /**
  35110. * The original options given to the constructor or a chart factory
  35111. * like {@link Highcharts.chart} and {@link Highcharts.stockChart}.
  35112. * The original options are shallow copied to avoid mutation. The
  35113. * copy, `chart.userOptions`, may later be mutated to reflect
  35114. * updated options throughout the lifetime of the chart.
  35115. *
  35116. * For collections, like `series`, `xAxis` and `yAxis`, the chart
  35117. * user options should always be reflected by the item user option,
  35118. * so for example the following should always be true:
  35119. *
  35120. * `chart.xAxis[0].userOptions === chart.userOptions.xAxis[0]`
  35121. *
  35122. * @name Highcharts.Chart#userOptions
  35123. * @type {Highcharts.Options}
  35124. */
  35125. this.userOptions = extend({}, userOptions);
  35126. this.margin = [];
  35127. this.spacing = [];
  35128. // Pixel data bounds for touch zoom
  35129. this.bounds = { h: {}, v: {} };
  35130. // An array of functions that returns labels that should be
  35131. // considered for anti-collision
  35132. this.labelCollectors = [];
  35133. this.callback = callback;
  35134. this.isResizing = 0;
  35135. /**
  35136. * The options structure for the chart after merging
  35137. * {@link #defaultOptions} and {@link #userOptions}. It contains
  35138. * members for the sub elements like series, legend, tooltip etc.
  35139. *
  35140. * @name Highcharts.Chart#options
  35141. * @type {Highcharts.Options}
  35142. */
  35143. this.options = options;
  35144. /**
  35145. * All the axes in the chart.
  35146. *
  35147. * @see Highcharts.Chart.xAxis
  35148. * @see Highcharts.Chart.yAxis
  35149. *
  35150. * @name Highcharts.Chart#axes
  35151. * @type {Array<Highcharts.Axis>}
  35152. */
  35153. this.axes = [];
  35154. /**
  35155. * All the current series in the chart.
  35156. *
  35157. * @name Highcharts.Chart#series
  35158. * @type {Array<Highcharts.Series>}
  35159. */
  35160. this.series = [];
  35161. /**
  35162. * The `Time` object associated with the chart. Since v6.0.5,
  35163. * time settings can be applied individually for each chart. If
  35164. * no individual settings apply, the `Time` object is shared by
  35165. * all instances.
  35166. *
  35167. * @name Highcharts.Chart#time
  35168. * @type {Highcharts.Time}
  35169. */
  35170. this.time =
  35171. userOptions.time && Object.keys(userOptions.time).length ?
  35172. new Time(userOptions.time) :
  35173. H.time;
  35174. /**
  35175. * Callback function to override the default function that formats
  35176. * all the numbers in the chart. Returns a string with the formatted
  35177. * number.
  35178. *
  35179. * @name Highcharts.Chart#numberFormatter
  35180. * @type {Highcharts.NumberFormatterCallbackFunction}
  35181. */
  35182. this.numberFormatter = optionsChart.numberFormatter || numberFormat;
  35183. /**
  35184. * Whether the chart is in styled mode, meaning all presentational
  35185. * attributes are avoided.
  35186. *
  35187. * @name Highcharts.Chart#styledMode
  35188. * @type {boolean}
  35189. */
  35190. this.styledMode = optionsChart.styledMode;
  35191. this.hasCartesianSeries = optionsChart.showAxes;
  35192. const chart = this;
  35193. /**
  35194. * Index position of the chart in the {@link Highcharts#charts}
  35195. * property.
  35196. *
  35197. * @name Highcharts.Chart#index
  35198. * @type {number}
  35199. * @readonly
  35200. */
  35201. chart.index = charts.length; // Add the chart to the global lookup
  35202. charts.push(chart);
  35203. H.chartCount++;
  35204. // Chart event handlers
  35205. registerEventOptions(this, optionsChart);
  35206. /**
  35207. * A collection of the X axes in the chart.
  35208. *
  35209. * @name Highcharts.Chart#xAxis
  35210. * @type {Array<Highcharts.Axis>}
  35211. */
  35212. chart.xAxis = [];
  35213. /**
  35214. * A collection of the Y axes in the chart.
  35215. *
  35216. * @name Highcharts.Chart#yAxis
  35217. * @type {Array<Highcharts.Axis>}
  35218. *
  35219. * @todo
  35220. * Make events official: Fire the event `afterInit`.
  35221. */
  35222. chart.yAxis = [];
  35223. chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
  35224. this.setZoomOptions();
  35225. // Fire after init but before first render, before axes and series
  35226. // have been initialized.
  35227. fireEvent(chart, 'afterInit');
  35228. chart.firstRender();
  35229. });
  35230. }
  35231. /**
  35232. * Internal function to unitialize an individual series.
  35233. *
  35234. * @private
  35235. * @function Highcharts.Chart#initSeries
  35236. */
  35237. initSeries(options) {
  35238. const chart = this, optionsChart = chart.options.chart, type = (options.type ||
  35239. optionsChart.type), SeriesClass = seriesTypes[type];
  35240. // No such series type
  35241. if (!SeriesClass) {
  35242. error(17, true, chart, { missingModuleFor: type });
  35243. }
  35244. const series = new SeriesClass();
  35245. if (typeof series.init === 'function') {
  35246. series.init(chart, options);
  35247. }
  35248. return series;
  35249. }
  35250. /**
  35251. * Internal function to set data for all series with enabled sorting.
  35252. *
  35253. * @private
  35254. * @function Highcharts.Chart#setSeriesData
  35255. */
  35256. setSeriesData() {
  35257. this.getSeriesOrderByLinks().forEach(function (series) {
  35258. // We need to set data for series with sorting after series init
  35259. if (!series.points && !series.data && series.enabledDataSorting) {
  35260. series.setData(series.options.data, false);
  35261. }
  35262. });
  35263. }
  35264. /**
  35265. * Sort and return chart series in order depending on the number of linked
  35266. * series.
  35267. *
  35268. * @private
  35269. * @function Highcharts.Series#getSeriesOrderByLinks
  35270. */
  35271. getSeriesOrderByLinks() {
  35272. return this.series.concat().sort(function (a, b) {
  35273. if (a.linkedSeries.length || b.linkedSeries.length) {
  35274. return b.linkedSeries.length - a.linkedSeries.length;
  35275. }
  35276. return 0;
  35277. });
  35278. }
  35279. /**
  35280. * Order all series or axes above a given index. When series or axes are
  35281. * added and ordered by configuration, only the last series is handled
  35282. * (#248, #1123, #2456, #6112). This function is called on series and axis
  35283. * initialization and destroy.
  35284. *
  35285. * @private
  35286. * @function Highcharts.Chart#orderItems
  35287. * @param {string} coll The collection name
  35288. * @param {number} [fromIndex=0]
  35289. * If this is given, only the series above this index are handled.
  35290. */
  35291. orderItems(coll, fromIndex = 0) {
  35292. const collection = this[coll],
  35293. // Item options should be reflected in chart.options.series,
  35294. // chart.options.yAxis etc
  35295. optionsArray = this.options[coll] = splat(this.options[coll])
  35296. .slice(), userOptionsArray = this.userOptions[coll] = this.userOptions[coll] ?
  35297. splat(this.userOptions[coll]).slice() :
  35298. [];
  35299. if (this.hasRendered) {
  35300. // Remove all above index
  35301. optionsArray.splice(fromIndex);
  35302. userOptionsArray.splice(fromIndex);
  35303. }
  35304. if (collection) {
  35305. for (let i = fromIndex, iEnd = collection.length; i < iEnd; ++i) {
  35306. const item = collection[i];
  35307. if (item) {
  35308. /**
  35309. * Contains the series' index in the `Chart.series` array.
  35310. *
  35311. * @name Highcharts.Series#index
  35312. * @type {number}
  35313. * @readonly
  35314. */
  35315. item.index = i;
  35316. if (item instanceof Series) {
  35317. item.name = item.getName();
  35318. }
  35319. if (!item.options.isInternal) {
  35320. optionsArray[i] = item.options;
  35321. userOptionsArray[i] = item.userOptions;
  35322. }
  35323. }
  35324. }
  35325. }
  35326. }
  35327. /**
  35328. * Check whether a given point is within the plot area.
  35329. *
  35330. * @function Highcharts.Chart#isInsidePlot
  35331. *
  35332. * @param {number} plotX
  35333. * Pixel x relative to the plot area.
  35334. *
  35335. * @param {number} plotY
  35336. * Pixel y relative to the plot area.
  35337. *
  35338. * @param {Highcharts.ChartIsInsideOptionsObject} [options]
  35339. * Options object.
  35340. *
  35341. * @return {boolean}
  35342. * Returns true if the given point is inside the plot area.
  35343. */
  35344. isInsidePlot(plotX, plotY, options = {}) {
  35345. const { inverted, plotBox, plotLeft, plotTop, scrollablePlotBox } = this;
  35346. let scrollLeft = 0, scrollTop = 0;
  35347. if (options.visiblePlotOnly && this.scrollingContainer) {
  35348. ({ scrollLeft, scrollTop } = this.scrollingContainer);
  35349. }
  35350. const series = options.series, box = (options.visiblePlotOnly && scrollablePlotBox) || plotBox, x = options.inverted ? plotY : plotX, y = options.inverted ? plotX : plotY, e = {
  35351. x,
  35352. y,
  35353. isInsidePlot: true,
  35354. options
  35355. };
  35356. if (!options.ignoreX) {
  35357. const xAxis = (series &&
  35358. (inverted && !this.polar ? series.yAxis : series.xAxis)) || {
  35359. pos: plotLeft,
  35360. len: Infinity
  35361. };
  35362. const chartX = options.paneCoordinates ?
  35363. xAxis.pos + x : plotLeft + x;
  35364. if (!(chartX >= Math.max(scrollLeft + plotLeft, xAxis.pos) &&
  35365. chartX <= Math.min(scrollLeft + plotLeft + box.width, xAxis.pos + xAxis.len))) {
  35366. e.isInsidePlot = false;
  35367. }
  35368. }
  35369. if (!options.ignoreY && e.isInsidePlot) {
  35370. const yAxis = (!inverted && options.axis &&
  35371. !options.axis.isXAxis && options.axis) || (series && (inverted ? series.xAxis : series.yAxis)) || {
  35372. pos: plotTop,
  35373. len: Infinity
  35374. };
  35375. const chartY = options.paneCoordinates ?
  35376. yAxis.pos + y : plotTop + y;
  35377. if (!(chartY >= Math.max(scrollTop + plotTop, yAxis.pos) &&
  35378. chartY <= Math.min(scrollTop + plotTop + box.height, yAxis.pos + yAxis.len))) {
  35379. e.isInsidePlot = false;
  35380. }
  35381. }
  35382. fireEvent(this, 'afterIsInsidePlot', e);
  35383. return e.isInsidePlot;
  35384. }
  35385. /**
  35386. * Redraw the chart after changes have been done to the data, axis extremes
  35387. * chart size or chart elements. All methods for updating axes, series or
  35388. * points have a parameter for redrawing the chart. This is `true` by
  35389. * default. But in many cases you want to do more than one operation on the
  35390. * chart before redrawing, for example add a number of points. In those
  35391. * cases it is a waste of resources to redraw the chart for each new point
  35392. * added. So you add the points and call `chart.redraw()` after.
  35393. *
  35394. * @function Highcharts.Chart#redraw
  35395. *
  35396. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  35397. * If or how to apply animation to the redraw. When `undefined`, it applies
  35398. * the animation that is set in the `chart.animation` option.
  35399. *
  35400. * @emits Highcharts.Chart#event:afterSetExtremes
  35401. * @emits Highcharts.Chart#event:beforeRedraw
  35402. * @emits Highcharts.Chart#event:predraw
  35403. * @emits Highcharts.Chart#event:redraw
  35404. * @emits Highcharts.Chart#event:render
  35405. * @emits Highcharts.Chart#event:updatedData
  35406. */
  35407. redraw(animation) {
  35408. fireEvent(this, 'beforeRedraw');
  35409. 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 = [];
  35410. let hasDirtyStacks, hasStackedSeries, i, isDirtyBox = chart.isDirtyBox, redrawLegend = chart.isDirtyLegend, serie;
  35411. renderer.rootFontSize = renderer.boxWrapper.getStyle('font-size');
  35412. // Handle responsive rules, not only on resize (#6130)
  35413. if (chart.setResponsive) {
  35414. chart.setResponsive(false);
  35415. }
  35416. // Set the global animation. When chart.hasRendered is not true, the
  35417. // redraw call comes from a responsive rule and animation should not
  35418. // occur.
  35419. setAnimation(chart.hasRendered ? animation : false, chart);
  35420. if (isHiddenChart) {
  35421. chart.temporaryDisplay();
  35422. }
  35423. // Adjust title layout (reflow multiline text)
  35424. chart.layOutTitles(false);
  35425. // link stacked series
  35426. i = series.length;
  35427. while (i--) {
  35428. serie = series[i];
  35429. if (serie.options.stacking || serie.options.centerInCategory) {
  35430. hasStackedSeries = true;
  35431. if (serie.isDirty) {
  35432. hasDirtyStacks = true;
  35433. break;
  35434. }
  35435. }
  35436. }
  35437. if (hasDirtyStacks) { // mark others as dirty
  35438. i = series.length;
  35439. while (i--) {
  35440. serie = series[i];
  35441. if (serie.options.stacking) {
  35442. serie.isDirty = true;
  35443. }
  35444. }
  35445. }
  35446. // Handle updated data in the series
  35447. series.forEach(function (serie) {
  35448. if (serie.isDirty) {
  35449. if (serie.options.legendType === 'point') {
  35450. if (typeof serie.updateTotals === 'function') {
  35451. serie.updateTotals();
  35452. }
  35453. redrawLegend = true;
  35454. }
  35455. else if (legendUserOptions &&
  35456. (legendUserOptions.labelFormatter ||
  35457. legendUserOptions.labelFormat)) {
  35458. redrawLegend = true; // #2165
  35459. }
  35460. }
  35461. if (serie.isDirtyData) {
  35462. fireEvent(serie, 'updatedData');
  35463. }
  35464. });
  35465. // handle added or removed series
  35466. if (redrawLegend && legend && legend.options.enabled) {
  35467. // draw legend graphics
  35468. legend.render();
  35469. chart.isDirtyLegend = false;
  35470. }
  35471. // reset stacks
  35472. if (hasStackedSeries) {
  35473. chart.getStacks();
  35474. }
  35475. // set axes scales
  35476. axes.forEach(function (axis) {
  35477. axis.updateNames();
  35478. axis.setScale();
  35479. });
  35480. chart.getMargins(); // #3098
  35481. // If one axis is dirty, all axes must be redrawn (#792, #2169)
  35482. axes.forEach(function (axis) {
  35483. if (axis.isDirty) {
  35484. isDirtyBox = true;
  35485. }
  35486. });
  35487. // redraw axes
  35488. axes.forEach(function (axis) {
  35489. // Fire 'afterSetExtremes' only if extremes are set
  35490. const key = axis.min + ',' + axis.max;
  35491. if (axis.extKey !== key) { // #821, #4452
  35492. axis.extKey = key;
  35493. // prevent a recursive call to chart.redraw() (#1119)
  35494. afterRedraw.push(function () {
  35495. fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751
  35496. delete axis.eventArgs;
  35497. });
  35498. }
  35499. if (isDirtyBox || hasStackedSeries) {
  35500. axis.redraw();
  35501. }
  35502. });
  35503. // the plot areas size has changed
  35504. if (isDirtyBox) {
  35505. chart.drawChartBox();
  35506. }
  35507. // Fire an event before redrawing series, used by the boost module to
  35508. // clear previous series renderings.
  35509. fireEvent(chart, 'predraw');
  35510. // redraw affected series
  35511. series.forEach(function (serie) {
  35512. if ((isDirtyBox || serie.isDirty) && serie.visible) {
  35513. serie.redraw();
  35514. }
  35515. // Set it here, otherwise we will have unlimited 'updatedData' calls
  35516. // for a hidden series after setData(). Fixes #6012
  35517. serie.isDirtyData = false;
  35518. });
  35519. // move tooltip or reset
  35520. if (pointer) {
  35521. pointer.reset(true);
  35522. }
  35523. // redraw if canvas
  35524. renderer.draw();
  35525. // Fire the events
  35526. fireEvent(chart, 'redraw');
  35527. fireEvent(chart, 'render');
  35528. if (isHiddenChart) {
  35529. chart.temporaryDisplay(true);
  35530. }
  35531. // Fire callbacks that are put on hold until after the redraw
  35532. afterRedraw.forEach(function (callback) {
  35533. callback.call();
  35534. });
  35535. }
  35536. /**
  35537. * Get an axis, series or point object by `id` as given in the configuration
  35538. * options. Returns `undefined` if no item is found.
  35539. *
  35540. * @sample highcharts/plotoptions/series-id/
  35541. * Get series by id
  35542. *
  35543. * @function Highcharts.Chart#get
  35544. *
  35545. * @param {string} id
  35546. * The id as given in the configuration options.
  35547. *
  35548. * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
  35549. * The retrieved item.
  35550. */
  35551. get(id) {
  35552. const series = this.series;
  35553. /**
  35554. * @private
  35555. */
  35556. function itemById(item) {
  35557. return (item.id === id ||
  35558. (item.options && item.options.id === id));
  35559. }
  35560. let ret =
  35561. // Search axes
  35562. find(this.axes, itemById) ||
  35563. // Search series
  35564. find(this.series, itemById);
  35565. // Search points
  35566. for (let i = 0; !ret && i < series.length; i++) {
  35567. ret = find(series[i].points || [], itemById);
  35568. }
  35569. return ret;
  35570. }
  35571. /**
  35572. * Create the Axis instances based on the config options.
  35573. *
  35574. * @private
  35575. * @function Highcharts.Chart#getAxes
  35576. * @emits Highcharts.Chart#event:afterGetAxes
  35577. * @emits Highcharts.Chart#event:getAxes
  35578. */
  35579. getAxes() {
  35580. const options = this.options;
  35581. fireEvent(this, 'getAxes');
  35582. for (const coll of ['xAxis', 'yAxis']) {
  35583. const arr = options[coll] = splat(options[coll] || {});
  35584. for (const axisOptions of arr) {
  35585. // eslint-disable-next-line no-new
  35586. new Axis(this, axisOptions, coll);
  35587. }
  35588. }
  35589. fireEvent(this, 'afterGetAxes');
  35590. }
  35591. /**
  35592. * Returns an array of all currently selected points in the chart. Points
  35593. * can be selected by clicking or programmatically by the
  35594. * {@link Highcharts.Point#select}
  35595. * function.
  35596. *
  35597. * @sample highcharts/plotoptions/series-allowpointselect-line/
  35598. * Get selected points
  35599. *
  35600. * @function Highcharts.Chart#getSelectedPoints
  35601. *
  35602. * @return {Array<Highcharts.Point>}
  35603. * The currently selected points.
  35604. */
  35605. getSelectedPoints() {
  35606. return this.series.reduce((acc, series) => {
  35607. // For one-to-one points inspect series.data in order to retrieve
  35608. // points outside the visible range (#6445). For grouped data,
  35609. // inspect the generated series.points.
  35610. series.getPointsCollection()
  35611. .forEach((point) => {
  35612. if (pick(point.selectedStaging, point.selected)) {
  35613. acc.push(point);
  35614. }
  35615. });
  35616. return acc;
  35617. }, []);
  35618. }
  35619. /**
  35620. * Returns an array of all currently selected series in the chart. Series
  35621. * can be selected either programmatically by the
  35622. * {@link Highcharts.Series#select}
  35623. * function or by checking the checkbox next to the legend item if
  35624. * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
  35625. * is true.
  35626. *
  35627. * @sample highcharts/members/chart-getselectedseries/
  35628. * Get selected series
  35629. *
  35630. * @function Highcharts.Chart#getSelectedSeries
  35631. *
  35632. * @return {Array<Highcharts.Series>}
  35633. * The currently selected series.
  35634. */
  35635. getSelectedSeries() {
  35636. return this.series.filter(function (serie) {
  35637. return serie.selected;
  35638. });
  35639. }
  35640. /**
  35641. * Set a new title or subtitle for the chart.
  35642. *
  35643. * @sample highcharts/members/chart-settitle/
  35644. * Set title text and styles
  35645. *
  35646. * @function Highcharts.Chart#setTitle
  35647. *
  35648. * @param {Highcharts.TitleOptions} [titleOptions]
  35649. * New title options. The title text itself is set by the
  35650. * `titleOptions.text` property.
  35651. *
  35652. * @param {Highcharts.SubtitleOptions} [subtitleOptions]
  35653. * New subtitle options. The subtitle text itself is set by the
  35654. * `subtitleOptions.text` property.
  35655. *
  35656. * @param {boolean} [redraw]
  35657. * Whether to redraw the chart or wait for a later call to
  35658. * `chart.redraw()`.
  35659. */
  35660. setTitle(titleOptions, subtitleOptions, redraw) {
  35661. this.applyDescription('title', titleOptions);
  35662. this.applyDescription('subtitle', subtitleOptions);
  35663. // The initial call also adds the caption. On update, chart.update will
  35664. // relay to Chart.setCaption.
  35665. this.applyDescription('caption', void 0);
  35666. this.layOutTitles(redraw);
  35667. }
  35668. /**
  35669. * Apply a title, subtitle or caption for the chart
  35670. *
  35671. * @private
  35672. * @function Highcharts.Chart#applyDescription
  35673. * @param name {string}
  35674. * Either title, subtitle or caption
  35675. * @param {Highcharts.TitleOptions|Highcharts.SubtitleOptions|Highcharts.CaptionOptions|undefined} explicitOptions
  35676. * The options to set, will be merged with default options.
  35677. */
  35678. applyDescription(name, explicitOptions) {
  35679. const chart = this;
  35680. // Merge default options with explicit options
  35681. const options = this.options[name] = merge(this.options[name], explicitOptions);
  35682. let elem = this[name];
  35683. if (elem && explicitOptions) {
  35684. this[name] = elem = elem.destroy(); // remove old
  35685. }
  35686. if (options && !elem) {
  35687. elem = this.renderer.text(options.text, 0, 0, options.useHTML)
  35688. .attr({
  35689. align: options.align,
  35690. 'class': 'highcharts-' + name,
  35691. zIndex: options.zIndex || 4
  35692. })
  35693. .add();
  35694. // Update methods, relay to `applyDescription`
  35695. elem.update = function (updateOptions, redraw) {
  35696. chart.applyDescription(name, updateOptions);
  35697. chart.layOutTitles(redraw);
  35698. };
  35699. // Presentational
  35700. if (!this.styledMode) {
  35701. elem.css(extend(name === 'title' ? {
  35702. // #2944
  35703. fontSize: this.options.isStock ? '1em' : '1.2em'
  35704. } : {}, options.style));
  35705. }
  35706. /**
  35707. * The chart title. The title has an `update` method that allows
  35708. * modifying the options directly or indirectly via
  35709. * `chart.update`.
  35710. *
  35711. * @sample highcharts/members/title-update/
  35712. * Updating titles
  35713. *
  35714. * @name Highcharts.Chart#title
  35715. * @type {Highcharts.TitleObject}
  35716. */
  35717. /**
  35718. * The chart subtitle. The subtitle has an `update` method that
  35719. * allows modifying the options directly or indirectly via
  35720. * `chart.update`.
  35721. *
  35722. * @name Highcharts.Chart#subtitle
  35723. * @type {Highcharts.SubtitleObject}
  35724. */
  35725. this[name] = elem;
  35726. }
  35727. }
  35728. /**
  35729. * Internal function to lay out the chart title, subtitle and caption, and
  35730. * cache the full offset height for use in `getMargins`. The result is
  35731. * stored in `this.titleOffset`.
  35732. *
  35733. * @private
  35734. * @function Highcharts.Chart#layOutTitles
  35735. *
  35736. * @param {boolean} [redraw=true]
  35737. * @emits Highcharts.Chart#event:afterLayOutTitles
  35738. */
  35739. layOutTitles(redraw = true) {
  35740. const titleOffset = [0, 0, 0], renderer = this.renderer, spacingBox = this.spacingBox;
  35741. // Lay out the title and the subtitle respectively
  35742. ['title', 'subtitle', 'caption'].forEach(function (key) {
  35743. const title = this[key], titleOptions = (this.options[key]), verticalAlign = titleOptions.verticalAlign || 'top', offset = key === 'title' ?
  35744. verticalAlign === 'top' ? -3 : 0 :
  35745. // Floating subtitle (#6574)
  35746. verticalAlign === 'top' ? titleOffset[0] + 2 : 0;
  35747. if (title) {
  35748. title
  35749. .css({
  35750. width: (titleOptions.width ||
  35751. spacingBox.width + (titleOptions.widthAdjust || 0)) + 'px'
  35752. });
  35753. const baseline = renderer.fontMetrics(title).b,
  35754. // Skip the cache for HTML (#3481, #11666)
  35755. height = Math.round(title.getBBox(titleOptions.useHTML).height);
  35756. title.align(extend({
  35757. y: verticalAlign === 'bottom' ?
  35758. baseline :
  35759. offset + baseline,
  35760. height
  35761. }, titleOptions), false, 'spacingBox');
  35762. if (!titleOptions.floating) {
  35763. if (verticalAlign === 'top') {
  35764. titleOffset[0] = Math.ceil(titleOffset[0] +
  35765. height);
  35766. }
  35767. else if (verticalAlign === 'bottom') {
  35768. titleOffset[2] = Math.ceil(titleOffset[2] +
  35769. height);
  35770. }
  35771. }
  35772. }
  35773. }, this);
  35774. // Handle title.margin and caption.margin
  35775. if (titleOffset[0] &&
  35776. (this.options.title.verticalAlign || 'top') === 'top') {
  35777. titleOffset[0] += this.options.title.margin;
  35778. }
  35779. if (titleOffset[2] &&
  35780. this.options.caption.verticalAlign === 'bottom') {
  35781. titleOffset[2] += this.options.caption.margin;
  35782. }
  35783. const requiresDirtyBox = (!this.titleOffset ||
  35784. this.titleOffset.join(',') !== titleOffset.join(','));
  35785. // Used in getMargins
  35786. this.titleOffset = titleOffset;
  35787. fireEvent(this, 'afterLayOutTitles');
  35788. if (!this.isDirtyBox && requiresDirtyBox) {
  35789. this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
  35790. // Redraw if necessary (#2719, #2744)
  35791. if (this.hasRendered && redraw && this.isDirtyBox) {
  35792. this.redraw();
  35793. }
  35794. }
  35795. }
  35796. /**
  35797. * Internal function to get the available size of the container element
  35798. *
  35799. * @private
  35800. * @function Highcharts.Chart#getContainerBox
  35801. */
  35802. getContainerBox() {
  35803. return {
  35804. width: getStyle(this.renderTo, 'width', true) || 0,
  35805. height: getStyle(this.renderTo, 'height', true) || 0
  35806. };
  35807. }
  35808. /**
  35809. * Internal function to get the chart width and height according to options
  35810. * and container size. Sets {@link Chart.chartWidth} and
  35811. * {@link Chart.chartHeight}.
  35812. *
  35813. * @private
  35814. * @function Highcharts.Chart#getChartSize
  35815. */
  35816. getChartSize() {
  35817. const chart = this, optionsChart = chart.options.chart, widthOption = optionsChart.width, heightOption = optionsChart.height, containerBox = chart.getContainerBox();
  35818. /**
  35819. * The current pixel width of the chart.
  35820. *
  35821. * @name Highcharts.Chart#chartWidth
  35822. * @type {number}
  35823. */
  35824. chart.chartWidth = Math.max(// #1393
  35825. 0, widthOption || containerBox.width || 600 // #1460
  35826. );
  35827. /**
  35828. * The current pixel height of the chart.
  35829. *
  35830. * @name Highcharts.Chart#chartHeight
  35831. * @type {number}
  35832. */
  35833. chart.chartHeight = Math.max(0, relativeLength(heightOption, chart.chartWidth) ||
  35834. (containerBox.height > 1 ? containerBox.height : 400));
  35835. chart.containerBox = containerBox;
  35836. }
  35837. /**
  35838. * If the renderTo element has no offsetWidth, most likely one or more of
  35839. * its parents are hidden. Loop up the DOM tree to temporarily display the
  35840. * parents, then save the original display properties, and when the true
  35841. * size is retrieved, reset them. Used on first render and on redraws.
  35842. *
  35843. * @private
  35844. * @function Highcharts.Chart#temporaryDisplay
  35845. *
  35846. * @param {boolean} [revert]
  35847. * Revert to the saved original styles.
  35848. */
  35849. temporaryDisplay(revert) {
  35850. let node = this.renderTo, tempStyle;
  35851. if (!revert) {
  35852. while (node && node.style) {
  35853. // When rendering to a detached node, it needs to be temporarily
  35854. // attached in order to read styling and bounding boxes (#5783,
  35855. // #7024).
  35856. if (!doc.body.contains(node) && !node.parentNode) {
  35857. node.hcOrigDetached = true;
  35858. doc.body.appendChild(node);
  35859. }
  35860. if (getStyle(node, 'display', false) === 'none' ||
  35861. node.hcOricDetached) {
  35862. node.hcOrigStyle = {
  35863. display: node.style.display,
  35864. height: node.style.height,
  35865. overflow: node.style.overflow
  35866. };
  35867. tempStyle = {
  35868. display: 'block',
  35869. overflow: 'hidden'
  35870. };
  35871. if (node !== this.renderTo) {
  35872. tempStyle.height = 0;
  35873. }
  35874. css(node, tempStyle);
  35875. // If it still doesn't have an offset width after setting
  35876. // display to block, it probably has an !important priority
  35877. // #2631, 6803
  35878. if (!node.offsetWidth) {
  35879. node.style.setProperty('display', 'block', 'important');
  35880. }
  35881. }
  35882. node = node.parentNode;
  35883. if (node === doc.body) {
  35884. break;
  35885. }
  35886. }
  35887. }
  35888. else {
  35889. while (node && node.style) {
  35890. if (node.hcOrigStyle) {
  35891. css(node, node.hcOrigStyle);
  35892. delete node.hcOrigStyle;
  35893. }
  35894. if (node.hcOrigDetached) {
  35895. doc.body.removeChild(node);
  35896. node.hcOrigDetached = false;
  35897. }
  35898. node = node.parentNode;
  35899. }
  35900. }
  35901. }
  35902. /**
  35903. * Set the {@link Chart.container|chart container's} class name, in
  35904. * addition to `highcharts-container`.
  35905. *
  35906. * @function Highcharts.Chart#setClassName
  35907. *
  35908. * @param {string} [className]
  35909. * The additional class name.
  35910. */
  35911. setClassName(className) {
  35912. this.container.className = 'highcharts-container ' + (className || '');
  35913. }
  35914. /**
  35915. * Get the containing element, determine the size and create the inner
  35916. * container div to hold the chart.
  35917. *
  35918. * @private
  35919. * @function Highcharts.Chart#afterGetContainer
  35920. * @emits Highcharts.Chart#event:afterGetContainer
  35921. */
  35922. getContainer() {
  35923. const chart = this, options = chart.options, optionsChart = options.chart, indexAttrName = 'data-highcharts-chart', containerId = uniqueKey();
  35924. let containerStyle, renderTo = chart.renderTo;
  35925. if (!renderTo) {
  35926. chart.renderTo = renderTo =
  35927. optionsChart.renderTo;
  35928. }
  35929. if (isString(renderTo)) {
  35930. chart.renderTo = renderTo =
  35931. doc.getElementById(renderTo);
  35932. }
  35933. // Display an error if the renderTo is wrong
  35934. if (!renderTo) {
  35935. error(13, true, chart);
  35936. }
  35937. // If the container already holds a chart, destroy it. The check for
  35938. // hasRendered is there because web pages that are saved to disk from
  35939. // the browser, will preserve the data-highcharts-chart attribute and
  35940. // the SVG contents, but not an interactive chart. So in this case,
  35941. // charts[oldChartIndex] will point to the wrong chart if any (#2609).
  35942. const oldChartIndex = pInt(attr(renderTo, indexAttrName));
  35943. if (isNumber(oldChartIndex) &&
  35944. charts[oldChartIndex] &&
  35945. charts[oldChartIndex].hasRendered) {
  35946. charts[oldChartIndex].destroy();
  35947. }
  35948. // Make a reference to the chart from the div
  35949. attr(renderTo, indexAttrName, chart.index);
  35950. // remove previous chart
  35951. renderTo.innerHTML = AST.emptyHTML;
  35952. // If the container doesn't have an offsetWidth, it has or is a child of
  35953. // a node that has display:none. We need to temporarily move it out to a
  35954. // visible state to determine the size, else the legend and tooltips
  35955. // won't render properly. The skipClone option is used in sparklines as
  35956. // a micro optimization, saving about 1-2 ms each chart.
  35957. if (!optionsChart.skipClone && !renderTo.offsetWidth) {
  35958. chart.temporaryDisplay();
  35959. }
  35960. // get the width and height
  35961. chart.getChartSize();
  35962. const chartWidth = chart.chartWidth;
  35963. const chartHeight = chart.chartHeight;
  35964. // Allow table cells and flex-boxes to shrink without the chart blocking
  35965. // them out (#6427)
  35966. css(renderTo, { overflow: 'hidden' });
  35967. // Create the inner container
  35968. if (!chart.styledMode) {
  35969. containerStyle = extend({
  35970. position: 'relative',
  35971. // needed for context menu (avoidscrollbars) and content
  35972. // overflow in IE
  35973. overflow: 'hidden',
  35974. width: chartWidth + 'px',
  35975. height: chartHeight + 'px',
  35976. textAlign: 'left',
  35977. lineHeight: 'normal',
  35978. zIndex: 0,
  35979. '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
  35980. userSelect: 'none',
  35981. 'touch-action': 'manipulation',
  35982. outline: 'none'
  35983. }, optionsChart.style || {});
  35984. }
  35985. /**
  35986. * The containing HTML element of the chart. The container is
  35987. * dynamically inserted into the element given as the `renderTo`
  35988. * parameter in the {@link Highcharts#chart} constructor.
  35989. *
  35990. * @name Highcharts.Chart#container
  35991. * @type {Highcharts.HTMLDOMElement}
  35992. */
  35993. const container = createElement('div', {
  35994. id: containerId
  35995. }, containerStyle, renderTo);
  35996. chart.container = container;
  35997. // cache the cursor (#1650)
  35998. chart._cursor = container.style.cursor;
  35999. // Initialize the renderer
  36000. const Renderer = optionsChart.renderer || !svg ?
  36001. RendererRegistry.getRendererType(optionsChart.renderer) :
  36002. SVGRenderer;
  36003. /**
  36004. * The renderer instance of the chart. Each chart instance has only one
  36005. * associated renderer.
  36006. *
  36007. * @name Highcharts.Chart#renderer
  36008. * @type {Highcharts.SVGRenderer}
  36009. */
  36010. chart.renderer = new Renderer(container, chartWidth, chartHeight, void 0, optionsChart.forExport, options.exporting && options.exporting.allowHTML, chart.styledMode);
  36011. chart.containerBox = chart.getContainerBox();
  36012. // Set the initial animation from the options
  36013. setAnimation(void 0, chart);
  36014. chart.setClassName(optionsChart.className);
  36015. if (!chart.styledMode) {
  36016. chart.renderer.setStyle(optionsChart.style);
  36017. }
  36018. else {
  36019. // Initialize definitions
  36020. for (const key in options.defs) { // eslint-disable-line guard-for-in
  36021. this.renderer.definition(options.defs[key]);
  36022. }
  36023. }
  36024. // Add a reference to the charts index
  36025. chart.renderer.chartIndex = chart.index;
  36026. fireEvent(this, 'afterGetContainer');
  36027. }
  36028. /**
  36029. * Calculate margins by rendering axis labels in a preliminary position.
  36030. * Title, subtitle and legend have already been rendered at this stage, but
  36031. * will be moved into their final positions.
  36032. *
  36033. * @private
  36034. * @function Highcharts.Chart#getMargins
  36035. * @emits Highcharts.Chart#event:getMargins
  36036. */
  36037. getMargins(skipAxes) {
  36038. const { spacing, margin, titleOffset } = this;
  36039. this.resetMargins();
  36040. // Adjust for title and subtitle
  36041. if (titleOffset[0] && !defined(margin[0])) {
  36042. this.plotTop = Math.max(this.plotTop, titleOffset[0] + spacing[0]);
  36043. }
  36044. if (titleOffset[2] && !defined(margin[2])) {
  36045. this.marginBottom = Math.max(this.marginBottom, titleOffset[2] + spacing[2]);
  36046. }
  36047. // Adjust for legend
  36048. if (this.legend && this.legend.display) {
  36049. this.legend.adjustMargins(margin, spacing);
  36050. }
  36051. fireEvent(this, 'getMargins');
  36052. if (!skipAxes) {
  36053. this.getAxisMargins();
  36054. }
  36055. }
  36056. /**
  36057. * @private
  36058. * @function Highcharts.Chart#getAxisMargins
  36059. */
  36060. getAxisMargins() {
  36061. const chart = this,
  36062. // [top, right, bottom, left]
  36063. axisOffset = chart.axisOffset = [0, 0, 0, 0], colorAxis = chart.colorAxis, margin = chart.margin, getOffset = function (axes) {
  36064. axes.forEach(function (axis) {
  36065. if (axis.visible) {
  36066. axis.getOffset();
  36067. }
  36068. });
  36069. };
  36070. // pre-render axes to get labels offset width
  36071. if (chart.hasCartesianSeries) {
  36072. getOffset(chart.axes);
  36073. }
  36074. else if (colorAxis && colorAxis.length) {
  36075. getOffset(colorAxis);
  36076. }
  36077. // Add the axis offsets
  36078. marginNames.forEach(function (m, side) {
  36079. if (!defined(margin[side])) {
  36080. chart[m] += axisOffset[side];
  36081. }
  36082. });
  36083. chart.setChartSize();
  36084. }
  36085. /**
  36086. * Return the current options of the chart, but only those that differ from
  36087. * default options. Items that can be either an object or an array of
  36088. * objects, like `series`, `xAxis` and `yAxis`, are always returned as
  36089. * array.
  36090. *
  36091. * @sample highcharts/members/chart-getoptions
  36092. *
  36093. * @function Highcharts.Chart#getOptions
  36094. *
  36095. * @since 11.1.0
  36096. */
  36097. getOptions() {
  36098. return diffObjects(this.userOptions, defaultOptions);
  36099. }
  36100. /**
  36101. * Reflows the chart to its container. By default, the Resize Observer is
  36102. * attached to the chart's div which allows to reflows the chart
  36103. * automatically to its container, as per the
  36104. * [chart.reflow](https://api.highcharts.com/highcharts/chart.reflow)
  36105. * option.
  36106. *
  36107. * @sample highcharts/chart/events-container/
  36108. * Pop up and reflow
  36109. *
  36110. * @function Highcharts.Chart#reflow
  36111. *
  36112. * @param {global.Event} [e]
  36113. * Event arguments. Used primarily when the function is called
  36114. * internally as a response to window resize.
  36115. */
  36116. reflow(e) {
  36117. const chart = this, optionsChart = chart.options.chart, hasUserSize = (defined(optionsChart.width) &&
  36118. defined(optionsChart.height)), oldBox = chart.containerBox, containerBox = chart.getContainerBox();
  36119. delete chart.pointer.chartPosition;
  36120. // Width and height checks for display:none. Target is doc in Opera
  36121. // and win in Firefox, Chrome and IE9.
  36122. if (!hasUserSize &&
  36123. !chart.isPrinting &&
  36124. oldBox &&
  36125. // When fired by resize observer inside hidden container
  36126. containerBox.width) {
  36127. if (containerBox.width !== oldBox.width ||
  36128. containerBox.height !== oldBox.height) {
  36129. U.clearTimeout(chart.reflowTimeout);
  36130. // When called from window.resize, e is set, else it's called
  36131. // directly (#2224)
  36132. chart.reflowTimeout = syncTimeout(function () {
  36133. // Set size, it may have been destroyed in the meantime
  36134. // (#1257)
  36135. if (chart.container) {
  36136. chart.setSize(void 0, void 0, false);
  36137. }
  36138. }, e ? 100 : 0);
  36139. }
  36140. chart.containerBox = containerBox;
  36141. }
  36142. }
  36143. /**
  36144. * Toggle the event handlers necessary for auto resizing, depending on the
  36145. * `chart.reflow` option.
  36146. *
  36147. * @private
  36148. * @function Highcharts.Chart#setReflow
  36149. */
  36150. setReflow() {
  36151. const chart = this;
  36152. const runReflow = (e) => {
  36153. var _a;
  36154. if (((_a = chart.options) === null || _a === void 0 ? void 0 : _a.chart.reflow) && chart.hasLoaded) {
  36155. chart.reflow(e);
  36156. }
  36157. };
  36158. if (typeof ResizeObserver === 'function') {
  36159. (new ResizeObserver(runReflow)).observe(chart.renderTo);
  36160. // Fallback for more legacy browser versions.
  36161. }
  36162. else {
  36163. const unbind = addEvent(win, 'resize', runReflow);
  36164. addEvent(this, 'destroy', unbind);
  36165. }
  36166. }
  36167. /**
  36168. * Resize the chart to a given width and height. In order to set the width
  36169. * only, the height argument may be skipped. To set the height only, pass
  36170. * `undefined` for the width.
  36171. *
  36172. * @sample highcharts/members/chart-setsize-button/
  36173. * Test resizing from buttons
  36174. * @sample highcharts/members/chart-setsize-jquery-resizable/
  36175. * Add a jQuery UI resizable
  36176. * @sample stock/members/chart-setsize/
  36177. * Highcharts Stock with UI resizable
  36178. *
  36179. * @function Highcharts.Chart#setSize
  36180. *
  36181. * @param {number|null} [width]
  36182. * The new pixel width of the chart. Since v4.2.6, the argument can
  36183. * be `undefined` in order to preserve the current value (when
  36184. * setting height only), or `null` to adapt to the width of the
  36185. * containing element.
  36186. *
  36187. * @param {number|null} [height]
  36188. * The new pixel height of the chart. Since v4.2.6, the argument can
  36189. * be `undefined` in order to preserve the current value, or `null`
  36190. * in order to adapt to the height of the containing element.
  36191. *
  36192. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36193. * Whether and how to apply animation. When `undefined`, it applies
  36194. * the animation that is set in the `chart.animation` option.
  36195. *
  36196. *
  36197. * @emits Highcharts.Chart#event:endResize
  36198. * @emits Highcharts.Chart#event:resize
  36199. */
  36200. setSize(width, height, animation) {
  36201. const chart = this, renderer = chart.renderer;
  36202. // Handle the isResizing counter
  36203. chart.isResizing += 1;
  36204. // set the animation for the current process
  36205. setAnimation(animation, chart);
  36206. const globalAnimation = renderer.globalAnimation;
  36207. chart.oldChartHeight = chart.chartHeight;
  36208. chart.oldChartWidth = chart.chartWidth;
  36209. if (typeof width !== 'undefined') {
  36210. chart.options.chart.width = width;
  36211. }
  36212. if (typeof height !== 'undefined') {
  36213. chart.options.chart.height = height;
  36214. }
  36215. chart.getChartSize();
  36216. // Resize the container with the global animation applied if enabled
  36217. // (#2503)
  36218. if (!chart.styledMode) {
  36219. (globalAnimation ? animate : css)(chart.container, {
  36220. width: chart.chartWidth + 'px',
  36221. height: chart.chartHeight + 'px'
  36222. }, globalAnimation);
  36223. }
  36224. chart.setChartSize(true);
  36225. renderer.setSize(chart.chartWidth, chart.chartHeight, globalAnimation);
  36226. // handle axes
  36227. chart.axes.forEach(function (axis) {
  36228. axis.isDirty = true;
  36229. axis.setScale();
  36230. });
  36231. chart.isDirtyLegend = true; // force legend redraw
  36232. chart.isDirtyBox = true; // force redraw of plot and chart border
  36233. chart.layOutTitles(); // #2857
  36234. chart.getMargins();
  36235. chart.redraw(globalAnimation);
  36236. chart.oldChartHeight = null;
  36237. fireEvent(chart, 'resize');
  36238. // Fire endResize and set isResizing back. If animation is disabled,
  36239. // fire without delay
  36240. syncTimeout(function () {
  36241. if (chart) {
  36242. fireEvent(chart, 'endResize', null, function () {
  36243. chart.isResizing -= 1;
  36244. });
  36245. }
  36246. }, animObject(globalAnimation).duration);
  36247. }
  36248. /**
  36249. * Set the public chart properties. This is done before and after the
  36250. * pre-render to determine margin sizes.
  36251. *
  36252. * @private
  36253. * @function Highcharts.Chart#setChartSize
  36254. * @emits Highcharts.Chart#event:afterSetChartSize
  36255. */
  36256. setChartSize(skipAxes) {
  36257. 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;
  36258. let plotLeft, plotTop, plotWidth, plotHeight;
  36259. /**
  36260. * The current left position of the plot area in pixels.
  36261. *
  36262. * @name Highcharts.Chart#plotLeft
  36263. * @type {number}
  36264. */
  36265. chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
  36266. /**
  36267. * The current top position of the plot area in pixels.
  36268. *
  36269. * @name Highcharts.Chart#plotTop
  36270. * @type {number}
  36271. */
  36272. chart.plotTop = plotTop = Math.round(chart.plotTop);
  36273. /**
  36274. * The current width of the plot area in pixels.
  36275. *
  36276. * @name Highcharts.Chart#plotWidth
  36277. * @type {number}
  36278. */
  36279. chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
  36280. /**
  36281. * The current height of the plot area in pixels.
  36282. *
  36283. * @name Highcharts.Chart#plotHeight
  36284. * @type {number}
  36285. */
  36286. chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
  36287. chart.plotSizeX = inverted ? plotHeight : plotWidth;
  36288. chart.plotSizeY = inverted ? plotWidth : plotHeight;
  36289. chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
  36290. // Set boxes used for alignment
  36291. chart.spacingBox = renderer.spacingBox = {
  36292. x: spacing[3],
  36293. y: spacing[0],
  36294. width: chartWidth - spacing[3] - spacing[1],
  36295. height: chartHeight - spacing[0] - spacing[2]
  36296. };
  36297. chart.plotBox = renderer.plotBox = {
  36298. x: plotLeft,
  36299. y: plotTop,
  36300. width: plotWidth,
  36301. height: plotHeight
  36302. };
  36303. 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);
  36304. chart.clipBox = {
  36305. x: clipX,
  36306. y: clipY,
  36307. width: Math.floor(chart.plotSizeX -
  36308. Math.max(plotBorderWidth, clipOffset[1]) / 2 -
  36309. clipX),
  36310. height: Math.max(0, Math.floor(chart.plotSizeY -
  36311. Math.max(plotBorderWidth, clipOffset[2]) / 2 -
  36312. clipY))
  36313. };
  36314. if (!skipAxes) {
  36315. chart.axes.forEach(function (axis) {
  36316. axis.setAxisSize();
  36317. axis.setAxisTranslation();
  36318. });
  36319. renderer.alignElements();
  36320. }
  36321. fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
  36322. }
  36323. /**
  36324. * Initial margins before auto size margins are applied.
  36325. *
  36326. * @private
  36327. * @function Highcharts.Chart#resetMargins
  36328. */
  36329. resetMargins() {
  36330. fireEvent(this, 'resetMargins');
  36331. const chart = this, chartOptions = chart.options.chart;
  36332. // Create margin and spacing array
  36333. ['margin', 'spacing'].forEach(function splashArrays(target) {
  36334. const value = chartOptions[target], values = isObject(value) ? value : [value, value, value, value];
  36335. [
  36336. 'Top',
  36337. 'Right',
  36338. 'Bottom',
  36339. 'Left'
  36340. ].forEach(function (sideName, side) {
  36341. chart[target][side] = pick(chartOptions[target + sideName], values[side]);
  36342. });
  36343. });
  36344. // Set margin names like chart.plotTop, chart.plotLeft,
  36345. // chart.marginRight, chart.marginBottom.
  36346. marginNames.forEach(function (m, side) {
  36347. chart[m] = pick(chart.margin[side], chart.spacing[side]);
  36348. });
  36349. chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
  36350. chart.clipOffset = [0, 0, 0, 0];
  36351. }
  36352. /**
  36353. * Internal function to draw or redraw the borders and backgrounds for chart
  36354. * and plot area.
  36355. *
  36356. * @private
  36357. * @function Highcharts.Chart#drawChartBox
  36358. * @emits Highcharts.Chart#event:afterDrawChartBox
  36359. */
  36360. drawChartBox() {
  36361. 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;
  36362. let chartBackground = chart.chartBackground, plotBackground = chart.plotBackground, plotBorder = chart.plotBorder, chartBorderWidth, mgn, bgAttr, verb = 'animate';
  36363. // Chart area
  36364. if (!chartBackground) {
  36365. chart.chartBackground = chartBackground = renderer.rect()
  36366. .addClass('highcharts-background')
  36367. .add();
  36368. verb = 'attr';
  36369. }
  36370. if (!styledMode) {
  36371. // Presentational
  36372. chartBorderWidth = optionsChart.borderWidth || 0;
  36373. mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
  36374. bgAttr = {
  36375. fill: chartBackgroundColor || 'none'
  36376. };
  36377. if (chartBorderWidth || chartBackground['stroke-width']) { // #980
  36378. bgAttr.stroke = optionsChart.borderColor;
  36379. bgAttr['stroke-width'] = chartBorderWidth;
  36380. }
  36381. chartBackground
  36382. .attr(bgAttr)
  36383. .shadow(optionsChart.shadow);
  36384. }
  36385. else {
  36386. chartBorderWidth = mgn = chartBackground.strokeWidth();
  36387. }
  36388. chartBackground[verb]({
  36389. x: mgn / 2,
  36390. y: mgn / 2,
  36391. width: chartWidth - mgn - chartBorderWidth % 2,
  36392. height: chartHeight - mgn - chartBorderWidth % 2,
  36393. r: optionsChart.borderRadius
  36394. });
  36395. // Plot background
  36396. verb = 'animate';
  36397. if (!plotBackground) {
  36398. verb = 'attr';
  36399. chart.plotBackground = plotBackground = renderer.rect()
  36400. .addClass('highcharts-plot-background')
  36401. .add();
  36402. }
  36403. plotBackground[verb](plotBox);
  36404. if (!styledMode) {
  36405. // Presentational attributes for the background
  36406. plotBackground
  36407. .attr({
  36408. fill: plotBackgroundColor || 'none'
  36409. })
  36410. .shadow(optionsChart.plotShadow);
  36411. // Create the background image
  36412. if (plotBackgroundImage) {
  36413. if (!plotBGImage) {
  36414. chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight).add();
  36415. }
  36416. else {
  36417. if (plotBackgroundImage !== plotBGImage.attr('href')) {
  36418. plotBGImage.attr('href', plotBackgroundImage);
  36419. }
  36420. plotBGImage.animate(plotBox);
  36421. }
  36422. }
  36423. }
  36424. // Plot clip
  36425. if (!clipRect) {
  36426. chart.clipRect = renderer.clipRect(clipBox);
  36427. }
  36428. else {
  36429. clipRect.animate({
  36430. width: clipBox.width,
  36431. height: clipBox.height
  36432. });
  36433. }
  36434. // Plot area border
  36435. verb = 'animate';
  36436. if (!plotBorder) {
  36437. verb = 'attr';
  36438. chart.plotBorder = plotBorder = renderer.rect()
  36439. .addClass('highcharts-plot-border')
  36440. .attr({
  36441. zIndex: 1 // Above the grid
  36442. })
  36443. .add();
  36444. }
  36445. if (!styledMode) {
  36446. // Presentational
  36447. plotBorder.attr({
  36448. stroke: optionsChart.plotBorderColor,
  36449. 'stroke-width': optionsChart.plotBorderWidth || 0,
  36450. fill: 'none'
  36451. });
  36452. }
  36453. plotBorder[verb](plotBorder.crisp({
  36454. x: plotLeft,
  36455. y: plotTop,
  36456. width: plotWidth,
  36457. height: plotHeight
  36458. }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
  36459. // reset
  36460. chart.isDirtyBox = false;
  36461. fireEvent(this, 'afterDrawChartBox');
  36462. }
  36463. /**
  36464. * Detect whether a certain chart property is needed based on inspecting its
  36465. * options and series. This mainly applies to the chart.inverted property,
  36466. * and in extensions to the chart.angular and chart.polar properties.
  36467. *
  36468. * @private
  36469. * @function Highcharts.Chart#propFromSeries
  36470. */
  36471. propFromSeries() {
  36472. const chart = this, optionsChart = chart.options.chart, seriesOptions = chart.options.series;
  36473. let i, klass, value;
  36474. /**
  36475. * The flag is set to `true` if a series of the chart is inverted.
  36476. *
  36477. * @name Highcharts.Chart#inverted
  36478. * @type {boolean|undefined}
  36479. */
  36480. ['inverted', 'angular', 'polar'].forEach(function (key) {
  36481. // The default series type's class
  36482. klass = seriesTypes[optionsChart.type];
  36483. // Get the value from available chart-wide properties
  36484. value =
  36485. // It is set in the options:
  36486. optionsChart[key] ||
  36487. // The default series class:
  36488. (klass && klass.prototype[key]);
  36489. // requires it
  36490. // 4. Check if any the chart's series require it
  36491. i = seriesOptions && seriesOptions.length;
  36492. while (!value && i--) {
  36493. klass = seriesTypes[seriesOptions[i].type];
  36494. if (klass && klass.prototype[key]) {
  36495. value = true;
  36496. }
  36497. }
  36498. // Set the chart property
  36499. chart[key] = value;
  36500. });
  36501. }
  36502. /**
  36503. * Internal function to link two or more series together, based on the
  36504. * `linkedTo` option. This is done from `Chart.render`, and after
  36505. * `Chart.addSeries` and `Series.remove`.
  36506. *
  36507. * @private
  36508. * @function Highcharts.Chart#linkSeries
  36509. * @emits Highcharts.Chart#event:afterLinkSeries
  36510. */
  36511. linkSeries(isUpdating) {
  36512. const chart = this, chartSeries = chart.series;
  36513. // Reset links
  36514. chartSeries.forEach(function (series) {
  36515. series.linkedSeries.length = 0;
  36516. });
  36517. // Apply new links
  36518. chartSeries.forEach(function (series) {
  36519. let linkedTo = series.options.linkedTo;
  36520. if (isString(linkedTo)) {
  36521. if (linkedTo === ':previous') {
  36522. linkedTo = chart.series[series.index - 1];
  36523. }
  36524. else {
  36525. linkedTo = chart.get(linkedTo);
  36526. }
  36527. // #3341 avoid mutual linking
  36528. if (linkedTo && linkedTo.linkedParent !== series) {
  36529. linkedTo.linkedSeries.push(series);
  36530. series.linkedParent = linkedTo;
  36531. if (linkedTo.enabledDataSorting) {
  36532. series.setDataSortingOptions();
  36533. }
  36534. series.visible = pick(series.options.visible, linkedTo.options.visible, series.visible); // #3879
  36535. }
  36536. }
  36537. });
  36538. fireEvent(this, 'afterLinkSeries', { isUpdating });
  36539. }
  36540. /**
  36541. * Render series for the chart.
  36542. *
  36543. * @private
  36544. * @function Highcharts.Chart#renderSeries
  36545. */
  36546. renderSeries() {
  36547. this.series.forEach(function (serie) {
  36548. serie.translate();
  36549. serie.render();
  36550. });
  36551. }
  36552. /**
  36553. * Render all graphics for the chart. Runs internally on initialization.
  36554. *
  36555. * @private
  36556. * @function Highcharts.Chart#render
  36557. */
  36558. render() {
  36559. const chart = this, axes = chart.axes, colorAxis = chart.colorAxis, renderer = chart.renderer, renderAxes = function (axes) {
  36560. axes.forEach(function (axis) {
  36561. if (axis.visible) {
  36562. axis.render();
  36563. }
  36564. });
  36565. };
  36566. let correction = 0; // correction for X axis labels
  36567. // Title
  36568. chart.setTitle();
  36569. // Fire an event before the margins are computed. This is where the
  36570. // legend is assigned.
  36571. fireEvent(chart, 'beforeMargins');
  36572. // Get stacks
  36573. if (chart.getStacks) {
  36574. chart.getStacks();
  36575. }
  36576. // Get chart margins
  36577. chart.getMargins(true);
  36578. chart.setChartSize();
  36579. // Record preliminary dimensions for later comparison
  36580. const tempWidth = chart.plotWidth;
  36581. axes.some(function (axis) {
  36582. if (axis.horiz &&
  36583. axis.visible &&
  36584. axis.options.labels.enabled &&
  36585. axis.series.length) {
  36586. // 21 is the most common correction for X axis labels
  36587. correction = 21;
  36588. return true;
  36589. }
  36590. });
  36591. // use Math.max to prevent negative plotHeight
  36592. chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
  36593. const tempHeight = chart.plotHeight;
  36594. // Get margins by pre-rendering axes
  36595. axes.forEach(function (axis) {
  36596. axis.setScale();
  36597. });
  36598. chart.getAxisMargins();
  36599. // If the plot area size has changed significantly, calculate tick
  36600. // positions again
  36601. const redoHorizontal = tempWidth / chart.plotWidth > 1.1;
  36602. // Height is more sensitive, use lower threshold
  36603. const redoVertical = tempHeight / chart.plotHeight > 1.05;
  36604. if (redoHorizontal || redoVertical) {
  36605. axes.forEach(function (axis) {
  36606. if ((axis.horiz && redoHorizontal) ||
  36607. (!axis.horiz && redoVertical)) {
  36608. // update to reflect the new margins
  36609. axis.setTickInterval(true);
  36610. }
  36611. });
  36612. chart.getMargins(); // second pass to check for new labels
  36613. }
  36614. // Draw the borders and backgrounds
  36615. chart.drawChartBox();
  36616. // Axes
  36617. if (chart.hasCartesianSeries) {
  36618. renderAxes(axes);
  36619. }
  36620. else if (colorAxis && colorAxis.length) {
  36621. renderAxes(colorAxis);
  36622. }
  36623. // The series
  36624. if (!chart.seriesGroup) {
  36625. chart.seriesGroup = renderer.g('series-group')
  36626. .attr({ zIndex: 3 })
  36627. .shadow(chart.options.chart.seriesGroupShadow)
  36628. .add();
  36629. }
  36630. chart.renderSeries();
  36631. // Credits
  36632. chart.addCredits();
  36633. // Handle responsiveness
  36634. if (chart.setResponsive) {
  36635. chart.setResponsive();
  36636. }
  36637. // Set flag
  36638. chart.hasRendered = true;
  36639. }
  36640. /**
  36641. * Set a new credits label for the chart.
  36642. *
  36643. * @sample highcharts/credits/credits-update/
  36644. * Add and update credits
  36645. *
  36646. * @function Highcharts.Chart#addCredits
  36647. *
  36648. * @param {Highcharts.CreditsOptions} [credits]
  36649. * A configuration object for the new credits.
  36650. */
  36651. addCredits(credits) {
  36652. const chart = this, creds = merge(true, this.options.credits, credits);
  36653. if (creds.enabled && !this.credits) {
  36654. /**
  36655. * The chart's credits label. The label has an `update` method that
  36656. * allows setting new options as per the
  36657. * [credits options set](https://api.highcharts.com/highcharts/credits).
  36658. *
  36659. * @name Highcharts.Chart#credits
  36660. * @type {Highcharts.SVGElement}
  36661. */
  36662. this.credits = this.renderer.text(creds.text + (this.mapCredits || ''), 0, 0)
  36663. .addClass('highcharts-credits')
  36664. .on('click', function () {
  36665. if (creds.href) {
  36666. win.location.href = creds.href;
  36667. }
  36668. })
  36669. .attr({
  36670. align: creds.position.align,
  36671. zIndex: 8
  36672. });
  36673. if (!chart.styledMode) {
  36674. this.credits.css(creds.style);
  36675. }
  36676. this.credits
  36677. .add()
  36678. .align(creds.position);
  36679. // Dynamically update
  36680. this.credits.update = function (options) {
  36681. chart.credits = chart.credits.destroy();
  36682. chart.addCredits(options);
  36683. };
  36684. }
  36685. }
  36686. /**
  36687. * Remove the chart and purge memory. This method is called internally
  36688. * before adding a second chart into the same container, as well as on
  36689. * window unload to prevent leaks.
  36690. *
  36691. * @sample highcharts/members/chart-destroy/
  36692. * Destroy the chart from a button
  36693. * @sample stock/members/chart-destroy/
  36694. * Destroy with Highcharts Stock
  36695. *
  36696. * @function Highcharts.Chart#destroy
  36697. *
  36698. * @emits Highcharts.Chart#event:destroy
  36699. */
  36700. destroy() {
  36701. const chart = this, axes = chart.axes, series = chart.series, container = chart.container, parentNode = container && container.parentNode;
  36702. let i;
  36703. // fire the chart.destoy event
  36704. fireEvent(chart, 'destroy');
  36705. // Delete the chart from charts lookup array
  36706. if (chart.renderer.forExport) {
  36707. erase(charts, chart); // #6569
  36708. }
  36709. else {
  36710. charts[chart.index] = void 0;
  36711. }
  36712. H.chartCount--;
  36713. chart.renderTo.removeAttribute('data-highcharts-chart');
  36714. // remove events
  36715. removeEvent(chart);
  36716. // ==== Destroy collections:
  36717. // Destroy axes
  36718. i = axes.length;
  36719. while (i--) {
  36720. axes[i] = axes[i].destroy();
  36721. }
  36722. // Destroy scroller & scroller series before destroying base series
  36723. if (this.scroller && this.scroller.destroy) {
  36724. this.scroller.destroy();
  36725. }
  36726. // Destroy each series
  36727. i = series.length;
  36728. while (i--) {
  36729. series[i] = series[i].destroy();
  36730. }
  36731. // ==== Destroy chart properties:
  36732. [
  36733. 'title', 'subtitle', 'chartBackground', 'plotBackground',
  36734. 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
  36735. 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
  36736. 'renderer'
  36737. ].forEach(function (name) {
  36738. const prop = chart[name];
  36739. if (prop && prop.destroy) {
  36740. chart[name] = prop.destroy();
  36741. }
  36742. });
  36743. // Remove container and all SVG, check container as it can break in IE
  36744. // when destroyed before finished loading
  36745. if (container) {
  36746. container.innerHTML = AST.emptyHTML;
  36747. removeEvent(container);
  36748. if (parentNode) {
  36749. discardElement(container);
  36750. }
  36751. }
  36752. // clean it all up
  36753. objectEach(chart, function (val, key) {
  36754. delete chart[key];
  36755. });
  36756. }
  36757. /**
  36758. * Prepare for first rendering after all data are loaded.
  36759. *
  36760. * @private
  36761. * @function Highcharts.Chart#firstRender
  36762. * @emits Highcharts.Chart#event:beforeRender
  36763. */
  36764. firstRender() {
  36765. const chart = this, options = chart.options;
  36766. // Create the container
  36767. chart.getContainer();
  36768. chart.resetMargins();
  36769. chart.setChartSize();
  36770. // Set the common chart properties (mainly invert) from the given series
  36771. chart.propFromSeries();
  36772. // get axes
  36773. chart.getAxes();
  36774. // Initialize the series
  36775. const series = isArray(options.series) ? options.series : [];
  36776. options.series = []; // Avoid mutation
  36777. series.forEach(
  36778. // #9680
  36779. function (serieOptions) {
  36780. chart.initSeries(serieOptions);
  36781. });
  36782. chart.linkSeries();
  36783. chart.setSeriesData();
  36784. // Run an event after axes and series are initialized, but before
  36785. // render. At this stage, the series data is indexed and cached in the
  36786. // xData and yData arrays, so we can access those before rendering. Used
  36787. // in Highcharts Stock.
  36788. fireEvent(chart, 'beforeRender');
  36789. chart.render();
  36790. chart.pointer.getChartPosition(); // #14973
  36791. // Fire the load event if there are no external images
  36792. if (!chart.renderer.imgCount && !chart.hasLoaded) {
  36793. chart.onload();
  36794. }
  36795. // If the chart was rendered outside the top container, put it back in
  36796. // (#3679)
  36797. chart.temporaryDisplay(true);
  36798. }
  36799. /**
  36800. * Internal function that runs on chart load, async if any images are loaded
  36801. * in the chart. Runs the callbacks and triggers the `load` and `render`
  36802. * events.
  36803. *
  36804. * @private
  36805. * @function Highcharts.Chart#onload
  36806. * @emits Highcharts.Chart#event:load
  36807. * @emits Highcharts.Chart#event:render
  36808. */
  36809. onload() {
  36810. // Run callbacks, first the ones registered by modules, then user's one
  36811. this.callbacks.concat([this.callback]).forEach(function (fn) {
  36812. // Chart destroyed in its own callback (#3600)
  36813. if (fn && typeof this.index !== 'undefined') {
  36814. fn.apply(this, [this]);
  36815. }
  36816. }, this);
  36817. fireEvent(this, 'load');
  36818. fireEvent(this, 'render');
  36819. // Set up auto resize, check for not destroyed (#6068)
  36820. if (defined(this.index)) {
  36821. this.setReflow();
  36822. }
  36823. this.warnIfA11yModuleNotLoaded();
  36824. // Don't run again
  36825. this.hasLoaded = true;
  36826. }
  36827. /**
  36828. * Emit console warning if the a11y module is not loaded.
  36829. */
  36830. warnIfA11yModuleNotLoaded() {
  36831. const { options, title } = this;
  36832. if (options && !this.accessibility) {
  36833. // Make chart behave as an image with the title as alt text
  36834. this.renderer.boxWrapper.attr({
  36835. role: 'img',
  36836. 'aria-label': ((title && title.element.textContent) || ''
  36837. // #17753, < is not allowed in SVG attributes
  36838. ).replace(/</g, '&lt;')
  36839. });
  36840. if (!(options.accessibility && options.accessibility.enabled === false)) {
  36841. error('Highcharts warning: Consider including the ' +
  36842. '"accessibility.js" module to make your chart more ' +
  36843. 'usable for people with disabilities. Set the ' +
  36844. '"accessibility.enabled" option to false to remove this ' +
  36845. 'warning. See https://www.highcharts.com/docs/accessibility/accessibility-module.', false, this);
  36846. }
  36847. }
  36848. }
  36849. /**
  36850. * Add a series to the chart after render time. Note that this method should
  36851. * never be used when adding data synchronously at chart render time, as it
  36852. * adds expense to the calculations and rendering. When adding data at the
  36853. * same time as the chart is initialized, add the series as a configuration
  36854. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  36855. *
  36856. * @sample highcharts/members/chart-addseries/
  36857. * Add a series from a button
  36858. * @sample stock/members/chart-addseries/
  36859. * Add a series in Highcharts Stock
  36860. *
  36861. * @function Highcharts.Chart#addSeries
  36862. *
  36863. * @param {Highcharts.SeriesOptionsType} options
  36864. * The config options for the series.
  36865. *
  36866. * @param {boolean} [redraw=true]
  36867. * Whether to redraw the chart after adding.
  36868. *
  36869. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36870. * Whether to apply animation, and optionally animation
  36871. * configuration. When `undefined`, it applies the animation that is
  36872. * set in the `chart.animation` option.
  36873. *
  36874. * @return {Highcharts.Series}
  36875. * The newly created series object.
  36876. *
  36877. * @emits Highcharts.Chart#event:addSeries
  36878. * @emits Highcharts.Chart#event:afterAddSeries
  36879. */
  36880. addSeries(options, redraw, animation) {
  36881. const chart = this;
  36882. let series;
  36883. if (options) { // <- not necessary
  36884. redraw = pick(redraw, true); // defaults to true
  36885. fireEvent(chart, 'addSeries', { options: options }, function () {
  36886. series = chart.initSeries(options);
  36887. chart.isDirtyLegend = true;
  36888. chart.linkSeries();
  36889. if (series.enabledDataSorting) {
  36890. // We need to call `setData` after `linkSeries`
  36891. series.setData(options.data, false);
  36892. }
  36893. fireEvent(chart, 'afterAddSeries', { series: series });
  36894. if (redraw) {
  36895. chart.redraw(animation);
  36896. }
  36897. });
  36898. }
  36899. return series;
  36900. }
  36901. /**
  36902. * Add an axis to the chart after render time. Note that this method should
  36903. * never be used when adding data synchronously at chart render time, as it
  36904. * adds expense to the calculations and rendering. When adding data at the
  36905. * same time as the chart is initialized, add the axis as a configuration
  36906. * option instead.
  36907. *
  36908. * @sample highcharts/members/chart-addaxis/
  36909. * Add and remove axes
  36910. *
  36911. * @function Highcharts.Chart#addAxis
  36912. *
  36913. * @param {Highcharts.AxisOptions} options
  36914. * The axis options.
  36915. *
  36916. * @param {boolean} [isX=false]
  36917. * Whether it is an X axis or a value axis.
  36918. *
  36919. * @param {boolean} [redraw=true]
  36920. * Whether to redraw the chart after adding.
  36921. *
  36922. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36923. * Whether and how to apply animation in the redraw. When
  36924. * `undefined`, it applies the animation that is set in the
  36925. * `chart.animation` option.
  36926. *
  36927. * @return {Highcharts.Axis}
  36928. * The newly generated Axis object.
  36929. */
  36930. addAxis(options, isX, redraw, animation) {
  36931. return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
  36932. }
  36933. /**
  36934. * Add a color axis to the chart after render time. Note that this method
  36935. * should never be used when adding data synchronously at chart render time,
  36936. * as it adds expense to the calculations and rendering. When adding data at
  36937. * the same time as the chart is initialized, add the axis as a
  36938. * configuration option instead.
  36939. *
  36940. * @sample highcharts/members/chart-addaxis/
  36941. * Add and remove axes
  36942. *
  36943. * @function Highcharts.Chart#addColorAxis
  36944. *
  36945. * @param {Highcharts.ColorAxisOptions} options
  36946. * The axis options.
  36947. *
  36948. * @param {boolean} [redraw=true]
  36949. * Whether to redraw the chart after adding.
  36950. *
  36951. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  36952. * Whether and how to apply animation in the redraw. When
  36953. * `undefined`, it applies the animation that is set in the
  36954. * `chart.animation` option.
  36955. *
  36956. * @return {Highcharts.Axis}
  36957. * The newly generated Axis object.
  36958. */
  36959. addColorAxis(options, redraw, animation) {
  36960. return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
  36961. }
  36962. /**
  36963. * Factory for creating different axis types.
  36964. *
  36965. * @private
  36966. * @function Highcharts.Chart#createAxis
  36967. *
  36968. * @param {string} coll
  36969. * An axis type.
  36970. *
  36971. * @param {...Array<*>} arguments
  36972. * All arguments for the constructor.
  36973. *
  36974. * @return {Highcharts.Axis}
  36975. * The newly generated Axis object.
  36976. */
  36977. createAxis(coll, options) {
  36978. const axis = new Axis(this, options.axis, coll);
  36979. if (pick(options.redraw, true)) {
  36980. this.redraw(options.animation);
  36981. }
  36982. return axis;
  36983. }
  36984. /**
  36985. * Dim the chart and show a loading text or symbol. Options for the loading
  36986. * screen are defined in {@link
  36987. * https://api.highcharts.com/highcharts/loading|the loading options}.
  36988. *
  36989. * @sample highcharts/members/chart-hideloading/
  36990. * Show and hide loading from a button
  36991. * @sample highcharts/members/chart-showloading/
  36992. * Apply different text labels
  36993. * @sample stock/members/chart-show-hide-loading/
  36994. * Toggle loading in Highcharts Stock
  36995. *
  36996. * @function Highcharts.Chart#showLoading
  36997. *
  36998. * @param {string} [str]
  36999. * An optional text to show in the loading label instead of the
  37000. * default one. The default text is set in
  37001. * [lang.loading](https://api.highcharts.com/highcharts/lang.loading).
  37002. */
  37003. showLoading(str) {
  37004. const chart = this, options = chart.options, loadingOptions = options.loading, setLoadingSize = function () {
  37005. if (loadingDiv) {
  37006. css(loadingDiv, {
  37007. left: chart.plotLeft + 'px',
  37008. top: chart.plotTop + 'px',
  37009. width: chart.plotWidth + 'px',
  37010. height: chart.plotHeight + 'px'
  37011. });
  37012. }
  37013. };
  37014. let loadingDiv = chart.loadingDiv, loadingSpan = chart.loadingSpan;
  37015. // create the layer at the first call
  37016. if (!loadingDiv) {
  37017. chart.loadingDiv = loadingDiv = createElement('div', {
  37018. className: 'highcharts-loading highcharts-loading-hidden'
  37019. }, null, chart.container);
  37020. }
  37021. if (!loadingSpan) {
  37022. chart.loadingSpan = loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
  37023. addEvent(chart, 'redraw', setLoadingSize); // #1080
  37024. }
  37025. loadingDiv.className = 'highcharts-loading';
  37026. // Update text
  37027. AST.setElementHTML(loadingSpan, pick(str, options.lang.loading, ''));
  37028. if (!chart.styledMode) {
  37029. // Update visuals
  37030. css(loadingDiv, extend(loadingOptions.style, {
  37031. zIndex: 10
  37032. }));
  37033. css(loadingSpan, loadingOptions.labelStyle);
  37034. // Show it
  37035. if (!chart.loadingShown) {
  37036. css(loadingDiv, {
  37037. opacity: 0,
  37038. display: ''
  37039. });
  37040. animate(loadingDiv, {
  37041. opacity: loadingOptions.style.opacity || 0.5
  37042. }, {
  37043. duration: loadingOptions.showDuration || 0
  37044. });
  37045. }
  37046. }
  37047. chart.loadingShown = true;
  37048. setLoadingSize();
  37049. }
  37050. /**
  37051. * Hide the loading layer.
  37052. *
  37053. * @see Highcharts.Chart#showLoading
  37054. *
  37055. * @sample highcharts/members/chart-hideloading/
  37056. * Show and hide loading from a button
  37057. * @sample stock/members/chart-show-hide-loading/
  37058. * Toggle loading in Highcharts Stock
  37059. *
  37060. * @function Highcharts.Chart#hideLoading
  37061. */
  37062. hideLoading() {
  37063. const options = this.options, loadingDiv = this.loadingDiv;
  37064. if (loadingDiv) {
  37065. loadingDiv.className =
  37066. 'highcharts-loading highcharts-loading-hidden';
  37067. if (!this.styledMode) {
  37068. animate(loadingDiv, {
  37069. opacity: 0
  37070. }, {
  37071. duration: options.loading.hideDuration || 100,
  37072. complete: function () {
  37073. css(loadingDiv, { display: 'none' });
  37074. }
  37075. });
  37076. }
  37077. }
  37078. this.loadingShown = false;
  37079. }
  37080. /**
  37081. * A generic function to update any element of the chart. Elements can be
  37082. * enabled and disabled, moved, re-styled, re-formatted etc.
  37083. *
  37084. * A special case is configuration objects that take arrays, for example
  37085. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  37086. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  37087. * [series](https://api.highcharts.com/highcharts/series). For these
  37088. * collections, an `id` option is used to map the new option set to an
  37089. * existing object. If an existing object of the same id is not found, the
  37090. * corresponding item is updated. So for example, running `chart.update`
  37091. * with a series item without an id, will cause the existing chart's series
  37092. * with the same index in the series array to be updated. When the
  37093. * `oneToOne` parameter is true, `chart.update` will also take care of
  37094. * adding and removing items from the collection. Read more under the
  37095. * parameter description below.
  37096. *
  37097. * Note that when changing series data, `chart.update` may mutate the passed
  37098. * data options.
  37099. *
  37100. * See also the
  37101. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  37102. * Switching between `responsive.rules` basically runs `chart.update` under
  37103. * the hood.
  37104. *
  37105. * @sample highcharts/members/chart-update/
  37106. * Update chart geometry
  37107. *
  37108. * @function Highcharts.Chart#update
  37109. *
  37110. * @param {Highcharts.Options} options
  37111. * A configuration object for the new chart options.
  37112. *
  37113. * @param {boolean} [redraw=true]
  37114. * Whether to redraw the chart.
  37115. *
  37116. * @param {boolean} [oneToOne=false]
  37117. * When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
  37118. * collections will be updated one to one, and items will be either
  37119. * added or removed to match the new updated options. For example,
  37120. * if the chart has two series and we call `chart.update` with a
  37121. * configuration containing three series, one will be added. If we
  37122. * call `chart.update` with one series, one will be removed. Setting
  37123. * an empty `series` array will remove all series, but leaving out
  37124. * the`series` property will leave all series untouched. If the
  37125. * series have id's, the new series options will be matched by id,
  37126. * and the remaining ones removed.
  37127. *
  37128. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  37129. * Whether to apply animation, and optionally animation
  37130. * configuration. When `undefined`, it applies the animation that is
  37131. * set in the `chart.animation` option.
  37132. *
  37133. * @emits Highcharts.Chart#event:update
  37134. * @emits Highcharts.Chart#event:afterUpdate
  37135. */
  37136. update(options, redraw, oneToOne, animation) {
  37137. const chart = this, adders = {
  37138. credits: 'addCredits',
  37139. title: 'setTitle',
  37140. subtitle: 'setSubtitle',
  37141. caption: 'setCaption'
  37142. }, isResponsiveOptions = options.isResponsiveOptions, itemsForRemoval = [];
  37143. let updateAllAxes, updateAllSeries, runSetSize;
  37144. fireEvent(chart, 'update', { options: options });
  37145. // If there are responsive rules in action, undo the responsive rules
  37146. // before we apply the updated options and replay the responsive rules
  37147. // on top from the chart.redraw function (#9617).
  37148. if (!isResponsiveOptions) {
  37149. chart.setResponsive(false, true);
  37150. }
  37151. options = diffObjects(options, chart.options);
  37152. chart.userOptions = merge(chart.userOptions, options);
  37153. // If the top-level chart option is present, some special updates are
  37154. // required
  37155. const optionsChart = options.chart;
  37156. if (optionsChart) {
  37157. merge(true, chart.options.chart, optionsChart);
  37158. // Add support for deprecated zooming options like zoomType, #17861
  37159. this.setZoomOptions();
  37160. // Setter function
  37161. if ('className' in optionsChart) {
  37162. chart.setClassName(optionsChart.className);
  37163. }
  37164. if ('inverted' in optionsChart ||
  37165. 'polar' in optionsChart ||
  37166. 'type' in optionsChart) {
  37167. // Parse options.chart.inverted and options.chart.polar together
  37168. // with the available series.
  37169. chart.propFromSeries();
  37170. updateAllAxes = true;
  37171. }
  37172. if ('alignTicks' in optionsChart) { // #6452
  37173. updateAllAxes = true;
  37174. }
  37175. if ('events' in optionsChart) {
  37176. // Chart event handlers
  37177. registerEventOptions(this, optionsChart);
  37178. }
  37179. objectEach(optionsChart, function (val, key) {
  37180. if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  37181. -1) {
  37182. updateAllSeries = true;
  37183. }
  37184. // Only dirty box
  37185. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  37186. chart.isDirtyBox = true;
  37187. }
  37188. // Chart setSize
  37189. if (chart.propsRequireReflow.indexOf(key) !== -1) {
  37190. if (isResponsiveOptions) {
  37191. chart.isDirtyBox = true;
  37192. }
  37193. else {
  37194. runSetSize = true;
  37195. }
  37196. }
  37197. });
  37198. if (!chart.styledMode && optionsChart.style) {
  37199. chart.renderer.setStyle(chart.options.chart.style || {});
  37200. }
  37201. }
  37202. // Moved up, because tooltip needs updated plotOptions (#6218)
  37203. if (!chart.styledMode && options.colors) {
  37204. this.options.colors = options.colors;
  37205. }
  37206. if (options.time) {
  37207. // Maintaining legacy global time. If the chart is instanciated
  37208. // first with global time, then updated with time options, we need
  37209. // to create a new Time instance to avoid mutating the global time
  37210. // (#10536).
  37211. if (this.time === defaultTime) {
  37212. this.time = new Time(options.time);
  37213. }
  37214. // If we're updating, the time class is different from other chart
  37215. // classes (chart.legend, chart.tooltip etc) in that it doesn't know
  37216. // about the chart. The other chart[something].update functions also
  37217. // set the chart.options[something]. For the time class however we
  37218. // need to update the chart options separately. #14230.
  37219. merge(true, chart.options.time, options.time);
  37220. }
  37221. // Some option stuctures correspond one-to-one to chart objects that
  37222. // have update methods, for example
  37223. // options.credits => chart.credits
  37224. // options.legend => chart.legend
  37225. // options.title => chart.title
  37226. // options.tooltip => chart.tooltip
  37227. // options.subtitle => chart.subtitle
  37228. // options.mapNavigation => chart.mapNavigation
  37229. // options.navigator => chart.navigator
  37230. // options.scrollbar => chart.scrollbar
  37231. objectEach(options, function (val, key) {
  37232. if (chart[key] &&
  37233. typeof chart[key].update === 'function') {
  37234. chart[key].update(val, false);
  37235. // If a one-to-one object does not exist, look for an adder function
  37236. }
  37237. else if (typeof chart[adders[key]] === 'function') {
  37238. chart[adders[key]](val);
  37239. // Else, just merge the options. For nodes like loading, noData,
  37240. // plotOptions
  37241. }
  37242. else if (key !== 'colors' &&
  37243. chart.collectionsWithUpdate.indexOf(key) === -1) {
  37244. merge(true, chart.options[key], options[key]);
  37245. }
  37246. if (key !== 'chart' &&
  37247. chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
  37248. updateAllSeries = true;
  37249. }
  37250. });
  37251. // Setters for collections. For axes and series, each item is referred
  37252. // by an id. If the id is not found, it defaults to the corresponding
  37253. // item in the collection, so setting one series without an id, will
  37254. // update the first series in the chart. Setting two series without
  37255. // an id will update the first and the second respectively (#6019)
  37256. // chart.update and responsive.
  37257. this.collectionsWithUpdate.forEach(function (coll) {
  37258. if (options[coll]) {
  37259. splat(options[coll]).forEach(function (newOptions, i) {
  37260. const hasId = defined(newOptions.id);
  37261. let item;
  37262. // Match by id
  37263. if (hasId) {
  37264. item = chart.get(newOptions.id);
  37265. }
  37266. // No match by id found, match by index instead
  37267. if (!item && chart[coll]) {
  37268. item = chart[coll][pick(newOptions.index, i)];
  37269. // Check if we grabbed an item with an exising but
  37270. // different id (#13541). Check that the item in this
  37271. // position is not internal (navigator).
  37272. if (item && ((hasId && defined(item.options.id)) ||
  37273. item.options.isInternal)) {
  37274. item = void 0;
  37275. }
  37276. }
  37277. if (item && item.coll === coll) {
  37278. item.update(newOptions, false);
  37279. if (oneToOne) {
  37280. item.touched = true;
  37281. }
  37282. }
  37283. // If oneToOne and no matching item is found, add one
  37284. if (!item && oneToOne && chart.collectionsWithInit[coll]) {
  37285. chart.collectionsWithInit[coll][0].apply(chart,
  37286. // [newOptions, ...extraArguments, redraw=false]
  37287. [
  37288. newOptions
  37289. ].concat(
  37290. // Not all initializers require extra args
  37291. chart.collectionsWithInit[coll][1] || []).concat([
  37292. false
  37293. ])).touched = true;
  37294. }
  37295. });
  37296. // Add items for removal
  37297. if (oneToOne) {
  37298. chart[coll].forEach(function (item) {
  37299. if (!item.touched && !item.options.isInternal) {
  37300. itemsForRemoval.push(item);
  37301. }
  37302. else {
  37303. delete item.touched;
  37304. }
  37305. });
  37306. }
  37307. }
  37308. });
  37309. itemsForRemoval.forEach(function (item) {
  37310. if (item.chart && item.remove) { // #9097, avoid removing twice
  37311. item.remove(false);
  37312. }
  37313. });
  37314. if (updateAllAxes) {
  37315. chart.axes.forEach(function (axis) {
  37316. axis.update({}, false);
  37317. });
  37318. }
  37319. // Certain options require the whole series structure to be thrown away
  37320. // and rebuilt
  37321. if (updateAllSeries) {
  37322. chart.getSeriesOrderByLinks().forEach(function (series) {
  37323. // Avoid removed navigator series
  37324. if (series.chart) {
  37325. series.update({}, false);
  37326. }
  37327. }, this);
  37328. }
  37329. // Update size. Redraw is forced.
  37330. const newWidth = optionsChart && optionsChart.width;
  37331. const newHeight = optionsChart && (isString(optionsChart.height) ?
  37332. relativeLength(optionsChart.height, newWidth || chart.chartWidth) :
  37333. optionsChart.height);
  37334. if (
  37335. // In this case, run chart.setSize with newWidth and newHeight which
  37336. // are undefined, only for reflowing chart elements because margin
  37337. // or spacing has been set (#8190)
  37338. runSetSize ||
  37339. // In this case, the size is actually set
  37340. (isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  37341. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  37342. chart.setSize(newWidth, newHeight, animation);
  37343. }
  37344. else if (pick(redraw, true)) {
  37345. chart.redraw(animation);
  37346. }
  37347. fireEvent(chart, 'afterUpdate', {
  37348. options: options,
  37349. redraw: redraw,
  37350. animation: animation
  37351. });
  37352. }
  37353. /**
  37354. * Shortcut to set the subtitle options. This can also be done from {@link
  37355. * Chart#update} or {@link Chart#setTitle}.
  37356. *
  37357. * @function Highcharts.Chart#setSubtitle
  37358. *
  37359. * @param {Highcharts.SubtitleOptions} options
  37360. * New subtitle options. The subtitle text itself is set by the
  37361. * `options.text` property.
  37362. */
  37363. setSubtitle(options, redraw) {
  37364. this.applyDescription('subtitle', options);
  37365. this.layOutTitles(redraw);
  37366. }
  37367. /**
  37368. * Set the caption options. This can also be done from {@link
  37369. * Chart#update}.
  37370. *
  37371. * @function Highcharts.Chart#setCaption
  37372. *
  37373. * @param {Highcharts.CaptionOptions} options
  37374. * New caption options. The caption text itself is set by the
  37375. * `options.text` property.
  37376. */
  37377. setCaption(options, redraw) {
  37378. this.applyDescription('caption', options);
  37379. this.layOutTitles(redraw);
  37380. }
  37381. /**
  37382. * Display the zoom button, so users can reset zoom to the default view
  37383. * settings.
  37384. *
  37385. * @function Highcharts.Chart#showResetZoom
  37386. *
  37387. * @emits Highcharts.Chart#event:afterShowResetZoom
  37388. * @emits Highcharts.Chart#event:beforeShowResetZoom
  37389. */
  37390. showResetZoom() {
  37391. const chart = this, lang = defaultOptions.lang, btnOptions = chart.zooming.resetButton, theme = btnOptions.theme, alignTo = (btnOptions.relativeTo === 'chart' ||
  37392. btnOptions.relativeTo === 'spacingBox' ?
  37393. null :
  37394. 'scrollablePlotBox');
  37395. /**
  37396. * @private
  37397. */
  37398. function zoomOut() {
  37399. chart.zoomOut();
  37400. }
  37401. fireEvent(this, 'beforeShowResetZoom', null, function () {
  37402. chart.resetZoomButton = chart.renderer
  37403. .button(lang.resetZoom, null, null, zoomOut, theme)
  37404. .attr({
  37405. align: btnOptions.position.align,
  37406. title: lang.resetZoomTitle
  37407. })
  37408. .addClass('highcharts-reset-zoom')
  37409. .add()
  37410. .align(btnOptions.position, false, alignTo);
  37411. });
  37412. fireEvent(this, 'afterShowResetZoom');
  37413. }
  37414. /**
  37415. * Zoom the chart out after a user has zoomed in. See also
  37416. * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
  37417. *
  37418. * @function Highcharts.Chart#zoomOut
  37419. *
  37420. * @emits Highcharts.Chart#event:selection
  37421. */
  37422. zoomOut() {
  37423. fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
  37424. }
  37425. /**
  37426. * Zoom into a given portion of the chart given by axis coordinates.
  37427. *
  37428. * @private
  37429. * @function Highcharts.Chart#zoom
  37430. * @param {Highcharts.SelectEventObject} event
  37431. */
  37432. zoom(event) {
  37433. const chart = this, pointer = chart.pointer;
  37434. let displayButton = false, hasZoomed;
  37435. // If zoom is called with no arguments, reset the axes
  37436. if (!event || event.resetSelection) {
  37437. chart.axes.forEach(function (axis) {
  37438. hasZoomed = axis.zoom();
  37439. });
  37440. pointer.initiated = false; // #6804
  37441. }
  37442. else { // else, zoom in on all axes
  37443. event.xAxis.concat(event.yAxis).forEach(function (axisData) {
  37444. const axis = axisData.axis, isXAxis = axis.isXAxis;
  37445. // don't zoom more than minRange
  37446. if (pointer[isXAxis ? 'zoomX' : 'zoomY'] &&
  37447. (defined(pointer.mouseDownX) &&
  37448. defined(pointer.mouseDownY) &&
  37449. chart.isInsidePlot(pointer.mouseDownX - chart.plotLeft, pointer.mouseDownY - chart.plotTop, { axis })) || !defined(chart.inverted ? pointer.mouseDownX : pointer.mouseDownY)) {
  37450. hasZoomed = axis.zoom(axisData.min, axisData.max);
  37451. if (axis.displayBtn) {
  37452. displayButton = true;
  37453. }
  37454. }
  37455. });
  37456. }
  37457. // Show or hide the Reset zoom button
  37458. const resetZoomButton = chart.resetZoomButton;
  37459. if (displayButton && !resetZoomButton) {
  37460. chart.showResetZoom();
  37461. }
  37462. else if (!displayButton && isObject(resetZoomButton)) {
  37463. chart.resetZoomButton = resetZoomButton.destroy();
  37464. }
  37465. // Redraw
  37466. if (hasZoomed) {
  37467. chart.redraw(pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100));
  37468. }
  37469. }
  37470. /**
  37471. * Pan the chart by dragging the mouse across the pane. This function is
  37472. * called on mouse move, and the distance to pan is computed from chartX
  37473. * compared to the first chartX position in the dragging operation.
  37474. *
  37475. * @private
  37476. * @function Highcharts.Chart#pan
  37477. * @param {Highcharts.PointerEventObject} e
  37478. * @param {string} panning
  37479. */
  37480. pan(e, panning) {
  37481. const chart = this, hoverPoints = chart.hoverPoints, panningOptions = (typeof panning === 'object' ?
  37482. panning :
  37483. {
  37484. enabled: panning,
  37485. type: 'x'
  37486. }), chartOptions = chart.options.chart;
  37487. if (chartOptions && chartOptions.panning) {
  37488. chartOptions.panning = panningOptions;
  37489. }
  37490. const type = panningOptions.type;
  37491. let doRedraw;
  37492. fireEvent(this, 'pan', { originalEvent: e }, function () {
  37493. // remove active points for shared tooltip
  37494. if (hoverPoints) {
  37495. hoverPoints.forEach(function (point) {
  37496. point.setState();
  37497. });
  37498. }
  37499. let axes = chart.xAxis;
  37500. if (type === 'xy') {
  37501. axes = axes.concat(chart.yAxis);
  37502. }
  37503. else if (type === 'y') {
  37504. axes = chart.yAxis;
  37505. }
  37506. const nextMousePos = {};
  37507. axes.forEach(function (axis) {
  37508. if (!axis.options.panningEnabled || axis.options.isInternal) {
  37509. return;
  37510. }
  37511. 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) ||
  37512. (!axis.reversed && chart.inverted) ?
  37513. -1 :
  37514. 1, extremes = axis.getExtremes(), panMin = axis.toValue(startPos - mousePos, true) +
  37515. halfPointRange * pointRangeDirection, panMax = axis.toValue(startPos + axis.len - mousePos, true) -
  37516. ((halfPointRange * pointRangeDirection) ||
  37517. (axis.isXAxis && axis.pointRangePadding) ||
  37518. 0), flipped = panMax < panMin, hasVerticalPanning = axis.hasVerticalPanning();
  37519. let newMin = flipped ? panMax : panMin, newMax = flipped ? panMin : panMax, panningState = axis.panningState, spill;
  37520. // General calculations of panning state.
  37521. // This is related to using vertical panning. (#11315).
  37522. if (hasVerticalPanning &&
  37523. !axis.isXAxis && (!panningState || panningState.isDirty)) {
  37524. axis.series.forEach(function (series) {
  37525. const processedData = series.getProcessedData(true), dataExtremes = series.getExtremes(processedData.yData, true);
  37526. if (!panningState) {
  37527. panningState = {
  37528. startMin: Number.MAX_VALUE,
  37529. startMax: -Number.MAX_VALUE
  37530. };
  37531. }
  37532. if (isNumber(dataExtremes.dataMin) &&
  37533. isNumber(dataExtremes.dataMax)) {
  37534. panningState.startMin = Math.min(pick(series.options.threshold, Infinity), dataExtremes.dataMin, panningState.startMin);
  37535. panningState.startMax = Math.max(pick(series.options.threshold, -Infinity), dataExtremes.dataMax, panningState.startMax);
  37536. }
  37537. });
  37538. }
  37539. const paddedMin = Math.min(pick(panningState && panningState.startMin, extremes.dataMin), halfPointRange ?
  37540. extremes.min :
  37541. axis.toValue(axis.toPixels(extremes.min) -
  37542. axis.minPixelPadding));
  37543. const paddedMax = Math.max(pick(panningState && panningState.startMax, extremes.dataMax), halfPointRange ?
  37544. extremes.max :
  37545. axis.toValue(axis.toPixels(extremes.max) +
  37546. axis.minPixelPadding));
  37547. axis.panningState = panningState;
  37548. // It is not necessary to calculate extremes on ordinal axis,
  37549. // because they are already calculated, so we don't want to
  37550. // override them.
  37551. if (!axis.isOrdinal) {
  37552. // If the new range spills over, either to the min or max,
  37553. // adjust the new range.
  37554. spill = paddedMin - newMin;
  37555. if (spill > 0) {
  37556. newMax += spill;
  37557. newMin = paddedMin;
  37558. }
  37559. spill = newMax - paddedMax;
  37560. if (spill > 0) {
  37561. newMax = paddedMax;
  37562. newMin -= spill;
  37563. }
  37564. // Set new extremes if they are actually new
  37565. if (axis.series.length &&
  37566. newMin !== extremes.min &&
  37567. newMax !== extremes.max &&
  37568. newMin >= paddedMin &&
  37569. newMax <= paddedMax) {
  37570. axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' });
  37571. if (!chart.resetZoomButton &&
  37572. // Show reset zoom button only when both newMin and
  37573. // newMax values are between padded axis range.
  37574. newMin !== paddedMin &&
  37575. newMax !== paddedMax &&
  37576. type.match('y')) {
  37577. chart.showResetZoom();
  37578. axis.displayBtn = false;
  37579. }
  37580. doRedraw = true;
  37581. }
  37582. // set new reference for next run:
  37583. nextMousePos[mouseDown] = mousePos;
  37584. }
  37585. });
  37586. objectEach(nextMousePos, (pos, down) => {
  37587. chart[down] = pos;
  37588. });
  37589. if (doRedraw) {
  37590. chart.redraw(false);
  37591. }
  37592. css(chart.container, { cursor: 'move' });
  37593. });
  37594. }
  37595. }
  37596. extend(Chart.prototype, {
  37597. // Hook for adding callbacks in modules
  37598. callbacks: [],
  37599. /**
  37600. * These collections (arrays) implement `Chart.addSomethig` method used in
  37601. * chart.update() to create new object in the collection. Equivalent for
  37602. * deleting is resolved by simple `Somethig.remove()`.
  37603. *
  37604. * Note: We need to define these references after initializers are bound to
  37605. * chart's prototype.
  37606. *
  37607. * @private
  37608. */
  37609. collectionsWithInit: {
  37610. // collectionName: [ initializingMethod, [extraArguments] ]
  37611. xAxis: [Chart.prototype.addAxis, [true]],
  37612. yAxis: [Chart.prototype.addAxis, [false]],
  37613. series: [Chart.prototype.addSeries]
  37614. },
  37615. /**
  37616. * These collections (arrays) implement update() methods with support for
  37617. * one-to-one option.
  37618. * @private
  37619. */
  37620. collectionsWithUpdate: [
  37621. 'xAxis',
  37622. 'yAxis',
  37623. 'series'
  37624. ],
  37625. /**
  37626. * These properties cause isDirtyBox to be set to true when updating. Can be
  37627. * extended from plugins.
  37628. * @private
  37629. */
  37630. propsRequireDirtyBox: [
  37631. 'backgroundColor',
  37632. 'borderColor',
  37633. 'borderWidth',
  37634. 'borderRadius',
  37635. 'plotBackgroundColor',
  37636. 'plotBackgroundImage',
  37637. 'plotBorderColor',
  37638. 'plotBorderWidth',
  37639. 'plotShadow',
  37640. 'shadow'
  37641. ],
  37642. /**
  37643. * These properties require a full reflow of chart elements, best
  37644. * implemented through running `Chart.setSize` internally (#8190).
  37645. * @private
  37646. */
  37647. propsRequireReflow: [
  37648. 'margin',
  37649. 'marginTop',
  37650. 'marginRight',
  37651. 'marginBottom',
  37652. 'marginLeft',
  37653. 'spacing',
  37654. 'spacingTop',
  37655. 'spacingRight',
  37656. 'spacingBottom',
  37657. 'spacingLeft'
  37658. ],
  37659. /**
  37660. * These properties cause all series to be updated when updating. Can be
  37661. * extended from plugins.
  37662. * @private
  37663. */
  37664. propsRequireUpdateSeries: [
  37665. 'chart.inverted',
  37666. 'chart.polar',
  37667. 'chart.ignoreHiddenSeries',
  37668. 'chart.type',
  37669. 'colors',
  37670. 'plotOptions',
  37671. 'time',
  37672. 'tooltip'
  37673. ]
  37674. });
  37675. /* *
  37676. *
  37677. * Default Export
  37678. *
  37679. * */
  37680. /* *
  37681. *
  37682. * API Declarations
  37683. *
  37684. * */
  37685. /**
  37686. * Callback for chart constructors.
  37687. *
  37688. * @callback Highcharts.ChartCallbackFunction
  37689. *
  37690. * @param {Highcharts.Chart} chart
  37691. * Created chart.
  37692. */
  37693. /**
  37694. * Format a number and return a string based on input settings.
  37695. *
  37696. * @callback Highcharts.NumberFormatterCallbackFunction
  37697. *
  37698. * @param {number} number
  37699. * The input number to format.
  37700. *
  37701. * @param {number} decimals
  37702. * The amount of decimals. A value of -1 preserves the amount in the
  37703. * input number.
  37704. *
  37705. * @param {string} [decimalPoint]
  37706. * The decimal point, defaults to the one given in the lang options, or
  37707. * a dot.
  37708. *
  37709. * @param {string} [thousandsSep]
  37710. * The thousands separator, defaults to the one given in the lang
  37711. * options, or a space character.
  37712. *
  37713. * @return {string} The formatted number.
  37714. */
  37715. /**
  37716. * The chart title. The title has an `update` method that allows modifying the
  37717. * options directly or indirectly via `chart.update`.
  37718. *
  37719. * @interface Highcharts.TitleObject
  37720. * @extends Highcharts.SVGElement
  37721. */ /**
  37722. * Modify options for the title.
  37723. *
  37724. * @function Highcharts.TitleObject#update
  37725. *
  37726. * @param {Highcharts.TitleOptions} titleOptions
  37727. * Options to modify.
  37728. *
  37729. * @param {boolean} [redraw=true]
  37730. * Whether to redraw the chart after the title is altered. If doing more
  37731. * operations on the chart, it is a good idea to set redraw to false and
  37732. * call {@link Chart#redraw} after.
  37733. */
  37734. /**
  37735. * The chart subtitle. The subtitle has an `update` method that
  37736. * allows modifying the options directly or indirectly via
  37737. * `chart.update`.
  37738. *
  37739. * @interface Highcharts.SubtitleObject
  37740. * @extends Highcharts.SVGElement
  37741. */ /**
  37742. * Modify options for the subtitle.
  37743. *
  37744. * @function Highcharts.SubtitleObject#update
  37745. *
  37746. * @param {Highcharts.SubtitleOptions} subtitleOptions
  37747. * Options to modify.
  37748. *
  37749. * @param {boolean} [redraw=true]
  37750. * Whether to redraw the chart after the subtitle is altered. If doing
  37751. * more operations on the chart, it is a good idea to set redraw to false
  37752. * and call {@link Chart#redraw} after.
  37753. */
  37754. /**
  37755. * The chart caption. The caption has an `update` method that
  37756. * allows modifying the options directly or indirectly via
  37757. * `chart.update`.
  37758. *
  37759. * @interface Highcharts.CaptionObject
  37760. * @extends Highcharts.SVGElement
  37761. */ /**
  37762. * Modify options for the caption.
  37763. *
  37764. * @function Highcharts.CaptionObject#update
  37765. *
  37766. * @param {Highcharts.CaptionOptions} captionOptions
  37767. * Options to modify.
  37768. *
  37769. * @param {boolean} [redraw=true]
  37770. * Whether to redraw the chart after the caption is altered. If doing
  37771. * more operations on the chart, it is a good idea to set redraw to false
  37772. * and call {@link Chart#redraw} after.
  37773. */
  37774. /**
  37775. * @interface Highcharts.ChartIsInsideOptionsObject
  37776. */ /**
  37777. * @name Highcharts.ChartIsInsideOptionsObject#axis
  37778. * @type {Highcharts.Axis|undefined}
  37779. */ /**
  37780. * @name Highcharts.ChartIsInsideOptionsObject#ignoreX
  37781. * @type {boolean|undefined}
  37782. */ /**
  37783. * @name Highcharts.ChartIsInsideOptionsObject#ignoreY
  37784. * @type {boolean|undefined}
  37785. */ /**
  37786. * @name Highcharts.ChartIsInsideOptionsObject#inverted
  37787. * @type {boolean|undefined}
  37788. */ /**
  37789. * @name Highcharts.ChartIsInsideOptionsObject#paneCoordinates
  37790. * @type {boolean|undefined}
  37791. */ /**
  37792. * @name Highcharts.ChartIsInsideOptionsObject#series
  37793. * @type {Highcharts.Series|undefined}
  37794. */ /**
  37795. * @name Highcharts.ChartIsInsideOptionsObject#visiblePlotOnly
  37796. * @type {boolean|undefined}
  37797. */
  37798. ''; // keeps doclets above in JS file
  37799. return Chart;
  37800. });
  37801. _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) {
  37802. /* *
  37803. *
  37804. * (c) 2010-2021 Torstein Honsi
  37805. *
  37806. * License: www.highcharts.com/license
  37807. *
  37808. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37809. *
  37810. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  37811. * horizontally on mobile devices. Supports left and right side axes.
  37812. */
  37813. /*
  37814. WIP on vertical scrollable plot area (#9378). To do:
  37815. - Bottom axis positioning
  37816. - Test with Gantt
  37817. - Look for size optimizing the code
  37818. - API and demos
  37819. */
  37820. const { stop } = A;
  37821. const { addEvent, createElement, defined, merge, pick } = U;
  37822. /* eslint-disable no-invalid-this, valid-jsdoc */
  37823. addEvent(Chart, 'afterSetChartSize', function (e) {
  37824. let scrollablePlotArea = this.options.chart.scrollablePlotArea, scrollableMinWidth = scrollablePlotArea && scrollablePlotArea.minWidth, scrollableMinHeight = scrollablePlotArea && scrollablePlotArea.minHeight, scrollablePixelsX, scrollablePixelsY, corrections;
  37825. if (!this.renderer.forExport) {
  37826. // The amount of pixels to scroll, the difference between chart
  37827. // width and scrollable width
  37828. if (scrollableMinWidth) {
  37829. this.scrollablePixelsX = scrollablePixelsX = Math.max(0, scrollableMinWidth - this.chartWidth);
  37830. if (scrollablePixelsX) {
  37831. this.scrollablePlotBox = (this.renderer.scrollablePlotBox = merge(this.plotBox));
  37832. this.plotBox.width = this.plotWidth += scrollablePixelsX;
  37833. if (this.inverted) {
  37834. this.clipBox.height += scrollablePixelsX;
  37835. }
  37836. else {
  37837. this.clipBox.width += scrollablePixelsX;
  37838. }
  37839. corrections = {
  37840. // Corrections for right side
  37841. 1: { name: 'right', value: scrollablePixelsX }
  37842. };
  37843. }
  37844. // Currently we can only do either X or Y
  37845. }
  37846. else if (scrollableMinHeight) {
  37847. this.scrollablePixelsY = scrollablePixelsY = Math.max(0, scrollableMinHeight - this.chartHeight);
  37848. if (defined(scrollablePixelsY)) {
  37849. this.scrollablePlotBox = (this.renderer.scrollablePlotBox = merge(this.plotBox));
  37850. this.plotBox.height = this.plotHeight += scrollablePixelsY;
  37851. if (this.inverted) {
  37852. this.clipBox.width += scrollablePixelsY;
  37853. }
  37854. else {
  37855. this.clipBox.height += scrollablePixelsY;
  37856. }
  37857. corrections = {
  37858. 2: { name: 'bottom', value: scrollablePixelsY }
  37859. };
  37860. }
  37861. }
  37862. if (corrections && !e.skipAxes) {
  37863. this.axes.forEach(function (axis) {
  37864. // For right and bottom axes, only fix the plot line length
  37865. if (corrections[axis.side]) {
  37866. // Get the plot lines right in getPlotLinePath,
  37867. // temporarily set it to the adjusted plot width.
  37868. axis.getPlotLinePath = function () {
  37869. let marginName = corrections[axis.side].name, correctionValue = corrections[axis.side].value,
  37870. // axis.right or axis.bottom
  37871. margin = this[marginName], path;
  37872. // Temporarily adjust
  37873. this[marginName] = margin - correctionValue;
  37874. path = Axis.prototype.getPlotLinePath.apply(this, arguments);
  37875. // Reset
  37876. this[marginName] = margin;
  37877. return path;
  37878. };
  37879. }
  37880. else {
  37881. // Apply the corrected plotWidth
  37882. axis.setAxisSize();
  37883. axis.setAxisTranslation();
  37884. }
  37885. });
  37886. }
  37887. }
  37888. });
  37889. addEvent(Chart, 'render', function () {
  37890. if (this.scrollablePixelsX || this.scrollablePixelsY) {
  37891. if (this.setUpScrolling) {
  37892. this.setUpScrolling();
  37893. }
  37894. this.applyFixed();
  37895. }
  37896. else if (this.fixedDiv) { // Has been in scrollable mode
  37897. this.applyFixed();
  37898. }
  37899. });
  37900. /**
  37901. * @private
  37902. * @function Highcharts.Chart#setUpScrolling
  37903. * @return {void}
  37904. */
  37905. Chart.prototype.setUpScrolling = function () {
  37906. const css = {
  37907. WebkitOverflowScrolling: 'touch',
  37908. overflowX: 'hidden',
  37909. overflowY: 'hidden'
  37910. };
  37911. if (this.scrollablePixelsX) {
  37912. css.overflowX = 'auto';
  37913. }
  37914. if (this.scrollablePixelsY) {
  37915. css.overflowY = 'auto';
  37916. }
  37917. // Insert a container with position relative
  37918. // that scrolling and fixed container renders to (#10555)
  37919. this.scrollingParent = createElement('div', {
  37920. className: 'highcharts-scrolling-parent'
  37921. }, {
  37922. position: 'relative'
  37923. }, this.renderTo);
  37924. // Add the necessary divs to provide scrolling
  37925. this.scrollingContainer = createElement('div', {
  37926. 'className': 'highcharts-scrolling'
  37927. }, css, this.scrollingParent);
  37928. // On scroll, reset the chart position because it applies to the scrolled
  37929. // container
  37930. let lastHoverPoint;
  37931. addEvent(this.scrollingContainer, 'scroll', () => {
  37932. if (this.pointer) {
  37933. delete this.pointer.chartPosition;
  37934. if (this.hoverPoint) {
  37935. lastHoverPoint = this.hoverPoint;
  37936. }
  37937. this.pointer.runPointActions(void 0, lastHoverPoint, true);
  37938. }
  37939. });
  37940. this.innerContainer = createElement('div', {
  37941. 'className': 'highcharts-inner-container'
  37942. }, null, this.scrollingContainer);
  37943. // Now move the container inside
  37944. this.innerContainer.appendChild(this.container);
  37945. // Don't run again
  37946. this.setUpScrolling = null;
  37947. };
  37948. /**
  37949. * These elements are moved over to the fixed renderer and stay fixed when the
  37950. * user scrolls the chart
  37951. * @private
  37952. */
  37953. Chart.prototype.moveFixedElements = function () {
  37954. let container = this.container, fixedRenderer = this.fixedRenderer, fixedSelectors = [
  37955. '.highcharts-breadcrumbs-group',
  37956. '.highcharts-contextbutton',
  37957. '.highcharts-credits',
  37958. '.highcharts-legend',
  37959. '.highcharts-legend-checkbox',
  37960. '.highcharts-navigator-series',
  37961. '.highcharts-navigator-xaxis',
  37962. '.highcharts-navigator-yaxis',
  37963. '.highcharts-navigator',
  37964. '.highcharts-reset-zoom',
  37965. '.highcharts-drillup-button',
  37966. '.highcharts-scrollbar',
  37967. '.highcharts-subtitle',
  37968. '.highcharts-title'
  37969. ], axisClass;
  37970. if (this.scrollablePixelsX && !this.inverted) {
  37971. axisClass = '.highcharts-yaxis';
  37972. }
  37973. else if (this.scrollablePixelsX && this.inverted) {
  37974. axisClass = '.highcharts-xaxis';
  37975. }
  37976. else if (this.scrollablePixelsY && !this.inverted) {
  37977. axisClass = '.highcharts-xaxis';
  37978. }
  37979. else if (this.scrollablePixelsY && this.inverted) {
  37980. axisClass = '.highcharts-yaxis';
  37981. }
  37982. if (axisClass) {
  37983. fixedSelectors.push(`${axisClass}:not(.highcharts-radial-axis)`, `${axisClass}-labels:not(.highcharts-radial-axis-labels)`);
  37984. }
  37985. fixedSelectors.forEach(function (className) {
  37986. [].forEach.call(container.querySelectorAll(className), function (elem) {
  37987. (elem.namespaceURI === fixedRenderer.SVG_NS ?
  37988. fixedRenderer.box :
  37989. fixedRenderer.box.parentNode).appendChild(elem);
  37990. elem.style.pointerEvents = 'auto';
  37991. });
  37992. });
  37993. };
  37994. /**
  37995. * @private
  37996. * @function Highcharts.Chart#applyFixed
  37997. * @return {void}
  37998. */
  37999. Chart.prototype.applyFixed = function () {
  38000. const firstTime = !this.fixedDiv, chartOptions = this.options.chart, scrollableOptions = chartOptions.scrollablePlotArea, Renderer = RendererRegistry.getRendererType();
  38001. let fixedRenderer, scrollableWidth, scrollableHeight;
  38002. // First render
  38003. if (firstTime) {
  38004. this.fixedDiv = createElement('div', {
  38005. className: 'highcharts-fixed'
  38006. }, {
  38007. position: 'absolute',
  38008. overflow: 'hidden',
  38009. pointerEvents: 'none',
  38010. zIndex: (chartOptions.style && chartOptions.style.zIndex || 0) + 2,
  38011. top: 0
  38012. }, null, true);
  38013. if (this.scrollingContainer) {
  38014. this.scrollingContainer.parentNode.insertBefore(this.fixedDiv, this.scrollingContainer);
  38015. }
  38016. this.renderTo.style.overflow = 'visible';
  38017. this.fixedRenderer = fixedRenderer = new Renderer(this.fixedDiv, this.chartWidth, this.chartHeight, this.options.chart.style);
  38018. // Mask
  38019. this.scrollableMask = fixedRenderer
  38020. .path()
  38021. .attr({
  38022. fill: this.options.chart.backgroundColor || '#fff',
  38023. 'fill-opacity': pick(scrollableOptions.opacity, 0.85),
  38024. zIndex: -1
  38025. })
  38026. .addClass('highcharts-scrollable-mask')
  38027. .add();
  38028. addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
  38029. addEvent(this, 'afterApplyDrilldown', this.moveFixedElements);
  38030. addEvent(this, 'afterLayOutTitles', this.moveFixedElements);
  38031. }
  38032. else {
  38033. // Set the size of the fixed renderer to the visible width
  38034. this.fixedRenderer.setSize(this.chartWidth, this.chartHeight);
  38035. }
  38036. if (this.scrollableDirty || firstTime) {
  38037. this.scrollableDirty = false;
  38038. this.moveFixedElements();
  38039. }
  38040. // Increase the size of the scrollable renderer and background
  38041. scrollableWidth = this.chartWidth + (this.scrollablePixelsX || 0);
  38042. scrollableHeight = this.chartHeight + (this.scrollablePixelsY || 0);
  38043. stop(this.container);
  38044. this.container.style.width = scrollableWidth + 'px';
  38045. this.container.style.height = scrollableHeight + 'px';
  38046. this.renderer.boxWrapper.attr({
  38047. width: scrollableWidth,
  38048. height: scrollableHeight,
  38049. viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
  38050. });
  38051. this.chartBackground.attr({
  38052. width: scrollableWidth,
  38053. height: scrollableHeight
  38054. });
  38055. this.scrollingContainer.style.height = this.chartHeight + 'px';
  38056. // Set scroll position
  38057. if (firstTime) {
  38058. if (scrollableOptions.scrollPositionX) {
  38059. this.scrollingContainer.scrollLeft =
  38060. this.scrollablePixelsX *
  38061. scrollableOptions.scrollPositionX;
  38062. }
  38063. if (scrollableOptions.scrollPositionY) {
  38064. this.scrollingContainer.scrollTop =
  38065. this.scrollablePixelsY *
  38066. scrollableOptions.scrollPositionY;
  38067. }
  38068. }
  38069. // Mask behind the left and right side
  38070. 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 -
  38071. (this.scrollablePixelsX || 0), maskPlotBottom = this.plotTop + this.plotHeight -
  38072. (this.scrollablePixelsY || 0), d;
  38073. if (this.scrollablePixelsX) {
  38074. d = [
  38075. // Left side
  38076. ['M', 0, maskTop],
  38077. ['L', this.plotLeft - 1, maskTop],
  38078. ['L', this.plotLeft - 1, maskBottom],
  38079. ['L', 0, maskBottom],
  38080. ['Z'],
  38081. // Right side
  38082. ['M', maskPlotRight, maskTop],
  38083. ['L', this.chartWidth, maskTop],
  38084. ['L', this.chartWidth, maskBottom],
  38085. ['L', maskPlotRight, maskBottom],
  38086. ['Z']
  38087. ];
  38088. }
  38089. else if (this.scrollablePixelsY) {
  38090. d = [
  38091. // Top side
  38092. ['M', maskLeft, 0],
  38093. ['L', maskLeft, this.plotTop - 1],
  38094. ['L', maskRight, this.plotTop - 1],
  38095. ['L', maskRight, 0],
  38096. ['Z'],
  38097. // Bottom side
  38098. ['M', maskLeft, maskPlotBottom],
  38099. ['L', maskLeft, this.chartHeight],
  38100. ['L', maskRight, this.chartHeight],
  38101. ['L', maskRight, maskPlotBottom],
  38102. ['Z']
  38103. ];
  38104. }
  38105. else {
  38106. d = [['M', 0, 0]];
  38107. }
  38108. if (this.redrawTrigger !== 'adjustHeight') {
  38109. this.scrollableMask.attr({ d });
  38110. }
  38111. };
  38112. addEvent(Axis, 'afterInit', function () {
  38113. this.chart.scrollableDirty = true;
  38114. });
  38115. addEvent(Series, 'show', function () {
  38116. this.chart.scrollableDirty = true;
  38117. });
  38118. /* *
  38119. *
  38120. * API Declarations
  38121. *
  38122. * */
  38123. /**
  38124. * Options for a scrollable plot area. This feature provides a minimum size for
  38125. * the plot area of the chart. If the size gets smaller than this, typically
  38126. * on mobile devices, a native browser scrollbar is presented. This scrollbar
  38127. * provides smooth scrolling for the contents of the plot area, whereas the
  38128. * title, legend and unaffected axes are fixed.
  38129. *
  38130. * Since v7.1.2, a scrollable plot area can be defined for either horizontal or
  38131. * vertical scrolling, depending on whether the `minWidth` or `minHeight`
  38132. * option is set.
  38133. *
  38134. * @sample highcharts/chart/scrollable-plotarea
  38135. * Scrollable plot area
  38136. * @sample highcharts/chart/scrollable-plotarea-vertical
  38137. * Vertically scrollable plot area
  38138. * @sample {gantt} gantt/chart/scrollable-plotarea-vertical
  38139. * Gantt chart with vertically scrollable plot area
  38140. *
  38141. * @since 6.1.0
  38142. * @product highcharts gantt
  38143. * @apioption chart.scrollablePlotArea
  38144. */
  38145. /**
  38146. * The minimum height for the plot area. If it gets smaller than this, the plot
  38147. * area will become scrollable.
  38148. *
  38149. * @type {number}
  38150. * @since 7.1.2
  38151. * @apioption chart.scrollablePlotArea.minHeight
  38152. */
  38153. /**
  38154. * The minimum width for the plot area. If it gets smaller than this, the plot
  38155. * area will become scrollable.
  38156. *
  38157. * @type {number}
  38158. * @since 6.1.0
  38159. * @apioption chart.scrollablePlotArea.minWidth
  38160. */
  38161. /**
  38162. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  38163. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  38164. * Typically we would use 1 if the chart has right aligned Y axes.
  38165. *
  38166. * @type {number}
  38167. * @since 6.1.0
  38168. * @apioption chart.scrollablePlotArea.scrollPositionX
  38169. */
  38170. /**
  38171. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  38172. * 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
  38173. *
  38174. * @type {number}
  38175. * @since 7.1.2
  38176. * @apioption chart.scrollablePlotArea.scrollPositionY
  38177. */
  38178. /**
  38179. * The opacity of mask applied on one of the sides of the plot
  38180. * area.
  38181. *
  38182. * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
  38183. * Disabled opacity for the mask
  38184. *
  38185. * @type {number}
  38186. * @default 0.85
  38187. * @since 7.1.1
  38188. * @apioption chart.scrollablePlotArea.opacity
  38189. */
  38190. (''); // keep doclets above in transpiled file
  38191. });
  38192. _registerModule(_modules, 'Core/Axis/Stacking/StackItem.js', [_modules['Core/Templating.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (T, SeriesRegistry, U) {
  38193. /* *
  38194. *
  38195. * (c) 2010-2021 Torstein Honsi
  38196. *
  38197. * License: www.highcharts.com/license
  38198. *
  38199. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38200. *
  38201. * */
  38202. const { format } = T;
  38203. const { series: Series } = SeriesRegistry;
  38204. const { destroyObjectProperties, fireEvent, isNumber, merge, pick } = U;
  38205. /* *
  38206. *
  38207. * Class
  38208. *
  38209. * */
  38210. /**
  38211. * The class for stacks. Each stack, on a specific X value and either negative
  38212. * or positive, has its own stack item.
  38213. * @private
  38214. */
  38215. class StackItem {
  38216. /* *
  38217. *
  38218. * Constructor
  38219. *
  38220. * */
  38221. constructor(axis, options, negativeValue, x, stackOption) {
  38222. const inverted = axis.chart.inverted, reversed = axis.reversed;
  38223. this.axis = axis;
  38224. // The stack goes to the left either if the stack has negative value
  38225. // or when axis is reversed. XOR operator.
  38226. const isNegative = (this.isNegative = !!negativeValue !== !!reversed);
  38227. // Save the options to be able to style the label
  38228. this.options = options = options || {};
  38229. // Save the x value to be able to position the label later
  38230. this.x = x;
  38231. // Initialize total value
  38232. this.total = null;
  38233. this.cumulative = null;
  38234. // This will keep each points' extremes stored by series.index and point
  38235. // index
  38236. this.points = {};
  38237. this.hasValidPoints = false;
  38238. // Save the stack option on the series configuration object,
  38239. // and whether to treat it as percent
  38240. this.stack = stackOption;
  38241. this.leftCliff = 0;
  38242. this.rightCliff = 0;
  38243. // The align options and text align varies on whether the stack is
  38244. // negative and if the chart is inverted or not.
  38245. // First test the user supplied value, then use the dynamic.
  38246. this.alignOptions = {
  38247. align: options.align ||
  38248. (inverted ? (isNegative ? 'left' : 'right') : 'center'),
  38249. verticalAlign: options.verticalAlign ||
  38250. (inverted ? 'middle' : isNegative ? 'bottom' : 'top'),
  38251. y: options.y,
  38252. x: options.x
  38253. };
  38254. this.textAlign =
  38255. options.textAlign ||
  38256. (inverted ? (!isNegative ? 'left' : 'right') : 'center');
  38257. }
  38258. /**
  38259. * @private
  38260. */
  38261. destroy() {
  38262. destroyObjectProperties(this, this.axis);
  38263. }
  38264. /**
  38265. * Renders the stack total label and adds it to the stack label group.
  38266. * @private
  38267. */
  38268. render(group) {
  38269. const chart = this.axis.chart, options = this.options, formatOption = options.format,
  38270. // Format the text in the label.
  38271. str = formatOption ?
  38272. format(formatOption, this, chart) :
  38273. options.formatter.call(this);
  38274. // Change the text to reflect the new total and set visibility to hidden
  38275. // in case the serie is hidden
  38276. if (this.label) {
  38277. this.label.attr({ text: str, visibility: 'hidden' });
  38278. }
  38279. else {
  38280. // Create new label
  38281. this.label = chart.renderer.label(str, null, void 0, options.shape, void 0, void 0, options.useHTML, false, 'stack-labels');
  38282. const attr = {
  38283. r: options.borderRadius || 0,
  38284. text: str,
  38285. // set default padding to 5 as it is in datalabels #12308
  38286. padding: pick(options.padding, 5),
  38287. visibility: 'hidden' // hidden until setOffset is called
  38288. };
  38289. if (!chart.styledMode) {
  38290. attr.fill = options.backgroundColor;
  38291. attr.stroke = options.borderColor;
  38292. attr['stroke-width'] = options.borderWidth;
  38293. this.label.css(options.style || {});
  38294. }
  38295. this.label.attr(attr);
  38296. if (!this.label.added) {
  38297. this.label.add(group); // add to the labels-group
  38298. }
  38299. }
  38300. // Rank it higher than data labels (#8742)
  38301. this.label.labelrank = chart.plotSizeY;
  38302. fireEvent(this, 'afterRender');
  38303. }
  38304. /**
  38305. * Sets the offset that the stack has from the x value and repositions the
  38306. * label.
  38307. * @private
  38308. */
  38309. setOffset(xOffset, width, boxBottom, boxTop, defaultX, xAxis) {
  38310. const { alignOptions, axis, label, options, textAlign } = this, chart = axis.chart, stackBox = this.getStackBox({
  38311. xOffset,
  38312. width,
  38313. boxBottom,
  38314. boxTop,
  38315. defaultX,
  38316. xAxis
  38317. }), { verticalAlign } = alignOptions;
  38318. if (label && stackBox) {
  38319. const labelBox = label.getBBox(), padding = label.padding;
  38320. let isJustify = pick(options.overflow, 'justify') === 'justify', visible;
  38321. // Reset alignOptions property after justify #12337
  38322. alignOptions.x = options.x || 0;
  38323. alignOptions.y = options.y || 0;
  38324. // Calculate the adjusted Stack position, to take into consideration
  38325. // The size if the labelBox and vertical alignment as
  38326. // well as the text alignment. It's need to be done to work with
  38327. // default SVGLabel.align/justify methods.
  38328. const { x, y } = this.adjustStackPosition({
  38329. labelBox,
  38330. verticalAlign,
  38331. textAlign
  38332. });
  38333. stackBox.x -= x;
  38334. stackBox.y -= y;
  38335. // Align the label to the adjusted box.
  38336. label.align(alignOptions, false, stackBox);
  38337. // Check if label is inside the plotArea #12294
  38338. visible = chart.isInsidePlot(label.alignAttr.x + alignOptions.x + x, label.alignAttr.y + alignOptions.y + y);
  38339. if (!visible) {
  38340. isJustify = false;
  38341. }
  38342. if (isJustify) {
  38343. // Justify stackLabel into the alignBox
  38344. Series.prototype.justifyDataLabel.call(axis, label, alignOptions, label.alignAttr, labelBox, stackBox);
  38345. }
  38346. // Add attr to aviod the default animation of justifyDataLabel.
  38347. // Also add correct rotation with its rotation origin. #15129
  38348. label.attr({
  38349. x: label.alignAttr.x,
  38350. y: label.alignAttr.y,
  38351. rotation: options.rotation,
  38352. rotationOriginX: labelBox.width / 2,
  38353. rotationOriginY: labelBox.height / 2
  38354. });
  38355. // Check if the dataLabel should be visible.
  38356. if (pick(!isJustify && options.crop, true)) {
  38357. visible =
  38358. isNumber(label.x) &&
  38359. isNumber(label.y) &&
  38360. chart.isInsidePlot(label.x - padding + label.width, label.y) &&
  38361. chart.isInsidePlot(label.x + padding, label.y);
  38362. }
  38363. label[visible ? 'show' : 'hide']();
  38364. }
  38365. fireEvent(this, 'afterSetOffset', { xOffset, width });
  38366. }
  38367. /**
  38368. * Adjust the stack BBox position, to take into consideration the alignment
  38369. * of the dataLabel. This is necessary to make the stackDataLabel work with
  38370. * core methods like `SVGLabel.adjust` and `Series.justifyDataLabel`.
  38371. * @param AdjustStackPositionProps
  38372. * @return {{x: number, y: number}} Adjusted BBox position of the stack.
  38373. */
  38374. adjustStackPosition({ labelBox, verticalAlign, textAlign }) {
  38375. const factorMap = {
  38376. bottom: 0,
  38377. middle: 1,
  38378. top: 2,
  38379. right: 1,
  38380. center: 0,
  38381. left: -1
  38382. }, verticalAlignFactor = factorMap[verticalAlign], textAlignFactor = factorMap[textAlign];
  38383. return {
  38384. x: labelBox.width / 2 + (labelBox.width / 2) * textAlignFactor,
  38385. y: (labelBox.height / 2) * verticalAlignFactor
  38386. };
  38387. }
  38388. /**
  38389. * Get the bbox of the stack.
  38390. * @private
  38391. * @function Highcharts.StackItem#getStackBox
  38392. * @return {BBoxObject} The x, y, height, width of the stack.
  38393. */
  38394. getStackBox(stackBoxProps) {
  38395. const stackItem = this, axis = this.axis, chart = axis.chart, { boxTop, defaultX, xOffset, width, boxBottom } = stackBoxProps, totalStackValue = axis.stacking.usePercentage ?
  38396. 100 :
  38397. 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 ||
  38398. (isNumber(axis.min) &&
  38399. axis.logarithmic &&
  38400. axis.logarithmic.lin2log(axis.min)) ||
  38401. 0), height = Math.abs(y - yZero), inverted = chart.inverted, neg = stackItem.isNegative;
  38402. return inverted ?
  38403. {
  38404. x: (neg ? y : y - height) - chart.plotLeft,
  38405. y: xAxis.height - x - width,
  38406. width: height,
  38407. height: width
  38408. } : {
  38409. x: x + xAxis.transB - chart.plotLeft,
  38410. y: (neg ? y - height : y) - chart.plotTop,
  38411. width: width,
  38412. height: height
  38413. };
  38414. }
  38415. }
  38416. /* *
  38417. *
  38418. * Default Export
  38419. *
  38420. * */
  38421. /* *
  38422. *
  38423. * API Declarations
  38424. *
  38425. * */
  38426. /**
  38427. * Stack of data points
  38428. *
  38429. * @product highcharts
  38430. *
  38431. * @interface Highcharts.StackItemObject
  38432. */ /**
  38433. * Alignment settings
  38434. * @name Highcharts.StackItemObject#alignOptions
  38435. * @type {Highcharts.AlignObject}
  38436. */ /**
  38437. * Related axis
  38438. * @name Highcharts.StackItemObject#axis
  38439. * @type {Highcharts.Axis}
  38440. */ /**
  38441. * Cumulative value of the stacked data points
  38442. * @name Highcharts.StackItemObject#cumulative
  38443. * @type {number}
  38444. */ /**
  38445. * True if on the negative side
  38446. * @name Highcharts.StackItemObject#isNegative
  38447. * @type {boolean}
  38448. */ /**
  38449. * Related SVG element
  38450. * @name Highcharts.StackItemObject#label
  38451. * @type {Highcharts.SVGElement}
  38452. */ /**
  38453. * Related stack options
  38454. * @name Highcharts.StackItemObject#options
  38455. * @type {Highcharts.YAxisStackLabelsOptions}
  38456. */ /**
  38457. * Total value of the stacked data points
  38458. * @name Highcharts.StackItemObject#total
  38459. * @type {number}
  38460. */ /**
  38461. * Shared x value of the stack
  38462. * @name Highcharts.StackItemObject#x
  38463. * @type {number}
  38464. */
  38465. ''; // keeps doclets above in JS file
  38466. return StackItem;
  38467. });
  38468. _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) {
  38469. /* *
  38470. *
  38471. * (c) 2010-2021 Torstein Honsi
  38472. *
  38473. * License: www.highcharts.com/license
  38474. *
  38475. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38476. *
  38477. * */
  38478. const { getDeferredAnimation } = A;
  38479. const { series: { prototype: seriesProto } } = SeriesRegistry;
  38480. const { addEvent, correctFloat, defined, destroyObjectProperties, fireEvent, isArray, isNumber, objectEach, pick } = U;
  38481. /* *
  38482. *
  38483. * Functions
  38484. *
  38485. * */
  38486. /**
  38487. * Generate stacks for each series and calculate stacks total values
  38488. *
  38489. * @private
  38490. * @function Highcharts.Chart#getStacks
  38491. */
  38492. function chartGetStacks() {
  38493. const chart = this, inverted = chart.inverted;
  38494. // reset stacks for each yAxis
  38495. chart.yAxis.forEach((axis) => {
  38496. if (axis.stacking && axis.stacking.stacks && axis.hasVisibleSeries) {
  38497. axis.stacking.oldStacks = axis.stacking.stacks;
  38498. }
  38499. });
  38500. chart.series.forEach((series) => {
  38501. const xAxisOptions = series.xAxis && series.xAxis.options || {};
  38502. if (series.options.stacking &&
  38503. (series.visible === true ||
  38504. chart.options.chart.ignoreHiddenSeries === false)) {
  38505. series.stackKey = [
  38506. series.type,
  38507. pick(series.options.stack, ''),
  38508. inverted ? xAxisOptions.top : xAxisOptions.left,
  38509. inverted ? xAxisOptions.height : xAxisOptions.width
  38510. ].join(',');
  38511. }
  38512. });
  38513. }
  38514. /**
  38515. * @private
  38516. */
  38517. function onAxisDestroy() {
  38518. const stacking = this.stacking;
  38519. if (!stacking) {
  38520. return;
  38521. }
  38522. const stacks = stacking.stacks;
  38523. // Destroy each stack total
  38524. objectEach(stacks, function (stack, stackKey) {
  38525. destroyObjectProperties(stack);
  38526. stacks[stackKey] = null;
  38527. });
  38528. if (stacking &&
  38529. stacking.stackTotalGroup) {
  38530. stacking.stackTotalGroup.destroy();
  38531. }
  38532. }
  38533. /**
  38534. * @private
  38535. */
  38536. function onAxisInit() {
  38537. if (this.coll === 'yAxis' && !this.stacking) {
  38538. this.stacking = new AxisAdditions(this);
  38539. }
  38540. }
  38541. /**
  38542. * Get stack indicator, according to it's x-value, to determine points with the
  38543. * same x-value
  38544. *
  38545. * @private
  38546. * @function Highcharts.Series#getStackIndicator
  38547. */
  38548. function seriesGetStackIndicator(stackIndicator, x, index, key) {
  38549. // Update stack indicator, when:
  38550. // first point in a stack || x changed || stack type (negative vs positive)
  38551. // changed:
  38552. if (!defined(stackIndicator) ||
  38553. stackIndicator.x !== x ||
  38554. (key && stackIndicator.stackKey !== key)) {
  38555. stackIndicator = {
  38556. x: x,
  38557. index: 0,
  38558. key: key,
  38559. stackKey: key
  38560. };
  38561. }
  38562. else {
  38563. (stackIndicator).index++;
  38564. }
  38565. stackIndicator.key =
  38566. [index, x, stackIndicator.index].join(',');
  38567. return stackIndicator;
  38568. }
  38569. /**
  38570. * Iterate over all stacks and compute the absolute values to percent
  38571. *
  38572. * @private
  38573. * @function Highcharts.Series#modifyStacks
  38574. */
  38575. function seriesModifyStacks() {
  38576. const series = this, yAxis = series.yAxis, stackKey = series.stackKey, stacks = yAxis.stacking.stacks, processedXData = series.processedXData, stacking = series.options.stacking, stacker = series[stacking + 'Stacker'];
  38577. let stackIndicator;
  38578. if (stacker) { // Modifier function exists (Series.percentStacker etc.)
  38579. [stackKey, '-' + stackKey].forEach((key) => {
  38580. let i = processedXData.length, x, stack, pointExtremes;
  38581. while (i--) {
  38582. x = processedXData[i];
  38583. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index, key);
  38584. stack = stacks[key] && stacks[key][x];
  38585. pointExtremes =
  38586. stack && stack.points[stackIndicator.key];
  38587. if (pointExtremes) {
  38588. stacker.call(series, pointExtremes, stack, i);
  38589. }
  38590. }
  38591. });
  38592. }
  38593. }
  38594. /**
  38595. * Modifier function for percent stacks. Blows up the stack to 100%.
  38596. *
  38597. * @private
  38598. * @function Highcharts.Series#percentStacker
  38599. */
  38600. function seriesPercentStacker(pointExtremes, stack, i) {
  38601. const totalFactor = stack.total ? 100 / stack.total : 0;
  38602. // Y bottom value
  38603. pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
  38604. // Y value
  38605. pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
  38606. this.stackedYData[i] = pointExtremes[1];
  38607. }
  38608. /**
  38609. * Set grouped points in a stack-like object. When `centerInCategory` is true,
  38610. * and `stacking` is not enabled, we need a pseudo (horizontal) stack in order
  38611. * to handle grouping of points within the same category.
  38612. *
  38613. * @private
  38614. * @function Highcharts.Series#setStackedPoints
  38615. * @return {void}
  38616. */
  38617. function seriesSetGroupedPoints() {
  38618. const stacking = this.yAxis.stacking;
  38619. if (this.options.centerInCategory &&
  38620. (this.is('column') || this.is('columnrange')) &&
  38621. // With stacking enabled, we already have stacks that we can compute
  38622. // from
  38623. !this.options.stacking &&
  38624. // With only one series, we don't need to consider centerInCategory
  38625. this.chart.series.length > 1) {
  38626. seriesProto.setStackedPoints.call(this, 'group');
  38627. // After updating, if we now have proper stacks, we must delete the group
  38628. // pseudo stacks (#14986)
  38629. }
  38630. else if (stacking) {
  38631. objectEach(stacking.stacks, (type, key) => {
  38632. if (key.slice(-5) === 'group') {
  38633. objectEach(type, (stack) => stack.destroy());
  38634. delete stacking.stacks[key];
  38635. }
  38636. });
  38637. }
  38638. }
  38639. /**
  38640. * Adds series' points value to corresponding stack
  38641. *
  38642. * @private
  38643. * @function Highcharts.Series#setStackedPoints
  38644. */
  38645. function seriesSetStackedPoints(stackingParam) {
  38646. const chart = this.chart, stacking = stackingParam || this.options.stacking;
  38647. if (!stacking || (this.visible !== true &&
  38648. chart.options.chart.ignoreHiddenSeries !== false)) {
  38649. return;
  38650. }
  38651. 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' ?
  38652. chart.yAxis[0] :
  38653. series.yAxis, stacks = yAxis.stacking.stacks, oldStacks = yAxis.stacking.oldStacks;
  38654. let stackIndicator, isNegative, stack, other, key, pointKey, i, x, y;
  38655. yAxis.stacking.stacksTouched += 1;
  38656. // loop over the non-null y values and read them into a local array
  38657. for (i = 0; i < yDataLength; i++) {
  38658. x = xData[i];
  38659. y = yData[i];
  38660. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
  38661. pointKey = stackIndicator.key;
  38662. // Read stacked values into a stack based on the x value,
  38663. // the sign of y and the stack key. Stacking is also handled for null
  38664. // values (#739)
  38665. isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
  38666. key = isNegative ? negKey : stackKey;
  38667. // Create empty object for this stack if it doesn't exist yet
  38668. if (!stacks[key]) {
  38669. stacks[key] = {};
  38670. }
  38671. // Initialize StackItem for this x
  38672. if (!stacks[key][x]) {
  38673. if (oldStacks[key] &&
  38674. oldStacks[key][x]) {
  38675. stacks[key][x] = oldStacks[key][x];
  38676. stacks[key][x].total = null;
  38677. }
  38678. else {
  38679. stacks[key][x] = new StackItem(yAxis, yAxis.options.stackLabels, !!isNegative, x, stackOption);
  38680. }
  38681. }
  38682. // If the StackItem doesn't exist, create it first
  38683. stack = stacks[key][x];
  38684. if (y !== null) {
  38685. stack.points[pointKey] = stack.points[series.index] =
  38686. [pick(stack.cumulative, stackThreshold)];
  38687. // Record the base of the stack
  38688. if (!defined(stack.cumulative)) {
  38689. stack.base = pointKey;
  38690. }
  38691. stack.touched = yAxis.stacking.stacksTouched;
  38692. // In area charts, if there are multiple points on the same X value,
  38693. // let the area fill the full span of those points
  38694. if (stackIndicator.index > 0 && series.singleStacks === false) {
  38695. stack.points[pointKey][0] =
  38696. stack.points[series.index + ',' + x + ',0'][0];
  38697. }
  38698. // When updating to null, reset the point stack (#7493)
  38699. }
  38700. else {
  38701. stack.points[pointKey] = stack.points[series.index] =
  38702. null;
  38703. }
  38704. // Add value to the stack total
  38705. if (stacking === 'percent') {
  38706. // Percent stacked column, totals are the same for the positive and
  38707. // negative stacks
  38708. other = isNegative ? stackKey : negKey;
  38709. if (negStacks && stacks[other] && stacks[other][x]) {
  38710. other = stacks[other][x];
  38711. stack.total = other.total =
  38712. Math.max(other.total, stack.total) +
  38713. Math.abs(y) ||
  38714. 0;
  38715. // Percent stacked areas
  38716. }
  38717. else {
  38718. stack.total =
  38719. correctFloat(stack.total + (Math.abs(y) || 0));
  38720. }
  38721. }
  38722. else if (stacking === 'group') {
  38723. if (isArray(y)) {
  38724. y = y[0];
  38725. }
  38726. // In this stack, the total is the number of valid points
  38727. if (y !== null) {
  38728. stack.total = (stack.total || 0) + 1;
  38729. }
  38730. }
  38731. else {
  38732. stack.total = correctFloat(stack.total + (y || 0));
  38733. }
  38734. if (stacking === 'group') {
  38735. // This point's index within the stack, pushed to stack.points[1]
  38736. stack.cumulative = (stack.total || 1) - 1;
  38737. }
  38738. else {
  38739. stack.cumulative = correctFloat(pick(stack.cumulative, stackThreshold) +
  38740. (y || 0));
  38741. }
  38742. if (y !== null) {
  38743. stack.points[pointKey].push(stack.cumulative);
  38744. stackedYData[i] = stack.cumulative;
  38745. stack.hasValidPoints = true;
  38746. }
  38747. }
  38748. if (stacking === 'percent') {
  38749. yAxis.stacking.usePercentage = true;
  38750. }
  38751. if (stacking !== 'group') {
  38752. this.stackedYData = stackedYData; // To be used in getExtremes
  38753. }
  38754. // Reset old stacks
  38755. yAxis.stacking.oldStacks = {};
  38756. }
  38757. /* *
  38758. *
  38759. * Classes
  38760. *
  38761. * */
  38762. /**
  38763. * Adds stacking support to axes.
  38764. * @private
  38765. * @class
  38766. */
  38767. class AxisAdditions {
  38768. /* *
  38769. *
  38770. * Constructors
  38771. *
  38772. * */
  38773. constructor(axis) {
  38774. this.oldStacks = {};
  38775. this.stacks = {};
  38776. this.stacksTouched = 0;
  38777. this.axis = axis;
  38778. }
  38779. /* *
  38780. *
  38781. * Functions
  38782. *
  38783. * */
  38784. /**
  38785. * Build the stacks from top down
  38786. * @private
  38787. */
  38788. buildStacks() {
  38789. const stacking = this;
  38790. const axis = stacking.axis;
  38791. const axisSeries = axis.series;
  38792. const reversedStacks = axis.options.reversedStacks;
  38793. const len = axisSeries.length;
  38794. let actualSeries, i;
  38795. stacking.usePercentage = false;
  38796. i = len;
  38797. while (i--) {
  38798. actualSeries = axisSeries[reversedStacks ? i : len - i - 1];
  38799. actualSeries.setStackedPoints();
  38800. actualSeries.setGroupedPoints();
  38801. }
  38802. // Loop up again to compute percent and stream stack
  38803. for (i = 0; i < len; i++) {
  38804. axisSeries[i].modifyStacks();
  38805. }
  38806. fireEvent(axis, 'afterBuildStacks');
  38807. }
  38808. /**
  38809. * @private
  38810. */
  38811. cleanStacks() {
  38812. const stacking = this;
  38813. let stacks;
  38814. if (stacking.oldStacks) {
  38815. stacks = stacking.stacks = stacking.oldStacks;
  38816. }
  38817. // reset stacks
  38818. objectEach(stacks, function (type) {
  38819. objectEach(type, function (stack) {
  38820. stack.cumulative = stack.total;
  38821. });
  38822. });
  38823. }
  38824. /**
  38825. * Set all the stacks to initial states and destroy unused ones.
  38826. * @private
  38827. */
  38828. resetStacks() {
  38829. objectEach(this.stacks, (type) => {
  38830. objectEach(type, (stack, x) => {
  38831. // Clean up memory after point deletion (#1044, #4320)
  38832. if (isNumber(stack.touched) &&
  38833. stack.touched < this.stacksTouched) {
  38834. stack.destroy();
  38835. delete type[x];
  38836. // Reset stacks
  38837. }
  38838. else {
  38839. stack.total = null;
  38840. stack.cumulative = null;
  38841. }
  38842. });
  38843. });
  38844. }
  38845. /**
  38846. * @private
  38847. */
  38848. renderStackTotals() {
  38849. const stacking = this, axis = stacking.axis, chart = axis.chart, renderer = chart.renderer, stacks = stacking.stacks, stackLabelsAnim = axis.options.stackLabels &&
  38850. axis.options.stackLabels.animation, animationConfig = getDeferredAnimation(chart, stackLabelsAnim || false), stackTotalGroup = stacking.stackTotalGroup = (stacking.stackTotalGroup ||
  38851. renderer
  38852. .g('stack-labels')
  38853. .attr({
  38854. zIndex: 6,
  38855. opacity: 0
  38856. })
  38857. .add());
  38858. // plotLeft/Top will change when y axis gets wider so we need to
  38859. // translate the stackTotalGroup at every render call. See bug #506
  38860. // and #516
  38861. stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
  38862. // Render each stack total
  38863. objectEach(stacks, function (type) {
  38864. objectEach(type, function (stack) {
  38865. stack.render(stackTotalGroup);
  38866. });
  38867. });
  38868. stackTotalGroup.animate({
  38869. opacity: 1
  38870. }, animationConfig);
  38871. }
  38872. }
  38873. /* *
  38874. *
  38875. * Composition
  38876. *
  38877. * */
  38878. var StackingAxis;
  38879. (function (StackingAxis) {
  38880. /* *
  38881. *
  38882. * Constants
  38883. *
  38884. * */
  38885. const composedMembers = [];
  38886. /* *
  38887. *
  38888. * Functions
  38889. *
  38890. * */
  38891. /**
  38892. * Extends axis with stacking support.
  38893. * @private
  38894. */
  38895. function compose(AxisClass, ChartClass, SeriesClass) {
  38896. if (U.pushUnique(composedMembers, AxisClass)) {
  38897. addEvent(AxisClass, 'init', onAxisInit);
  38898. addEvent(AxisClass, 'destroy', onAxisDestroy);
  38899. }
  38900. if (U.pushUnique(composedMembers, ChartClass)) {
  38901. const chartProto = ChartClass.prototype;
  38902. chartProto.getStacks = chartGetStacks;
  38903. }
  38904. if (U.pushUnique(composedMembers, SeriesClass)) {
  38905. const seriesProto = SeriesClass.prototype;
  38906. seriesProto.getStackIndicator = seriesGetStackIndicator;
  38907. seriesProto.modifyStacks = seriesModifyStacks;
  38908. seriesProto.percentStacker = seriesPercentStacker;
  38909. seriesProto.setGroupedPoints = seriesSetGroupedPoints;
  38910. seriesProto.setStackedPoints = seriesSetStackedPoints;
  38911. }
  38912. }
  38913. StackingAxis.compose = compose;
  38914. })(StackingAxis || (StackingAxis = {}));
  38915. /* *
  38916. *
  38917. * Default Export
  38918. *
  38919. * */
  38920. return StackingAxis;
  38921. });
  38922. _registerModule(_modules, 'Series/Line/LineSeries.js', [_modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Series, SeriesRegistry, U) {
  38923. /* *
  38924. *
  38925. * (c) 2010-2021 Torstein Honsi
  38926. *
  38927. * License: www.highcharts.com/license
  38928. *
  38929. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38930. *
  38931. * */
  38932. const { defined, merge } = U;
  38933. /* *
  38934. *
  38935. * Class
  38936. *
  38937. * */
  38938. /**
  38939. * The line series is the base type and is therefor the series base prototype.
  38940. *
  38941. * @private
  38942. */
  38943. class LineSeries extends Series {
  38944. constructor() {
  38945. /* *
  38946. *
  38947. * Static Functions
  38948. *
  38949. * */
  38950. super(...arguments);
  38951. /* *
  38952. *
  38953. * Properties
  38954. *
  38955. * */
  38956. this.data = void 0;
  38957. this.options = void 0;
  38958. this.points = void 0;
  38959. }
  38960. /* *
  38961. *
  38962. * Functions
  38963. *
  38964. * */
  38965. /**
  38966. * Draw the graph. Called internally when rendering line-like series
  38967. * types. The first time it generates the `series.graph` item and
  38968. * optionally other series-wide items like `series.area` for area
  38969. * charts. On subsequent calls these items are updated with new
  38970. * positions and attributes.
  38971. *
  38972. * @function Highcharts.Series#drawGraph
  38973. */
  38974. drawGraph() {
  38975. const series = this, options = this.options, graphPath = (this.gappedPath || this.getGraphPath).call(this), styledMode = this.chart.styledMode;
  38976. let props = [[
  38977. 'graph',
  38978. 'highcharts-graph'
  38979. ]];
  38980. // Presentational properties
  38981. if (!styledMode) {
  38982. props[0].push((options.lineColor ||
  38983. this.color ||
  38984. "#cccccc" /* Palette.neutralColor20 */ // when colorByPoint = true
  38985. ), options.dashStyle);
  38986. }
  38987. props = series.getZonesGraphs(props);
  38988. // Draw the graph
  38989. props.forEach(function (prop, i) {
  38990. const graphKey = prop[0];
  38991. let attribs, graph = series[graphKey];
  38992. const verb = graph ? 'animate' : 'attr';
  38993. if (graph) {
  38994. graph.endX = series.preventGraphAnimation ?
  38995. null :
  38996. graphPath.xMap;
  38997. graph.animate({ d: graphPath });
  38998. }
  38999. else if (graphPath.length) { // #1487
  39000. /**
  39001. * SVG element of area-based charts. Can be used for styling
  39002. * purposes. If zones are configured, this element will be
  39003. * hidden and replaced by multiple zone areas, accessible
  39004. * via `series['zone-area-x']` (where x is a number,
  39005. * starting with 0).
  39006. *
  39007. * @name Highcharts.Series#area
  39008. * @type {Highcharts.SVGElement|undefined}
  39009. */
  39010. /**
  39011. * SVG element of line-based charts. Can be used for styling
  39012. * purposes. If zones are configured, this element will be
  39013. * hidden and replaced by multiple zone lines, accessible
  39014. * via `series['zone-graph-x']` (where x is a number,
  39015. * starting with 0).
  39016. *
  39017. * @name Highcharts.Series#graph
  39018. * @type {Highcharts.SVGElement|undefined}
  39019. */
  39020. series[graphKey] = graph = series.chart.renderer
  39021. .path(graphPath)
  39022. .addClass(prop[1])
  39023. .attr({ zIndex: 1 }) // #1069
  39024. .add(series.group);
  39025. }
  39026. if (graph && !styledMode) {
  39027. attribs = {
  39028. 'stroke': prop[2],
  39029. 'stroke-width': options.lineWidth || 0,
  39030. // Polygon series use filled graph
  39031. 'fill': (series.fillGraph && series.color) || 'none'
  39032. };
  39033. // Apply dash style
  39034. if (prop[3]) {
  39035. attribs.dashstyle = prop[3];
  39036. // The reason for the `else if` is that linecaps don't mix well
  39037. // with dashstyle. The gaps get partially filled by the
  39038. // linecap.
  39039. }
  39040. else if (options.linecap !== 'square') {
  39041. attribs['stroke-linecap'] =
  39042. attribs['stroke-linejoin'] = 'round';
  39043. }
  39044. graph[verb](attribs)
  39045. // Add shadow to normal series (0) or to first
  39046. // zone (1) #3932
  39047. .shadow((i < 2) && options.shadow);
  39048. }
  39049. // Helpers for animation
  39050. if (graph) {
  39051. graph.startX = graphPath.xMap;
  39052. graph.isArea = graphPath.isArea; // For arearange animation
  39053. }
  39054. });
  39055. }
  39056. // eslint-disable-next-line valid-jsdoc
  39057. /**
  39058. * Get the graph path.
  39059. *
  39060. * @private
  39061. */
  39062. getGraphPath(points, nullsAsZeroes, connectCliffs) {
  39063. const series = this, options = series.options, graphPath = [], xMap = [];
  39064. let gap, step = options.step;
  39065. points = points || series.points;
  39066. // Bottom of a stack is reversed
  39067. const reversed = points.reversed;
  39068. if (reversed) {
  39069. points.reverse();
  39070. }
  39071. // Reverse the steps (#5004)
  39072. step = {
  39073. right: 1,
  39074. center: 2
  39075. }[step] || (step && 3);
  39076. if (step && reversed) {
  39077. step = 4 - step;
  39078. }
  39079. // Remove invalid points, especially in spline (#5015)
  39080. points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
  39081. // Build the line
  39082. points.forEach(function (point, i) {
  39083. const plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1], isNull = point.isNull || typeof plotY !== 'number';
  39084. // the path to this point from the previous
  39085. let pathToPoint;
  39086. if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
  39087. !connectCliffs) {
  39088. gap = true; // ... and continue
  39089. }
  39090. // Line series, nullsAsZeroes is not handled
  39091. if (isNull && !defined(nullsAsZeroes) && i > 0) {
  39092. gap = !options.connectNulls;
  39093. // Area series, nullsAsZeroes is set
  39094. }
  39095. else if (isNull && !nullsAsZeroes) {
  39096. gap = true;
  39097. }
  39098. else {
  39099. if (i === 0 || gap) {
  39100. pathToPoint = [[
  39101. 'M',
  39102. point.plotX,
  39103. point.plotY
  39104. ]];
  39105. // Generate the spline as defined in the SplineSeries object
  39106. }
  39107. else if (series.getPointSpline) {
  39108. pathToPoint = [series.getPointSpline(points, point, i)];
  39109. }
  39110. else if (step) {
  39111. if (step === 1) { // right
  39112. pathToPoint = [[
  39113. 'L',
  39114. lastPoint.plotX,
  39115. plotY
  39116. ]];
  39117. }
  39118. else if (step === 2) { // center
  39119. pathToPoint = [[
  39120. 'L',
  39121. (lastPoint.plotX + plotX) / 2,
  39122. lastPoint.plotY
  39123. ], [
  39124. 'L',
  39125. (lastPoint.plotX + plotX) / 2,
  39126. plotY
  39127. ]];
  39128. }
  39129. else {
  39130. pathToPoint = [[
  39131. 'L',
  39132. plotX,
  39133. lastPoint.plotY
  39134. ]];
  39135. }
  39136. pathToPoint.push([
  39137. 'L',
  39138. plotX,
  39139. plotY
  39140. ]);
  39141. }
  39142. else {
  39143. // normal line to next point
  39144. pathToPoint = [[
  39145. 'L',
  39146. plotX,
  39147. plotY
  39148. ]];
  39149. }
  39150. // Prepare for animation. When step is enabled, there are
  39151. // two path nodes for each x value.
  39152. xMap.push(point.x);
  39153. if (step) {
  39154. xMap.push(point.x);
  39155. if (step === 2) { // step = center (#8073)
  39156. xMap.push(point.x);
  39157. }
  39158. }
  39159. graphPath.push.apply(graphPath, pathToPoint);
  39160. gap = false;
  39161. }
  39162. });
  39163. graphPath.xMap = xMap;
  39164. series.graphPath = graphPath;
  39165. return graphPath;
  39166. }
  39167. // eslint-disable-next-line valid-jsdoc
  39168. /**
  39169. * Get zones properties for building graphs. Extendable by series with
  39170. * multiple lines within one series.
  39171. *
  39172. * @private
  39173. */
  39174. getZonesGraphs(props) {
  39175. // Add the zone properties if any
  39176. this.zones.forEach(function (zone, i) {
  39177. const propset = [
  39178. 'zone-graph-' + i,
  39179. 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
  39180. (zone.className || '')
  39181. ];
  39182. if (!this.chart.styledMode) {
  39183. propset.push((zone.color || this.color), (zone.dashStyle || this.options.dashStyle));
  39184. }
  39185. props.push(propset);
  39186. }, this);
  39187. return props;
  39188. }
  39189. }
  39190. LineSeries.defaultOptions = merge(Series.defaultOptions,
  39191. /**
  39192. * General options for all series types.
  39193. *
  39194. * @optionparent plotOptions.series
  39195. */
  39196. {
  39197. legendSymbol: 'lineMarker'
  39198. });
  39199. SeriesRegistry.registerSeriesType('line', LineSeries);
  39200. /* *
  39201. *
  39202. * Default Export
  39203. *
  39204. * */
  39205. /* *
  39206. *
  39207. * API Options
  39208. *
  39209. * */
  39210. /**
  39211. * A line series displays information as a series of data points connected by
  39212. * straight line segments.
  39213. *
  39214. * @sample {highcharts} highcharts/demo/line-basic/
  39215. * Line chart
  39216. * @sample {highstock} stock/demo/basic-line/
  39217. * Line chart
  39218. *
  39219. * @extends plotOptions.series
  39220. * @product highcharts highstock
  39221. * @apioption plotOptions.line
  39222. */
  39223. /**
  39224. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  39225. * of a line graph. Round means that lines are rounded in the ends and
  39226. * bends.
  39227. *
  39228. * @type {Highcharts.SeriesLinecapValue}
  39229. * @default round
  39230. * @since 3.0.7
  39231. * @apioption plotOptions.line.linecap
  39232. */
  39233. /**
  39234. * A `line` series. If the [type](#series.line.type) option is not
  39235. * specified, it is inherited from [chart.type](#chart.type).
  39236. *
  39237. * @extends series,plotOptions.line
  39238. * @excluding dataParser,dataURL
  39239. * @product highcharts highstock
  39240. * @apioption series.line
  39241. */
  39242. /**
  39243. * An array of data points for the series. For the `line` series type,
  39244. * points can be given in the following ways:
  39245. *
  39246. * 1. An array of numerical values. In this case, the numerical values will be
  39247. * interpreted as `y` options. The `x` values will be automatically
  39248. * calculated, either starting at 0 and incremented by 1, or from
  39249. * `pointStart` and `pointInterval` given in the series options. If the axis
  39250. * has categories, these will be used. Example:
  39251. * ```js
  39252. * data: [0, 5, 3, 5]
  39253. * ```
  39254. *
  39255. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39256. * `x,y`. If the first value is a string, it is applied as the name of the
  39257. * point, and the `x` value is inferred.
  39258. * ```js
  39259. * data: [
  39260. * [0, 1],
  39261. * [1, 2],
  39262. * [2, 8]
  39263. * ]
  39264. * ```
  39265. *
  39266. * 3. An array of objects with named values. The following snippet shows only a
  39267. * few settings, see the complete options set below. If the total number of
  39268. * data points exceeds the series'
  39269. * [turboThreshold](#series.line.turboThreshold),
  39270. * this option is not available.
  39271. * ```js
  39272. * data: [{
  39273. * x: 1,
  39274. * y: 9,
  39275. * name: "Point2",
  39276. * color: "#00FF00"
  39277. * }, {
  39278. * x: 1,
  39279. * y: 6,
  39280. * name: "Point1",
  39281. * color: "#FF00FF"
  39282. * }]
  39283. * ```
  39284. *
  39285. * **Note:** In TypeScript you have to extend `PointOptionsObject` with an
  39286. * additional declaration to allow custom data types:
  39287. * ```ts
  39288. * declare module `highcharts` {
  39289. * interface PointOptionsObject {
  39290. * custom: Record<string, (boolean|number|string)>;
  39291. * }
  39292. * }
  39293. * ```
  39294. *
  39295. * @sample {highcharts} highcharts/chart/reflow-true/
  39296. * Numerical values
  39297. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39298. * Arrays of numeric x and y
  39299. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39300. * Arrays of datetime x and y
  39301. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39302. * Arrays of point.name and y
  39303. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39304. * Config objects
  39305. *
  39306. * @declare Highcharts.PointOptionsObject
  39307. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39308. * @apioption series.line.data
  39309. */
  39310. /**
  39311. * An additional, individual class name for the data point's graphic
  39312. * representation. Changes to a point's color will also be reflected in a
  39313. * chart's legend and tooltip.
  39314. *
  39315. * @sample {highcharts} highcharts/css/point-series-classname
  39316. *
  39317. * @type {string}
  39318. * @since 5.0.0
  39319. * @product highcharts gantt
  39320. * @apioption series.line.data.className
  39321. */
  39322. /**
  39323. * Individual color for the point. By default the color is pulled from
  39324. * the global `colors` array.
  39325. *
  39326. * In styled mode, the `color` option doesn't take effect. Instead, use
  39327. * `colorIndex`.
  39328. *
  39329. * @sample {highcharts} highcharts/point/color/
  39330. * Mark the highest point
  39331. *
  39332. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39333. * @product highcharts highstock gantt
  39334. * @apioption series.line.data.color
  39335. */
  39336. /**
  39337. * A specific color index to use for the point, so its graphic representations
  39338. * are given the class name `highcharts-color-{n}`. In styled mode this will
  39339. * change the color of the graphic. In non-styled mode, the color is set by the
  39340. * `fill` attribute, so the change in class name won't have a visual effect by
  39341. * default.
  39342. *
  39343. * Since v11, CSS variables on the form `--highcharts-color-{n}` make changing
  39344. * the color scheme very convenient.
  39345. *
  39346. * @sample {highcharts} highcharts/css/colorindex/
  39347. * Series and point color index
  39348. *
  39349. * @type {number}
  39350. * @since 5.0.0
  39351. * @product highcharts gantt
  39352. * @apioption series.line.data.colorIndex
  39353. */
  39354. /**
  39355. * A reserved subspace to store options and values for customized functionality.
  39356. * Here you can add additional data for your own event callbacks and formatter
  39357. * callbacks.
  39358. *
  39359. * @sample {highcharts} highcharts/point/custom/
  39360. * Point and series with custom data
  39361. *
  39362. * @type {Highcharts.Dictionary<*>}
  39363. * @apioption series.line.data.custom
  39364. */
  39365. /**
  39366. * Individual data label for each point. The options are the same as
  39367. * the ones for [plotOptions.series.dataLabels](
  39368. * #plotOptions.series.dataLabels).
  39369. *
  39370. * @sample highcharts/point/datalabels/
  39371. * Show a label for the last value
  39372. *
  39373. * @declare Highcharts.DataLabelsOptions
  39374. * @extends plotOptions.line.dataLabels
  39375. * @product highcharts highstock gantt
  39376. * @apioption series.line.data.dataLabels
  39377. */
  39378. /**
  39379. * A description of the point to add to the screen reader information
  39380. * about the point.
  39381. *
  39382. * @type {string}
  39383. * @since 5.0.0
  39384. * @requires modules/accessibility
  39385. * @apioption series.line.data.description
  39386. */
  39387. /**
  39388. * An id for the point. This can be used after render time to get a
  39389. * pointer to the point object through `chart.get()`.
  39390. *
  39391. * @sample {highcharts} highcharts/point/id/
  39392. * Remove an id'd point
  39393. *
  39394. * @type {string}
  39395. * @since 1.2.0
  39396. * @product highcharts highstock gantt
  39397. * @apioption series.line.data.id
  39398. */
  39399. /**
  39400. * The rank for this point's data label in case of collision. If two
  39401. * data labels are about to overlap, only the one with the highest `labelrank`
  39402. * will be drawn.
  39403. *
  39404. * @type {number}
  39405. * @apioption series.line.data.labelrank
  39406. */
  39407. /**
  39408. * The name of the point as shown in the legend, tooltip, dataLabels, etc.
  39409. *
  39410. * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
  39411. *
  39412. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39413. * Point names
  39414. *
  39415. * @type {string}
  39416. * @apioption series.line.data.name
  39417. */
  39418. /**
  39419. * Whether the data point is selected initially.
  39420. *
  39421. * @type {boolean}
  39422. * @default false
  39423. * @product highcharts highstock gantt
  39424. * @apioption series.line.data.selected
  39425. */
  39426. /**
  39427. * The x value of the point. For datetime axes, the X value is the timestamp
  39428. * in milliseconds since 1970.
  39429. *
  39430. * @type {number}
  39431. * @product highcharts highstock
  39432. * @apioption series.line.data.x
  39433. */
  39434. /**
  39435. * The y value of the point.
  39436. *
  39437. * @type {number|null}
  39438. * @product highcharts highstock
  39439. * @apioption series.line.data.y
  39440. */
  39441. /**
  39442. * The individual point events.
  39443. *
  39444. * @extends plotOptions.series.point.events
  39445. * @product highcharts highstock gantt
  39446. * @apioption series.line.data.events
  39447. */
  39448. /**
  39449. * Options for the point markers of line-like series.
  39450. *
  39451. * @declare Highcharts.PointMarkerOptionsObject
  39452. * @extends plotOptions.series.marker
  39453. * @product highcharts highstock
  39454. * @apioption series.line.data.marker
  39455. */
  39456. ''; // include precedent doclets in transpilat
  39457. return LineSeries;
  39458. });
  39459. _registerModule(_modules, 'Series/Area/AreaSeries.js', [_modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (Color, SeriesRegistry, U) {
  39460. /* *
  39461. *
  39462. * (c) 2010-2021 Torstein Honsi
  39463. *
  39464. * License: www.highcharts.com/license
  39465. *
  39466. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39467. *
  39468. * */
  39469. const { parse: color } = Color;
  39470. const { seriesTypes: { line: LineSeries } } = SeriesRegistry;
  39471. const { extend, merge, objectEach, pick } = U;
  39472. /* *
  39473. *
  39474. * Class
  39475. *
  39476. * */
  39477. /**
  39478. * Area series type.
  39479. *
  39480. * @private
  39481. * @class
  39482. * @name AreaSeries
  39483. *
  39484. * @augments LineSeries
  39485. */
  39486. class AreaSeries extends LineSeries {
  39487. constructor() {
  39488. /* *
  39489. *
  39490. * Static Properties
  39491. *
  39492. * */
  39493. super(...arguments);
  39494. this.data = void 0;
  39495. this.options = void 0;
  39496. this.points = void 0;
  39497. /* eslint-enable valid-jsdoc */
  39498. }
  39499. /* *
  39500. *
  39501. * Functions
  39502. *
  39503. * */
  39504. /* eslint-disable valid-jsdoc */
  39505. /**
  39506. * Draw the graph and the underlying area. This method calls the Series
  39507. * base function and adds the area. The areaPath is calculated in the
  39508. * getSegmentPath method called from Series.prototype.drawGraph.
  39509. * @private
  39510. */
  39511. drawGraph() {
  39512. // Define or reset areaPath
  39513. this.areaPath = [];
  39514. // Call the base method
  39515. super.drawGraph.apply(this);
  39516. // Define local variables
  39517. const series = this, areaPath = this.areaPath, options = this.options, zones = this.zones, props = [[
  39518. 'area',
  39519. 'highcharts-area',
  39520. this.color,
  39521. options.fillColor
  39522. ]]; // area name, main color, fill color
  39523. zones.forEach(function (zone, i) {
  39524. props.push([
  39525. 'zone-area-' + i,
  39526. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  39527. zone.className,
  39528. zone.color || series.color,
  39529. zone.fillColor || options.fillColor
  39530. ]);
  39531. });
  39532. props.forEach(function (prop) {
  39533. const areaKey = prop[0], attribs = {};
  39534. let area = series[areaKey];
  39535. const verb = area ? 'animate' : 'attr';
  39536. // Create or update the area
  39537. if (area) { // update
  39538. area.endX = series.preventGraphAnimation ?
  39539. null :
  39540. areaPath.xMap;
  39541. area.animate({ d: areaPath });
  39542. }
  39543. else { // create
  39544. attribs.zIndex = 0; // #1069
  39545. area = series[areaKey] = series.chart.renderer
  39546. .path(areaPath)
  39547. .addClass(prop[1])
  39548. .add(series.group);
  39549. area.isArea = true;
  39550. }
  39551. if (!series.chart.styledMode) {
  39552. // If there is fillColor defined for the area, set it
  39553. if (prop[3]) {
  39554. attribs.fill = prop[3];
  39555. }
  39556. else {
  39557. // Otherwise, we set it to the series color and add
  39558. // fill-opacity (#18939)
  39559. attribs.fill = prop[2];
  39560. attribs['fill-opacity'] = pick(options.fillOpacity, 0.75);
  39561. }
  39562. }
  39563. area[verb](attribs);
  39564. area.startX = areaPath.xMap;
  39565. area.shiftUnit = options.step ? 2 : 1;
  39566. });
  39567. }
  39568. /**
  39569. * @private
  39570. */
  39571. getGraphPath(points) {
  39572. 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
  39573. yAxis.getThreshold(options.threshold)), connectNulls = pick(// #10574
  39574. options.connectNulls, stacking === 'percent'),
  39575. // To display null points in underlying stacked series, this
  39576. // series graph must be broken, and the area also fall down to
  39577. // fill the gap left by the null point. #2069
  39578. addDummyPoints = function (i, otherI, side) {
  39579. const point = points[i], stackedValues = stacking &&
  39580. stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0;
  39581. let top, bottom, isNull = true;
  39582. if (cliffVal || nullVal) {
  39583. top = (nullVal ?
  39584. stackedValues[0] :
  39585. stackedValues[1]) + cliffVal;
  39586. bottom = stackedValues[0] + cliffVal;
  39587. isNull = !!nullVal;
  39588. }
  39589. else if (!stacking &&
  39590. points[otherI] &&
  39591. points[otherI].isNull) {
  39592. top = bottom = threshold;
  39593. }
  39594. // Add to the top and bottom line of the area
  39595. if (typeof top !== 'undefined') {
  39596. graphPoints.push({
  39597. plotX: plotX,
  39598. plotY: top === null ?
  39599. translatedThreshold :
  39600. yAxis.getThreshold(top),
  39601. isNull: isNull,
  39602. isCliff: true
  39603. });
  39604. bottomPoints.push({
  39605. plotX: plotX,
  39606. plotY: bottom === null ?
  39607. translatedThreshold :
  39608. yAxis.getThreshold(bottom),
  39609. doCurve: false // #1041, gaps in areaspline areas
  39610. });
  39611. }
  39612. };
  39613. let plotX, isNull, yBottom;
  39614. // Find what points to use
  39615. points = points || this.points;
  39616. // Fill in missing points
  39617. if (stacking) {
  39618. points = this.getStackPoints(points);
  39619. }
  39620. for (let i = 0, iEnd = points.length; i < iEnd; ++i) {
  39621. // Reset after series.update of stacking property (#12033)
  39622. if (!stacking) {
  39623. points[i].leftCliff = points[i].rightCliff =
  39624. points[i].leftNull = points[i].rightNull = void 0;
  39625. }
  39626. isNull = points[i].isNull;
  39627. plotX = pick(points[i].rectPlotX, points[i].plotX);
  39628. yBottom = stacking ?
  39629. pick(points[i].yBottom, translatedThreshold) :
  39630. translatedThreshold;
  39631. if (!isNull || connectNulls) {
  39632. if (!connectNulls) {
  39633. addDummyPoints(i, i - 1, 'left');
  39634. }
  39635. // Skip null point when stacking is false and connectNulls
  39636. // true
  39637. if (!(isNull && !stacking && connectNulls)) {
  39638. graphPoints.push(points[i]);
  39639. bottomPoints.push({
  39640. x: i,
  39641. plotX: plotX,
  39642. plotY: yBottom
  39643. });
  39644. }
  39645. if (!connectNulls) {
  39646. addDummyPoints(i, i + 1, 'right');
  39647. }
  39648. }
  39649. }
  39650. const topPath = getGraphPath.call(this, graphPoints, true, true);
  39651. bottomPoints.reversed = true;
  39652. const bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  39653. const firstBottomPoint = bottomPath[0];
  39654. if (firstBottomPoint && firstBottomPoint[0] === 'M') {
  39655. bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]];
  39656. }
  39657. const areaPath = topPath.concat(bottomPath);
  39658. if (areaPath.length) {
  39659. areaPath.push(['Z']);
  39660. }
  39661. // TODO: don't set leftCliff and rightCliff when connectNulls?
  39662. const graphPath = getGraphPath
  39663. .call(this, graphPoints, false, connectNulls);
  39664. areaPath.xMap = topPath.xMap;
  39665. this.areaPath = areaPath;
  39666. return graphPath;
  39667. }
  39668. /**
  39669. * Return an array of stacked points, where null and missing points are
  39670. * replaced by dummy points in order for gaps to be drawn correctly in
  39671. * stacks.
  39672. * @private
  39673. */
  39674. getStackPoints(points) {
  39675. 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);
  39676. points = points || this.points;
  39677. if (this.options.stacking) {
  39678. for (let i = 0; i < points.length; i++) {
  39679. // Reset after point update (#7326)
  39680. points[i].leftNull = points[i].rightNull = void 0;
  39681. // Create a map where we can quickly look up the points by
  39682. // their X values.
  39683. pointMap[points[i].x] = points[i];
  39684. }
  39685. // Sort the keys (#1651)
  39686. objectEach(stack, function (stackX, x) {
  39687. // nulled after switching between
  39688. // grouping and not (#1651, #2336)
  39689. if (stackX.total !== null) {
  39690. keys.push(x);
  39691. }
  39692. });
  39693. keys.sort(function (a, b) {
  39694. return a - b;
  39695. });
  39696. const visibleSeries = yAxisSeries.map((s) => s.visible);
  39697. keys.forEach(function (x, idx) {
  39698. let y = 0, stackPoint, stackedValues;
  39699. if (pointMap[x] && !pointMap[x].isNull) {
  39700. segment.push(pointMap[x]);
  39701. // Find left and right cliff. -1 goes left, 1 goes
  39702. // right.
  39703. [-1, 1].forEach(function (direction) {
  39704. const nullName = direction === 1 ?
  39705. 'rightNull' :
  39706. 'leftNull', cliffName = direction === 1 ?
  39707. 'rightCliff' :
  39708. 'leftCliff', otherStack = stack[keys[idx + direction]];
  39709. let cliff = 0;
  39710. // If there is a stack next to this one,
  39711. // to the left or to the right...
  39712. if (otherStack) {
  39713. let i = seriesIndex;
  39714. // Can go either up or down,
  39715. // depending on reversedStacks
  39716. while (i >= 0 && i < seriesLength) {
  39717. const si = yAxisSeries[i].index;
  39718. stackPoint = otherStack.points[si];
  39719. if (!stackPoint) {
  39720. // If the next point in this series is
  39721. // missing, mark the point with
  39722. // point.leftNull or point.rightNull = true.
  39723. if (si === series.index) {
  39724. pointMap[x][nullName] = true;
  39725. // If there are missing points in the next
  39726. // stack in any of the series below this
  39727. // one, we need to substract the missing
  39728. // values and add a hiatus to the left or
  39729. // right.
  39730. }
  39731. else if (visibleSeries[i]) {
  39732. stackedValues = stack[x].points[si];
  39733. if (stackedValues) {
  39734. cliff -= (stackedValues[1] -
  39735. stackedValues[0]);
  39736. }
  39737. }
  39738. }
  39739. // When reversedStacks is true, loop up,
  39740. // else loop down
  39741. i += upOrDown;
  39742. }
  39743. }
  39744. pointMap[x][cliffName] = cliff;
  39745. });
  39746. // There is no point for this X value in this series, so we
  39747. // insert a dummy point in order for the areas to be drawn
  39748. // correctly.
  39749. }
  39750. else {
  39751. // Loop down the stack to find the series below this
  39752. // one that has a value (#1991)
  39753. let i = seriesIndex;
  39754. while (i >= 0 && i < seriesLength) {
  39755. const si = yAxisSeries[i].index;
  39756. stackPoint = stack[x].points[si];
  39757. if (stackPoint) {
  39758. y = stackPoint[1];
  39759. break;
  39760. }
  39761. // When reversedStacks is true, loop up, else loop
  39762. // down
  39763. i += upOrDown;
  39764. }
  39765. y = pick(y, 0);
  39766. y = yAxis.translate(// #6272
  39767. y, 0, 1, 0, 1);
  39768. segment.push({
  39769. isNull: true,
  39770. plotX: xAxis.translate(// #6272
  39771. x, 0, 0, 0, 1),
  39772. x: x,
  39773. plotY: y,
  39774. yBottom: y
  39775. });
  39776. }
  39777. });
  39778. }
  39779. return segment;
  39780. }
  39781. }
  39782. /**
  39783. * The area series type.
  39784. *
  39785. * @sample {highcharts} highcharts/demo/area-basic/
  39786. * Area chart
  39787. * @sample {highstock} stock/demo/area/
  39788. * Area chart
  39789. *
  39790. * @extends plotOptions.line
  39791. * @excluding useOhlcData
  39792. * @product highcharts highstock
  39793. * @optionparent plotOptions.area
  39794. */
  39795. AreaSeries.defaultOptions = merge(LineSeries.defaultOptions, {
  39796. /**
  39797. * @see [fillColor](#plotOptions.area.fillColor)
  39798. * @see [fillOpacity](#plotOptions.area.fillOpacity)
  39799. *
  39800. * @apioption plotOptions.area.color
  39801. */
  39802. /**
  39803. * Fill color or gradient for the area. When `null`, the series' `color`
  39804. * is used with the series' `fillOpacity`.
  39805. *
  39806. * In styled mode, the fill color can be set with the `.highcharts-area`
  39807. * class name.
  39808. *
  39809. * @see [color](#plotOptions.area.color)
  39810. * @see [fillOpacity](#plotOptions.area.fillOpacity)
  39811. *
  39812. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  39813. * Null by default
  39814. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  39815. * Gradient
  39816. *
  39817. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39818. * @product highcharts highstock
  39819. * @apioption plotOptions.area.fillColor
  39820. */
  39821. /**
  39822. * Fill opacity for the area. When you set an explicit `fillColor`,
  39823. * the `fillOpacity` is not applied. Instead, you should define the
  39824. * opacity in the `fillColor` with an rgba color definition. The
  39825. * `fillOpacity` setting, also the default setting, overrides the alpha
  39826. * component of the `color` setting.
  39827. *
  39828. * In styled mode, the fill opacity can be set with the
  39829. * `.highcharts-area` class name.
  39830. *
  39831. * @see [color](#plotOptions.area.color)
  39832. * @see [fillColor](#plotOptions.area.fillColor)
  39833. *
  39834. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  39835. * Automatic fill color and fill opacity of 0.1
  39836. *
  39837. * @type {number}
  39838. * @default {highcharts} 0.75
  39839. * @default {highstock} 0.75
  39840. * @product highcharts highstock
  39841. * @apioption plotOptions.area.fillOpacity
  39842. */
  39843. /**
  39844. * A separate color for the graph line. By default the line takes the
  39845. * `color` of the series, but the lineColor setting allows setting a
  39846. * separate color for the line without altering the `fillColor`.
  39847. *
  39848. * In styled mode, the line stroke can be set with the
  39849. * `.highcharts-graph` class name.
  39850. *
  39851. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  39852. * Dark gray line
  39853. *
  39854. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39855. * @product highcharts highstock
  39856. * @apioption plotOptions.area.lineColor
  39857. */
  39858. /**
  39859. * A separate color for the negative part of the area.
  39860. *
  39861. * In styled mode, a negative color is set with the
  39862. * `.highcharts-negative` class name.
  39863. *
  39864. * @see [negativeColor](#plotOptions.area.negativeColor)
  39865. *
  39866. * @sample {highcharts} highcharts/css/series-negative-color/
  39867. * Negative color in styled mode
  39868. *
  39869. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  39870. * @since 3.0
  39871. * @product highcharts
  39872. * @apioption plotOptions.area.negativeFillColor
  39873. */
  39874. /**
  39875. * Whether the whole area or just the line should respond to mouseover
  39876. * tooltips and other mouse or touch events.
  39877. *
  39878. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  39879. * Display the tooltip when the area is hovered
  39880. *
  39881. * @type {boolean}
  39882. * @default false
  39883. * @since 1.1.6
  39884. * @product highcharts highstock
  39885. * @apioption plotOptions.area.trackByArea
  39886. */
  39887. /**
  39888. * The Y axis value to serve as the base for the area, for
  39889. * distinguishing between values above and below a threshold. The area
  39890. * between the graph and the threshold is filled.
  39891. *
  39892. * * If a number is given, the Y axis will scale to the threshold.
  39893. * * If `null`, the scaling behaves like a line series with fill between
  39894. * the graph and the Y axis minimum.
  39895. * * If `Infinity` or `-Infinity`, the area between the graph and the
  39896. * corresponding Y axis extreme is filled (since v6.1.0).
  39897. *
  39898. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  39899. * A threshold of 100
  39900. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  39901. * A threshold of Infinity
  39902. *
  39903. * @type {number|null}
  39904. * @since 2.0
  39905. * @product highcharts highstock
  39906. */
  39907. threshold: 0,
  39908. legendSymbol: 'rectangle'
  39909. });
  39910. extend(AreaSeries.prototype, {
  39911. singleStacks: false
  39912. });
  39913. SeriesRegistry.registerSeriesType('area', AreaSeries);
  39914. /* *
  39915. *
  39916. * Default Export
  39917. *
  39918. * */
  39919. /* *
  39920. *
  39921. * API Options
  39922. *
  39923. * */
  39924. /**
  39925. * A `area` series. If the [type](#series.area.type) option is not
  39926. * specified, it is inherited from [chart.type](#chart.type).
  39927. *
  39928. * @extends series,plotOptions.area
  39929. * @excluding dataParser, dataURL, useOhlcData
  39930. * @product highcharts highstock
  39931. * @apioption series.area
  39932. */
  39933. /**
  39934. * @see [fillColor](#series.area.fillColor)
  39935. * @see [fillOpacity](#series.area.fillOpacity)
  39936. *
  39937. * @apioption series.area.color
  39938. */
  39939. /**
  39940. * An array of data points for the series. For the `area` series type,
  39941. * points can be given in the following ways:
  39942. *
  39943. * 1. An array of numerical values. In this case, the numerical values will be
  39944. * interpreted as `y` options. The `x` values will be automatically
  39945. * calculated, either starting at 0 and incremented by 1, or from
  39946. * `pointStart` * and `pointInterval` given in the series options. If the
  39947. * axis has categories, these will be used. Example:
  39948. * ```js
  39949. * data: [0, 5, 3, 5]
  39950. * ```
  39951. *
  39952. * 2. An array of arrays with 2 values. In this case, the values correspond to
  39953. * `x,y`. If the first value is a string, it is applied as the name of the
  39954. * point, and the `x` value is inferred.
  39955. * ```js
  39956. * data: [
  39957. * [0, 9],
  39958. * [1, 7],
  39959. * [2, 6]
  39960. * ]
  39961. * ```
  39962. *
  39963. * 3. An array of objects with named values. The following snippet shows only a
  39964. * few settings, see the complete options set below. If the total number of
  39965. * data points exceeds the series'
  39966. * [turboThreshold](#series.area.turboThreshold), this option is not
  39967. * available.
  39968. * ```js
  39969. * data: [{
  39970. * x: 1,
  39971. * y: 9,
  39972. * name: "Point2",
  39973. * color: "#00FF00"
  39974. * }, {
  39975. * x: 1,
  39976. * y: 6,
  39977. * name: "Point1",
  39978. * color: "#FF00FF"
  39979. * }]
  39980. * ```
  39981. *
  39982. * @sample {highcharts} highcharts/chart/reflow-true/
  39983. * Numerical values
  39984. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39985. * Arrays of numeric x and y
  39986. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39987. * Arrays of datetime x and y
  39988. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39989. * Arrays of point.name and y
  39990. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39991. * Config objects
  39992. *
  39993. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  39994. * @extends series.line.data
  39995. * @product highcharts highstock
  39996. * @apioption series.area.data
  39997. */
  39998. /**
  39999. * @see [color](#series.area.color)
  40000. * @see [fillOpacity](#series.area.fillOpacity)
  40001. *
  40002. * @apioption series.area.fillColor
  40003. */
  40004. /**
  40005. * @see [color](#series.area.color)
  40006. * @see [fillColor](#series.area.fillColor)
  40007. *
  40008. * @default {highcharts} 0.75
  40009. * @default {highstock} 0.75
  40010. * @apioption series.area.fillOpacity
  40011. */
  40012. ''; // adds doclets above to transpilat
  40013. return AreaSeries;
  40014. });
  40015. _registerModule(_modules, 'Series/Spline/SplineSeries.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  40016. /* *
  40017. *
  40018. * (c) 2010-2021 Torstein Honsi
  40019. *
  40020. * License: www.highcharts.com/license
  40021. *
  40022. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40023. *
  40024. * */
  40025. const { line: LineSeries } = SeriesRegistry.seriesTypes;
  40026. const { merge, pick } = U;
  40027. /* *
  40028. *
  40029. * Class
  40030. *
  40031. * */
  40032. /**
  40033. * Spline series type.
  40034. *
  40035. * @private
  40036. */
  40037. class SplineSeries extends LineSeries {
  40038. constructor() {
  40039. /* *
  40040. *
  40041. * Static Properties
  40042. *
  40043. * */
  40044. super(...arguments);
  40045. /* *
  40046. *
  40047. * Properties
  40048. *
  40049. * */
  40050. this.data = void 0;
  40051. this.options = void 0;
  40052. this.points = void 0;
  40053. /* eslint-enable valid-jsdoc */
  40054. }
  40055. /* *
  40056. *
  40057. * Functions
  40058. *
  40059. * */
  40060. /* eslint-disable valid-jsdoc */
  40061. /**
  40062. * Get the spline segment from a given point's previous neighbour to the
  40063. * given point.
  40064. *
  40065. * @private
  40066. * @function Highcharts.seriesTypes.spline#getPointSpline
  40067. */
  40068. getPointSpline(points, point, i) {
  40069. const
  40070. // 1 means control points midway between points, 2 means 1/3
  40071. // from the point, 3 is 1/4 etc
  40072. smoothing = 1.5, denom = smoothing + 1, plotX = point.plotX || 0, plotY = point.plotY || 0, lastPoint = points[i - 1], nextPoint = points[i + 1];
  40073. let leftContX, leftContY, rightContX, rightContY;
  40074. /**
  40075. * @private
  40076. */
  40077. function doCurve(otherPoint) {
  40078. return otherPoint &&
  40079. !otherPoint.isNull &&
  40080. otherPoint.doCurve !== false &&
  40081. // #6387, area splines next to null:
  40082. !point.isCliff;
  40083. }
  40084. // Find control points
  40085. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  40086. const lastX = lastPoint.plotX || 0, lastY = lastPoint.plotY || 0, nextX = nextPoint.plotX || 0, nextY = nextPoint.plotY || 0;
  40087. let correction = 0;
  40088. leftContX = (smoothing * plotX + lastX) / denom;
  40089. leftContY = (smoothing * plotY + lastY) / denom;
  40090. rightContX = (smoothing * plotX + nextX) / denom;
  40091. rightContY = (smoothing * plotY + nextY) / denom;
  40092. // Have the two control points make a straight line through main
  40093. // point
  40094. if (rightContX !== leftContX) { // #5016, division by zero
  40095. correction = (((rightContY - leftContY) *
  40096. (rightContX - plotX)) /
  40097. (rightContX - leftContX) + plotY - rightContY);
  40098. }
  40099. leftContY += correction;
  40100. rightContY += correction;
  40101. // to prevent false extremes, check that control points are
  40102. // between neighbouring points' y values
  40103. if (leftContY > lastY && leftContY > plotY) {
  40104. leftContY = Math.max(lastY, plotY);
  40105. // mirror of left control point
  40106. rightContY = 2 * plotY - leftContY;
  40107. }
  40108. else if (leftContY < lastY && leftContY < plotY) {
  40109. leftContY = Math.min(lastY, plotY);
  40110. rightContY = 2 * plotY - leftContY;
  40111. }
  40112. if (rightContY > nextY && rightContY > plotY) {
  40113. rightContY = Math.max(nextY, plotY);
  40114. leftContY = 2 * plotY - rightContY;
  40115. }
  40116. else if (rightContY < nextY && rightContY < plotY) {
  40117. rightContY = Math.min(nextY, plotY);
  40118. leftContY = 2 * plotY - rightContY;
  40119. }
  40120. // record for drawing in next point
  40121. point.rightContX = rightContX;
  40122. point.rightContY = rightContY;
  40123. }
  40124. // Visualize control points for debugging
  40125. /*
  40126. if (leftContX) {
  40127. this.chart.renderer.circle(
  40128. leftContX + this.chart.plotLeft,
  40129. leftContY + this.chart.plotTop,
  40130. 2
  40131. )
  40132. .attr({
  40133. stroke: 'red',
  40134. 'stroke-width': 2,
  40135. fill: 'none',
  40136. zIndex: 9
  40137. })
  40138. .add();
  40139. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  40140. leftContY + this.chart.plotTop,
  40141. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  40142. .attr({
  40143. stroke: 'red',
  40144. 'stroke-width': 2,
  40145. zIndex: 9
  40146. })
  40147. .add();
  40148. }
  40149. if (rightContX) {
  40150. this.chart.renderer.circle(
  40151. rightContX + this.chart.plotLeft,
  40152. rightContY + this.chart.plotTop,
  40153. 2
  40154. )
  40155. .attr({
  40156. stroke: 'green',
  40157. 'stroke-width': 2,
  40158. fill: 'none',
  40159. zIndex: 9
  40160. })
  40161. .add();
  40162. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  40163. rightContY + this.chart.plotTop,
  40164. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  40165. .attr({
  40166. stroke: 'green',
  40167. 'stroke-width': 2,
  40168. zIndex: 9
  40169. })
  40170. .add();
  40171. }
  40172. // */
  40173. const ret = [
  40174. 'C',
  40175. pick(lastPoint.rightContX, lastPoint.plotX, 0),
  40176. pick(lastPoint.rightContY, lastPoint.plotY, 0),
  40177. pick(leftContX, plotX, 0),
  40178. pick(leftContY, plotY, 0),
  40179. plotX,
  40180. plotY
  40181. ];
  40182. // reset for updating series later
  40183. lastPoint.rightContX = lastPoint.rightContY = void 0;
  40184. return ret;
  40185. }
  40186. }
  40187. /**
  40188. * A spline series is a special type of line series, where the segments
  40189. * between the data points are smoothed.
  40190. *
  40191. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  40192. * Spline chart
  40193. * @sample {highstock} stock/demo/spline/
  40194. * Spline chart
  40195. *
  40196. * @extends plotOptions.series
  40197. * @excluding step, boostThreshold, boostBlending
  40198. * @product highcharts highstock
  40199. * @optionparent plotOptions.spline
  40200. */
  40201. SplineSeries.defaultOptions = merge(LineSeries.defaultOptions);
  40202. SeriesRegistry.registerSeriesType('spline', SplineSeries);
  40203. /* *
  40204. *
  40205. * Default Export
  40206. *
  40207. * */
  40208. /* *
  40209. *
  40210. * API Options
  40211. *
  40212. * */
  40213. /**
  40214. * A `spline` series. If the [type](#series.spline.type) option is
  40215. * not specified, it is inherited from [chart.type](#chart.type).
  40216. *
  40217. * @extends series,plotOptions.spline
  40218. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  40219. * @product highcharts highstock
  40220. * @apioption series.spline
  40221. */
  40222. /**
  40223. * An array of data points for the series. For the `spline` series type,
  40224. * points can be given in the following ways:
  40225. *
  40226. * 1. An array of numerical values. In this case, the numerical values will be
  40227. * interpreted as `y` options. The `x` values will be automatically
  40228. * calculated, either starting at 0 and incremented by 1, or from
  40229. * `pointStart` and `pointInterval` given in the series options. If the axis
  40230. * has categories, these will be used. Example:
  40231. * ```js
  40232. * data: [0, 5, 3, 5]
  40233. * ```
  40234. *
  40235. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40236. * `x,y`. If the first value is a string, it is applied as the name of the
  40237. * point, and the `x` value is inferred.
  40238. * ```js
  40239. * data: [
  40240. * [0, 9],
  40241. * [1, 2],
  40242. * [2, 8]
  40243. * ]
  40244. * ```
  40245. *
  40246. * 3. An array of objects with named values. The following snippet shows only a
  40247. * few settings, see the complete options set below. If the total number of
  40248. * data points exceeds the series'
  40249. * [turboThreshold](#series.spline.turboThreshold),
  40250. * this option is not available.
  40251. * ```js
  40252. * data: [{
  40253. * x: 1,
  40254. * y: 9,
  40255. * name: "Point2",
  40256. * color: "#00FF00"
  40257. * }, {
  40258. * x: 1,
  40259. * y: 0,
  40260. * name: "Point1",
  40261. * color: "#FF00FF"
  40262. * }]
  40263. * ```
  40264. *
  40265. * @sample {highcharts} highcharts/chart/reflow-true/
  40266. * Numerical values
  40267. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40268. * Arrays of numeric x and y
  40269. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40270. * Arrays of datetime x and y
  40271. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40272. * Arrays of point.name and y
  40273. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40274. * Config objects
  40275. *
  40276. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40277. * @extends series.line.data
  40278. * @product highcharts highstock
  40279. * @apioption series.spline.data
  40280. */
  40281. ''; // adds doclets above intro transpilat
  40282. return SplineSeries;
  40283. });
  40284. _registerModule(_modules, 'Series/AreaSpline/AreaSplineSeries.js', [_modules['Series/Spline/SplineSeries.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SplineSeries, SeriesRegistry, U) {
  40285. /* *
  40286. *
  40287. * (c) 2010-2021 Torstein Honsi
  40288. *
  40289. * License: www.highcharts.com/license
  40290. *
  40291. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40292. *
  40293. * */
  40294. const { area: AreaSeries, area: { prototype: areaProto } } = SeriesRegistry.seriesTypes;
  40295. const { extend, merge } = U;
  40296. /* *
  40297. *
  40298. * Class
  40299. *
  40300. * */
  40301. /**
  40302. * AreaSpline series type.
  40303. *
  40304. * @private
  40305. * @class
  40306. * @name Highcharts.seriesTypes.areaspline
  40307. *
  40308. * @augments Highcharts.Series
  40309. */
  40310. class AreaSplineSeries extends SplineSeries {
  40311. constructor() {
  40312. /* *
  40313. *
  40314. * Static Properties
  40315. *
  40316. * */
  40317. super(...arguments);
  40318. /* *
  40319. *
  40320. * Properties
  40321. *
  40322. * */
  40323. this.data = void 0;
  40324. this.points = void 0;
  40325. this.options = void 0;
  40326. }
  40327. }
  40328. AreaSplineSeries.defaultOptions = merge(SplineSeries.defaultOptions, AreaSeries.defaultOptions);
  40329. extend(AreaSplineSeries.prototype, {
  40330. getGraphPath: areaProto.getGraphPath,
  40331. getStackPoints: areaProto.getStackPoints,
  40332. drawGraph: areaProto.drawGraph
  40333. });
  40334. SeriesRegistry.registerSeriesType('areaspline', AreaSplineSeries);
  40335. /* *
  40336. *
  40337. * Default Export
  40338. *
  40339. * */
  40340. /* *
  40341. *
  40342. * API Options
  40343. *
  40344. * */
  40345. /**
  40346. * The area spline series is an area series where the graph between the
  40347. * points is smoothed into a spline.
  40348. *
  40349. * @sample {highcharts} highcharts/demo/areaspline/
  40350. * Area spline chart
  40351. * @sample {highstock} stock/demo/areaspline/
  40352. * Area spline chart
  40353. *
  40354. * @extends plotOptions.area
  40355. * @excluding step, boostThreshold, boostBlending
  40356. * @product highcharts highstock
  40357. * @apioption plotOptions.areaspline
  40358. */
  40359. /**
  40360. * @see [fillColor](#plotOptions.areaspline.fillColor)
  40361. * @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
  40362. *
  40363. * @apioption plotOptions.areaspline.color
  40364. */
  40365. /**
  40366. * @see [color](#plotOptions.areaspline.color)
  40367. * @see [fillOpacity](#plotOptions.areaspline.fillOpacity)
  40368. *
  40369. * @apioption plotOptions.areaspline.fillColor
  40370. */
  40371. /**
  40372. * @see [color](#plotOptions.areaspline.color)
  40373. * @see [fillColor](#plotOptions.areaspline.fillColor)
  40374. *
  40375. * @default 0.75
  40376. * @apioption plotOptions.areaspline.fillOpacity
  40377. */
  40378. /**
  40379. * A `areaspline` series. If the [type](#series.areaspline.type) option
  40380. * is not specified, it is inherited from [chart.type](#chart.type).
  40381. *
  40382. *
  40383. * @extends series,plotOptions.areaspline
  40384. * @excluding dataParser, dataURL, step, boostThreshold, boostBlending
  40385. * @product highcharts highstock
  40386. * @apioption series.areaspline
  40387. */
  40388. /**
  40389. * @see [fillColor](#series.areaspline.fillColor)
  40390. * @see [fillOpacity](#series.areaspline.fillOpacity)
  40391. *
  40392. * @apioption series.areaspline.color
  40393. */
  40394. /**
  40395. * An array of data points for the series. For the `areaspline` series
  40396. * type, points can be given in the following ways:
  40397. *
  40398. * 1. An array of numerical values. In this case, the numerical values will be
  40399. * interpreted as `y` options. The `x` values will be automatically
  40400. * calculated, either starting at 0 and incremented by 1, or from
  40401. * `pointStart` and `pointInterval` given in the series options. If the axis
  40402. * has categories, these will be used. Example:
  40403. * ```js
  40404. * data: [0, 5, 3, 5]
  40405. * ```
  40406. *
  40407. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40408. * `x,y`. If the first value is a string, it is applied as the name of the
  40409. * point, and the `x` value is inferred.
  40410. * ```js
  40411. * data: [
  40412. * [0, 10],
  40413. * [1, 9],
  40414. * [2, 3]
  40415. * ]
  40416. * ```
  40417. *
  40418. * 3. An array of objects with named values. The following snippet shows only a
  40419. * few settings, see the complete options set below. If the total number of
  40420. * data points exceeds the series'
  40421. * [turboThreshold](#series.areaspline.turboThreshold), this option is not
  40422. * available.
  40423. * ```js
  40424. * data: [{
  40425. * x: 1,
  40426. * y: 4,
  40427. * name: "Point2",
  40428. * color: "#00FF00"
  40429. * }, {
  40430. * x: 1,
  40431. * y: 4,
  40432. * name: "Point1",
  40433. * color: "#FF00FF"
  40434. * }]
  40435. * ```
  40436. *
  40437. * @sample {highcharts} highcharts/chart/reflow-true/
  40438. * Numerical values
  40439. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40440. * Arrays of numeric x and y
  40441. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40442. * Arrays of datetime x and y
  40443. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40444. * Arrays of point.name and y
  40445. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40446. * Config objects
  40447. *
  40448. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40449. * @extends series.line.data
  40450. * @product highcharts highstock
  40451. * @apioption series.areaspline.data
  40452. */
  40453. /**
  40454. * @see [color](#series.areaspline.color)
  40455. * @see [fillOpacity](#series.areaspline.fillOpacity)
  40456. *
  40457. * @apioption series.areaspline.fillColor
  40458. */
  40459. /**
  40460. * @see [color](#series.areaspline.color)
  40461. * @see [fillColor](#series.areaspline.fillColor)
  40462. *
  40463. * @default 0.75
  40464. * @apioption series.areaspline.fillOpacity
  40465. */
  40466. ''; // adds doclets above into transpilat
  40467. return AreaSplineSeries;
  40468. });
  40469. _registerModule(_modules, 'Series/Column/ColumnSeriesDefaults.js', [], function () {
  40470. /* *
  40471. *
  40472. * (c) 2010-2021 Torstein Honsi
  40473. *
  40474. * License: www.highcharts.com/license
  40475. *
  40476. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40477. *
  40478. * */
  40479. /* *
  40480. *
  40481. * API Options
  40482. *
  40483. * */
  40484. /**
  40485. * Column series display one column per value along an X axis.
  40486. *
  40487. * @sample {highcharts} highcharts/demo/column-basic/
  40488. * Column chart
  40489. * @sample {highstock} stock/demo/column/
  40490. * Column chart
  40491. *
  40492. * @extends plotOptions.line
  40493. * @excluding connectEnds, connectNulls, gapSize, gapUnit, linecap,
  40494. * lineWidth, marker, step, useOhlcData
  40495. * @product highcharts highstock
  40496. * @optionparent plotOptions.column
  40497. */
  40498. const ColumnSeriesDefaults = {
  40499. /**
  40500. * The corner radius of the border surrounding each column or bar. A number
  40501. * signifies pixels. A percentage string, like for example `50%`, signifies
  40502. * a relative size. For columns this is relative to the column width, for
  40503. * pies it is relative to the radius and the inner radius.
  40504. *
  40505. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  40506. * Rounded columns
  40507. * @sample highcharts/plotoptions/series-border-radius
  40508. * Column and pie with rounded border
  40509. *
  40510. * @type {number|string|Highcharts.BorderRadiusOptionsObject}
  40511. * @product highcharts highstock gantt
  40512. */
  40513. borderRadius: 3,
  40514. /**
  40515. * When using automatic point colors pulled from the global
  40516. * [colors](colors) or series-specific
  40517. * [plotOptions.column.colors](series.colors) collections, this option
  40518. * determines whether the chart should receive one color per series or
  40519. * one color per point.
  40520. *
  40521. * In styled mode, the `colors` or `series.colors` arrays are not
  40522. * supported, and instead this option gives the points individual color
  40523. * class names on the form `highcharts-color-{n}`.
  40524. *
  40525. * @see [series colors](#plotOptions.column.colors)
  40526. *
  40527. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
  40528. * False by default
  40529. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
  40530. * True
  40531. *
  40532. * @type {boolean}
  40533. * @default false
  40534. * @since 2.0
  40535. * @product highcharts highstock gantt
  40536. * @apioption plotOptions.column.colorByPoint
  40537. */
  40538. /**
  40539. * A series specific or series type specific color set to apply instead
  40540. * of the global [colors](#colors) when [colorByPoint](
  40541. * #plotOptions.column.colorByPoint) is true.
  40542. *
  40543. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  40544. * @since 3.0
  40545. * @product highcharts highstock gantt
  40546. * @apioption plotOptions.column.colors
  40547. */
  40548. /**
  40549. * When `true`, the columns will center in the category, ignoring null
  40550. * or missing points. When `false`, space will be reserved for null or
  40551. * missing points.
  40552. *
  40553. * @sample {highcharts} highcharts/series-column/centerincategory/
  40554. * Center in category
  40555. *
  40556. * @since 8.0.1
  40557. * @product highcharts highstock gantt
  40558. */
  40559. centerInCategory: false,
  40560. /**
  40561. * Padding between each value groups, in x axis units.
  40562. *
  40563. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
  40564. * 0.2 by default
  40565. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
  40566. * No group padding - all columns are evenly spaced
  40567. *
  40568. * @product highcharts highstock gantt
  40569. */
  40570. groupPadding: 0.2,
  40571. /**
  40572. * Whether to group non-stacked columns or to let them render
  40573. * independent of each other. Non-grouped columns will be laid out
  40574. * individually and overlap each other.
  40575. *
  40576. * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
  40577. * Grouping disabled
  40578. * @sample {highstock} highcharts/plotoptions/column-grouping-false/
  40579. * Grouping disabled
  40580. *
  40581. * @type {boolean}
  40582. * @default true
  40583. * @since 2.3.0
  40584. * @product highcharts highstock gantt
  40585. * @apioption plotOptions.column.grouping
  40586. */
  40587. /** @ignore-option */
  40588. marker: null,
  40589. /**
  40590. * The maximum allowed pixel width for a column, translated to the
  40591. * height of a bar in a bar chart. This prevents the columns from
  40592. * becoming too wide when there is a small number of points in the
  40593. * chart.
  40594. *
  40595. * @see [pointWidth](#plotOptions.column.pointWidth)
  40596. *
  40597. * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
  40598. * Limited to 50
  40599. * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
  40600. * Limited to 50
  40601. *
  40602. * @type {number}
  40603. * @since 4.1.8
  40604. * @product highcharts highstock gantt
  40605. * @apioption plotOptions.column.maxPointWidth
  40606. */
  40607. /**
  40608. * Padding between each column or bar, in x axis units.
  40609. *
  40610. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
  40611. * 0.1 by default
  40612. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
  40613. * 0.25
  40614. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
  40615. * 0 for tightly packed columns
  40616. *
  40617. * @product highcharts highstock gantt
  40618. */
  40619. pointPadding: 0.1,
  40620. /**
  40621. * A pixel value specifying a fixed width for each column or bar point.
  40622. * When set to `undefined`, the width is calculated from the
  40623. * `pointPadding` and `groupPadding`. The width effects the dimension
  40624. * that is not based on the point value. For column series it is the
  40625. * hoizontal length and for bar series it is the vertical length.
  40626. *
  40627. * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
  40628. *
  40629. * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
  40630. * 20px wide columns regardless of chart width or the amount of
  40631. * data points
  40632. *
  40633. * @type {number}
  40634. * @since 1.2.5
  40635. * @product highcharts highstock gantt
  40636. * @apioption plotOptions.column.pointWidth
  40637. */
  40638. /**
  40639. * A pixel value specifying a fixed width for the column or bar.
  40640. * Overrides pointWidth on the series.
  40641. *
  40642. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  40643. *
  40644. * @type {number}
  40645. * @default undefined
  40646. * @since 7.0.0
  40647. * @product highcharts highstock gantt
  40648. * @apioption series.column.data.pointWidth
  40649. */
  40650. /**
  40651. * The minimal height for a column or width for a bar. By default,
  40652. * 0 values are not shown. To visualize a 0 (or close to zero) point,
  40653. * set the minimal point length to a pixel value like 3\. In stacked
  40654. * column charts, minPointLength might not be respected for tightly
  40655. * packed values.
  40656. *
  40657. * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
  40658. * Zero base value
  40659. * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
  40660. * Positive and negative close to zero values
  40661. *
  40662. * @product highcharts highstock gantt
  40663. */
  40664. minPointLength: 0,
  40665. /**
  40666. * When the series contains less points than the crop threshold, all
  40667. * points are drawn, event if the points fall outside the visible plot
  40668. * area at the current zoom. The advantage of drawing all points
  40669. * (including markers and columns), is that animation is performed on
  40670. * updates. On the other hand, when the series contains more points than
  40671. * the crop threshold, the series data is cropped to only contain points
  40672. * that fall within the plot area. The advantage of cropping away
  40673. * invisible points is to increase performance on large series.
  40674. *
  40675. * @product highcharts highstock gantt
  40676. */
  40677. cropThreshold: 50,
  40678. /**
  40679. * The X axis range that each point is valid for. This determines the
  40680. * width of the column. On a categorized axis, the range will be 1
  40681. * by default (one category unit). On linear and datetime axes, the
  40682. * range will be computed as the distance between the two closest data
  40683. * points.
  40684. *
  40685. * The default `null` means it is computed automatically, but this
  40686. * option can be used to override the automatic value.
  40687. *
  40688. * This option is set by default to 1 if data sorting is enabled.
  40689. *
  40690. * @sample {highcharts} highcharts/plotoptions/column-pointrange/
  40691. * Set the point range to one day on a data set with one week
  40692. * between the points
  40693. *
  40694. * @type {number|null}
  40695. * @since 2.3
  40696. * @product highcharts highstock gantt
  40697. */
  40698. pointRange: null,
  40699. states: {
  40700. /**
  40701. * Options for the hovered point. These settings override the normal
  40702. * state options when a point is moused over or touched.
  40703. *
  40704. * @extends plotOptions.series.states.hover
  40705. * @excluding halo, lineWidth, lineWidthPlus, marker
  40706. * @product highcharts highstock gantt
  40707. */
  40708. hover: {
  40709. /** @ignore-option */
  40710. halo: false,
  40711. /**
  40712. * A specific border color for the hovered point. Defaults to
  40713. * inherit the normal state border color.
  40714. *
  40715. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40716. * @product highcharts gantt
  40717. * @apioption plotOptions.column.states.hover.borderColor
  40718. */
  40719. /**
  40720. * A specific color for the hovered point.
  40721. *
  40722. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40723. * @product highcharts gantt
  40724. * @apioption plotOptions.column.states.hover.color
  40725. */
  40726. /**
  40727. * How much to brighten the point on interaction. Requires the
  40728. * main color to be defined in hex or rgb(a) format.
  40729. *
  40730. * In styled mode, the hover brightening is by default replaced
  40731. * with a fill-opacity set in the `.highcharts-point:hover`
  40732. * rule.
  40733. *
  40734. * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
  40735. * Brighten by 0.5
  40736. *
  40737. * @product highcharts highstock gantt
  40738. */
  40739. brightness: 0.1
  40740. },
  40741. /**
  40742. * Options for the selected point. These settings override the
  40743. * normal state options when a point is selected.
  40744. *
  40745. * @extends plotOptions.series.states.select
  40746. * @excluding halo, lineWidth, lineWidthPlus, marker
  40747. * @product highcharts highstock gantt
  40748. */
  40749. select: {
  40750. /**
  40751. * A specific color for the selected point.
  40752. *
  40753. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40754. * @default #cccccc
  40755. * @product highcharts highstock gantt
  40756. */
  40757. color: "#cccccc" /* Palette.neutralColor20 */,
  40758. /**
  40759. * A specific border color for the selected point.
  40760. *
  40761. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40762. * @default #000000
  40763. * @product highcharts highstock gantt
  40764. */
  40765. borderColor: "#000000" /* Palette.neutralColor100 */
  40766. }
  40767. },
  40768. dataLabels: {
  40769. align: void 0,
  40770. verticalAlign: void 0,
  40771. /**
  40772. * The y position offset of the label relative to the point in
  40773. * pixels.
  40774. *
  40775. * @type {number}
  40776. */
  40777. y: void 0
  40778. },
  40779. // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
  40780. /** @ignore-option */
  40781. startFromThreshold: true,
  40782. stickyTracking: false,
  40783. tooltip: {
  40784. distance: 6
  40785. },
  40786. /**
  40787. * The Y axis value to serve as the base for the columns, for
  40788. * distinguishing between values above and below a threshold. If `null`,
  40789. * the columns extend from the padding Y axis minimum.
  40790. *
  40791. * @type {number|null}
  40792. * @since 2.0
  40793. * @product highcharts
  40794. */
  40795. threshold: 0,
  40796. /**
  40797. * The width of the border surrounding each column or bar. Defaults to
  40798. * `1` when there is room for a border, but to `0` when the columns are
  40799. * so dense that a border would cover the next column.
  40800. *
  40801. * In styled mode, the stroke width can be set with the
  40802. * `.highcharts-point` rule.
  40803. *
  40804. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  40805. * 2px black border
  40806. *
  40807. * @type {number}
  40808. * @default undefined
  40809. * @product highcharts highstock gantt
  40810. * @apioption plotOptions.column.borderWidth
  40811. */
  40812. /**
  40813. * The color of the border surrounding each column or bar.
  40814. *
  40815. * In styled mode, the border stroke can be set with the
  40816. * `.highcharts-point` rule.
  40817. *
  40818. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  40819. * Dark gray border
  40820. *
  40821. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40822. * @default #ffffff
  40823. * @product highcharts highstock gantt
  40824. */
  40825. borderColor: "#ffffff" /* Palette.backgroundColor */
  40826. };
  40827. /**
  40828. * A `column` series. If the [type](#series.column.type) option is
  40829. * not specified, it is inherited from [chart.type](#chart.type).
  40830. *
  40831. * @extends series,plotOptions.column
  40832. * @excluding connectNulls, dataParser, dataURL, gapSize, gapUnit, linecap,
  40833. * lineWidth, marker, connectEnds, step
  40834. * @product highcharts highstock
  40835. * @apioption series.column
  40836. */
  40837. /**
  40838. * An array of data points for the series. For the `column` series type,
  40839. * points can be given in the following ways:
  40840. *
  40841. * 1. An array of numerical values. In this case, the numerical values will be
  40842. * interpreted as `y` options. The `x` values will be automatically
  40843. * calculated, either starting at 0 and incremented by 1, or from
  40844. * `pointStart` and `pointInterval` given in the series options. If the axis
  40845. * has categories, these will be used. Example:
  40846. * ```js
  40847. * data: [0, 5, 3, 5]
  40848. * ```
  40849. *
  40850. * 2. An array of arrays with 2 values. In this case, the values correspond to
  40851. * `x,y`. If the first value is a string, it is applied as the name of the
  40852. * point, and the `x` value is inferred.
  40853. * ```js
  40854. * data: [
  40855. * [0, 6],
  40856. * [1, 2],
  40857. * [2, 6]
  40858. * ]
  40859. * ```
  40860. *
  40861. * 3. An array of objects with named values. The following snippet shows only a
  40862. * few settings, see the complete options set below. If the total number of
  40863. * data points exceeds the series'
  40864. * [turboThreshold](#series.column.turboThreshold), this option is not
  40865. * available.
  40866. * ```js
  40867. * data: [{
  40868. * x: 1,
  40869. * y: 9,
  40870. * name: "Point2",
  40871. * color: "#00FF00"
  40872. * }, {
  40873. * x: 1,
  40874. * y: 6,
  40875. * name: "Point1",
  40876. * color: "#FF00FF"
  40877. * }]
  40878. * ```
  40879. *
  40880. * @sample {highcharts} highcharts/chart/reflow-true/
  40881. * Numerical values
  40882. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  40883. * Arrays of numeric x and y
  40884. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  40885. * Arrays of datetime x and y
  40886. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  40887. * Arrays of point.name and y
  40888. * @sample {highcharts} highcharts/series/data-array-of-objects/
  40889. * Config objects
  40890. *
  40891. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  40892. * @extends series.line.data
  40893. * @excluding marker
  40894. * @product highcharts highstock
  40895. * @apioption series.column.data
  40896. */
  40897. /**
  40898. * The color of the border surrounding the column or bar.
  40899. *
  40900. * In styled mode, the border stroke can be set with the `.highcharts-point`
  40901. * rule.
  40902. *
  40903. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  40904. * Dark gray border
  40905. *
  40906. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  40907. * @product highcharts highstock
  40908. * @apioption series.column.data.borderColor
  40909. */
  40910. /**
  40911. * The width of the border surrounding the column or bar.
  40912. *
  40913. * In styled mode, the stroke width can be set with the `.highcharts-point`
  40914. * rule.
  40915. *
  40916. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  40917. * 2px black border
  40918. *
  40919. * @type {number}
  40920. * @product highcharts highstock
  40921. * @apioption series.column.data.borderWidth
  40922. */
  40923. /**
  40924. * A name for the dash style to use for the column or bar. Overrides
  40925. * dashStyle on the series.
  40926. *
  40927. * In styled mode, the stroke dash-array can be set with the same classes as
  40928. * listed under [data.color](#series.column.data.color).
  40929. *
  40930. * @see [series.pointWidth](#plotOptions.column.dashStyle)
  40931. *
  40932. * @type {Highcharts.DashStyleValue}
  40933. * @apioption series.column.data.dashStyle
  40934. */
  40935. /**
  40936. * A pixel value specifying a fixed width for the column or bar. Overrides
  40937. * pointWidth on the series. The width effects the dimension that is not based
  40938. * on the point value.
  40939. *
  40940. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  40941. *
  40942. * @type {number}
  40943. * @apioption series.column.data.pointWidth
  40944. */
  40945. /**
  40946. * @excluding halo, lineWidth, lineWidthPlus, marker
  40947. * @product highcharts highstock
  40948. * @apioption series.column.states.hover
  40949. */
  40950. /**
  40951. * @excluding halo, lineWidth, lineWidthPlus, marker
  40952. * @product highcharts highstock
  40953. * @apioption series.column.states.select
  40954. */
  40955. ''; // keeps doclets above in JS file
  40956. /* *
  40957. *
  40958. * Default Export
  40959. *
  40960. * */
  40961. return ColumnSeriesDefaults;
  40962. });
  40963. _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) {
  40964. /* *
  40965. *
  40966. * (c) 2010-2021 Torstein Honsi
  40967. *
  40968. * License: www.highcharts.com/license
  40969. *
  40970. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40971. *
  40972. * */
  40973. const { animObject } = A;
  40974. const { parse: color } = Color;
  40975. const { hasTouch, noop } = H;
  40976. const { clamp, defined, extend, fireEvent, isArray, isNumber, merge, pick, objectEach, relativeLength } = U;
  40977. /* *
  40978. *
  40979. * Class
  40980. *
  40981. * */
  40982. /**
  40983. * The column series type.
  40984. *
  40985. * @private
  40986. * @class
  40987. * @name Highcharts.seriesTypes.column
  40988. *
  40989. * @augments Highcharts.Series
  40990. */
  40991. class ColumnSeries extends Series {
  40992. constructor() {
  40993. /* *
  40994. *
  40995. * Static Properties
  40996. *
  40997. * */
  40998. super(...arguments);
  40999. /* *
  41000. *
  41001. * Properties
  41002. *
  41003. * */
  41004. this.borderWidth = void 0;
  41005. this.data = void 0;
  41006. this.group = void 0;
  41007. this.options = void 0;
  41008. this.points = void 0;
  41009. /* eslint-enable valid-jsdoc */
  41010. }
  41011. /* *
  41012. *
  41013. * Functions
  41014. *
  41015. * */
  41016. /* eslint-disable valid-jsdoc */
  41017. /**
  41018. * Animate the column heights one by one from zero.
  41019. *
  41020. * @private
  41021. * @function Highcharts.seriesTypes.column#animate
  41022. *
  41023. * @param {boolean} init
  41024. * Whether to initialize the animation or run it
  41025. */
  41026. animate(init) {
  41027. const series = this, yAxis = this.yAxis, yAxisPos = yAxis.pos, options = series.options, inverted = this.chart.inverted, attr = {}, translateProp = inverted ?
  41028. 'translateX' :
  41029. 'translateY';
  41030. let translateStart, translatedThreshold;
  41031. if (init) {
  41032. attr.scaleY = 0.001;
  41033. translatedThreshold = clamp(yAxis.toPixels(options.threshold), yAxisPos, yAxisPos + yAxis.len);
  41034. if (inverted) {
  41035. attr.translateX = translatedThreshold - yAxis.len;
  41036. }
  41037. else {
  41038. attr.translateY = translatedThreshold;
  41039. }
  41040. // apply finnal clipping (used in Highcharts Stock) (#7083)
  41041. // animation is done by scaleY, so cliping is for panes
  41042. if (series.clipBox) {
  41043. series.setClip();
  41044. }
  41045. series.group.attr(attr);
  41046. }
  41047. else { // run the animation
  41048. translateStart = Number(series.group.attr(translateProp));
  41049. series.group.animate({ scaleY: 1 }, extend(animObject(series.options.animation), {
  41050. // Do the scale synchronously to ensure smooth
  41051. // updating (#5030, #7228)
  41052. step: function (val, fx) {
  41053. if (series.group) {
  41054. attr[translateProp] = translateStart +
  41055. fx.pos * (yAxisPos - translateStart);
  41056. series.group.attr(attr);
  41057. }
  41058. }
  41059. }));
  41060. }
  41061. }
  41062. /**
  41063. * Initialize the series. Extends the basic Series.init method by
  41064. * marking other series of the same type as dirty.
  41065. *
  41066. * @private
  41067. * @function Highcharts.seriesTypes.column#init
  41068. */
  41069. init(chart, options) {
  41070. super.init.apply(this, arguments);
  41071. const series = this;
  41072. chart = series.chart;
  41073. // if the series is added dynamically, force redraw of other
  41074. // series affected by a new column
  41075. if (chart.hasRendered) {
  41076. chart.series.forEach(function (otherSeries) {
  41077. if (otherSeries.type === series.type) {
  41078. otherSeries.isDirty = true;
  41079. }
  41080. });
  41081. }
  41082. }
  41083. /**
  41084. * Return the width and x offset of the columns adjusted for grouping,
  41085. * groupPadding, pointPadding, pointWidth etc.
  41086. *
  41087. * @private
  41088. * @function Highcharts.seriesTypes.column#getColumnMetrics
  41089. */
  41090. getColumnMetrics() {
  41091. const series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, reversedStacks = xAxis.options.reversedStacks,
  41092. // Keep backward compatibility: reversed xAxis had reversed
  41093. // stacks
  41094. reverseStacks = (xAxis.reversed && !reversedStacks) ||
  41095. (!xAxis.reversed && reversedStacks), stackGroups = {};
  41096. let stackKey, columnCount = 0;
  41097. // Get the total number of column type series. This is called on
  41098. // every series. Consider moving this logic to a chart.orderStacks()
  41099. // function and call it on init, addSeries and removeSeries
  41100. if (options.grouping === false) {
  41101. columnCount = 1;
  41102. }
  41103. else {
  41104. series.chart.series.forEach(function (otherSeries) {
  41105. const otherYAxis = otherSeries.yAxis, otherOptions = otherSeries.options;
  41106. let columnIndex;
  41107. if (otherSeries.type === series.type &&
  41108. (otherSeries.visible ||
  41109. !series.chart.options.chart.ignoreHiddenSeries) &&
  41110. yAxis.len === otherYAxis.len &&
  41111. yAxis.pos === otherYAxis.pos) { // #642, #2086
  41112. if (otherOptions.stacking &&
  41113. otherOptions.stacking !== 'group') {
  41114. stackKey = otherSeries.stackKey;
  41115. if (typeof stackGroups[stackKey] ===
  41116. 'undefined') {
  41117. stackGroups[stackKey] = columnCount++;
  41118. }
  41119. columnIndex = stackGroups[stackKey];
  41120. }
  41121. else if (otherOptions.grouping !== false) { // #1162
  41122. columnIndex = columnCount++;
  41123. }
  41124. otherSeries.columnIndex = columnIndex;
  41125. }
  41126. });
  41127. }
  41128. const categoryWidth = Math.min(Math.abs(xAxis.transA) * ((xAxis.ordinal && xAxis.ordinal.slope) ||
  41129. options.pointRange ||
  41130. xAxis.closestPointRange ||
  41131. xAxis.tickInterval ||
  41132. 1), // #2610
  41133. xAxis.len // #1535
  41134. ), 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,
  41135. // #1251, #3737
  41136. colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0), pointXOffset = pointPadding +
  41137. (groupPadding +
  41138. colIndex * pointOffsetWidth -
  41139. (categoryWidth / 2)) * (reverseStacks ? -1 : 1);
  41140. // Save it for reading in linked series (Error bars particularly)
  41141. series.columnMetrics = {
  41142. width: pointWidth,
  41143. offset: pointXOffset,
  41144. paddedWidth: pointOffsetWidth,
  41145. columnCount
  41146. };
  41147. return series.columnMetrics;
  41148. }
  41149. /**
  41150. * Make the columns crisp. The edges are rounded to the nearest full
  41151. * pixel.
  41152. *
  41153. * @private
  41154. * @function Highcharts.seriesTypes.column#crispCol
  41155. */
  41156. crispCol(x, y, w, h) {
  41157. const chart = this.chart, borderWidth = this.borderWidth, xCrisp = -(borderWidth % 2 ? 0.5 : 0);
  41158. let right, yCrisp = borderWidth % 2 ? 0.5 : 1;
  41159. // Horizontal. We need to first compute the exact right edge, then
  41160. // round it and compute the width from there.
  41161. if (this.options.crisp) {
  41162. right = Math.round(x + w) + xCrisp;
  41163. x = Math.round(x) + xCrisp;
  41164. w = right - x;
  41165. }
  41166. // Vertical
  41167. const bottom = Math.round(y + h) + yCrisp, fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
  41168. y = Math.round(y) + yCrisp;
  41169. h = bottom - y;
  41170. // Top edges are exceptions
  41171. if (fromTop && h) { // #5146
  41172. y -= 1;
  41173. h += 1;
  41174. }
  41175. return {
  41176. x: x,
  41177. y: y,
  41178. width: w,
  41179. height: h
  41180. };
  41181. }
  41182. /**
  41183. * Adjust for missing columns, according to the `centerInCategory`
  41184. * option. Missing columns are either single points or stacks where the
  41185. * point or points are either missing or null.
  41186. *
  41187. * @private
  41188. * @function Highcharts.seriesTypes.column#adjustForMissingColumns
  41189. * @param {number} x
  41190. * The x coordinate of the column, left side
  41191. *
  41192. * @param {number} pointWidth
  41193. * The pointWidth, already computed upstream
  41194. *
  41195. * @param {Highcharts.ColumnPoint} point
  41196. * The point instance
  41197. *
  41198. * @param {Highcharts.ColumnMetricsObject} metrics
  41199. * The series-wide column metrics
  41200. *
  41201. * @return {number}
  41202. * The adjusted x position, or the original if not adjusted
  41203. */
  41204. adjustForMissingColumns(x, pointWidth, point, metrics) {
  41205. const stacking = this.options.stacking;
  41206. if (!point.isNull && metrics.columnCount > 1) {
  41207. const reversedStacks = this.yAxis.options.reversedStacks;
  41208. let indexInCategory = 0, totalInCategory = reversedStacks ? 0 : -metrics.columnCount;
  41209. // Loop over all the stacks on the Y axis. When stacking is enabled,
  41210. // these are real point stacks. When stacking is not enabled, but
  41211. // `centerInCategory` is true, there is one stack handling the
  41212. // grouping of points in each category. This is done in the
  41213. // `setGroupedPoints` function.
  41214. objectEach(this.yAxis.stacking && this.yAxis.stacking.stacks, (stack) => {
  41215. if (typeof point.x === 'number') {
  41216. const stackItem = stack[point.x.toString()];
  41217. if (stackItem) {
  41218. const pointValues = stackItem.points[this.index];
  41219. // If true `stacking` is enabled, count the total
  41220. // number of non-null stacks in the category, and
  41221. // note which index this point is within those
  41222. // stacks.
  41223. if (stacking) {
  41224. if (pointValues) {
  41225. indexInCategory = totalInCategory;
  41226. }
  41227. if (stackItem.hasValidPoints) {
  41228. reversedStacks ? // #16169
  41229. totalInCategory++ : totalInCategory--;
  41230. }
  41231. // If `stacking` is not enabled, look for the index
  41232. }
  41233. else if (isArray(pointValues)) {
  41234. // If there are multiple points with the same X
  41235. // then gather all series in category, and
  41236. // assign index
  41237. let seriesIndexes = Object
  41238. .keys(stackItem.points)
  41239. .filter((pointKey) =>
  41240. // Filter out duplicate X's
  41241. !pointKey.match(',') &&
  41242. // Filter out null points
  41243. stackItem.points[pointKey] &&
  41244. stackItem.points[pointKey].length > 1)
  41245. .map(parseFloat)
  41246. .sort((a, b) => b - a);
  41247. indexInCategory = seriesIndexes.indexOf(this.index);
  41248. totalInCategory = seriesIndexes.length;
  41249. }
  41250. }
  41251. }
  41252. });
  41253. // Compute the adjusted x position
  41254. const boxWidth = (totalInCategory - 1) * metrics.paddedWidth +
  41255. pointWidth;
  41256. x = (point.plotX || 0) + boxWidth / 2 - pointWidth -
  41257. indexInCategory * metrics.paddedWidth;
  41258. }
  41259. return x;
  41260. }
  41261. /**
  41262. * Translate each point to the plot area coordinate system and find
  41263. * shape positions
  41264. *
  41265. * @private
  41266. * @function Highcharts.seriesTypes.column#translate
  41267. */
  41268. translate() {
  41269. const series = this, chart = series.chart, options = series.options, dense = series.dense =
  41270. series.closestPointRange * series.xAxis.transA < 2, borderWidth = series.borderWidth = pick(options.borderWidth, dense ? 0 : 1 // #3635
  41271. ), 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;
  41272. // postprocessed for border width
  41273. let seriesBarW = series.barW =
  41274. Math.max(seriesPointWidth, 1 + 2 * borderWidth), translatedThreshold = series.translatedThreshold =
  41275. yAxis.getThreshold(threshold);
  41276. if (chart.inverted) {
  41277. translatedThreshold -= 0.5; // #3355
  41278. }
  41279. // When the pointPadding is 0, we want the columns to be packed
  41280. // tightly, so we allow individual columns to have individual sizes.
  41281. // When pointPadding is greater, we strive for equal-width columns
  41282. // (#2694).
  41283. if (options.pointPadding) {
  41284. seriesBarW = Math.ceil(seriesBarW);
  41285. }
  41286. Series.prototype.translate.apply(series);
  41287. // Record the new values
  41288. series.points.forEach(function (point) {
  41289. const yBottom = pick(point.yBottom, translatedThreshold), safeDistance = 999 + Math.abs(yBottom), plotX = point.plotX || 0,
  41290. // Don't draw too far outside plot area (#1303, #2241,
  41291. // #4264)
  41292. plotY = clamp(point.plotY, -safeDistance, yAxis.len + safeDistance), stackBox = point.stackBox;
  41293. let up, barY = Math.min(plotY, yBottom), barH = Math.max(plotY, yBottom) - barY, pointWidth = seriesPointWidth, barX = plotX + seriesXOffset, barW = seriesBarW;
  41294. // Handle options.minPointLength
  41295. if (minPointLength && Math.abs(barH) < minPointLength) {
  41296. barH = minPointLength;
  41297. up = (!yAxis.reversed && !point.negative) ||
  41298. (yAxis.reversed && point.negative);
  41299. // Reverse zeros if there's no positive value in the series
  41300. // in visible range (#7046)
  41301. if (isNumber(threshold) &&
  41302. isNumber(dataMax) &&
  41303. point.y === threshold &&
  41304. dataMax <= threshold &&
  41305. // and if there's room for it (#7311)
  41306. (yAxis.min || 0) < threshold &&
  41307. // if all points are the same value (i.e zero) not draw
  41308. // as negative points (#10646), but only if there's room
  41309. // for it (#14876)
  41310. (dataMin !== dataMax || (yAxis.max || 0) <= threshold)) {
  41311. up = !up;
  41312. point.negative = !point.negative;
  41313. }
  41314. // If stacked...
  41315. barY = (Math.abs(barY - translatedThreshold) > minPointLength ?
  41316. // ...keep position
  41317. yBottom - minPointLength :
  41318. // #1485, #4051
  41319. translatedThreshold -
  41320. (up ? minPointLength : 0));
  41321. }
  41322. // Handle point.options.pointWidth
  41323. // @todo Handle grouping/stacking too. Calculate offset properly
  41324. if (defined(point.options.pointWidth)) {
  41325. pointWidth = barW =
  41326. Math.ceil(point.options.pointWidth);
  41327. barX -= Math.round((pointWidth - seriesPointWidth) / 2);
  41328. }
  41329. // Adjust for null or missing points
  41330. if (options.centerInCategory) {
  41331. barX = series.adjustForMissingColumns(barX, pointWidth, point, metrics);
  41332. }
  41333. // Cache for access in polar
  41334. point.barX = barX;
  41335. point.pointWidth = pointWidth;
  41336. // Fix the tooltip on center of grouped columns (#1216, #424,
  41337. // #3648)
  41338. point.tooltipPos = chart.inverted ?
  41339. [
  41340. clamp(yAxis.len + yAxis.pos - chart.plotLeft - plotY, yAxis.pos - chart.plotLeft, yAxis.len + yAxis.pos - chart.plotLeft),
  41341. xAxis.len + xAxis.pos - chart.plotTop - barX - barW / 2,
  41342. barH
  41343. ] :
  41344. [
  41345. xAxis.left - chart.plotLeft + barX + barW / 2,
  41346. clamp(plotY + yAxis.pos -
  41347. chart.plotTop, yAxis.pos - chart.plotTop, yAxis.len + yAxis.pos - chart.plotTop),
  41348. barH
  41349. ];
  41350. // Register shape type and arguments to be used in drawPoints. Allow
  41351. // `shapeType` defined on `pointClass` level.
  41352. point.shapeType = series.pointClass.prototype.shapeType ||
  41353. 'roundedRect';
  41354. point.shapeArgs = series.crispCol(barX,
  41355. // #3169, drilldown from null must have a position to work from.
  41356. // #6585, dataLabel should be placed on xAxis, not floating in
  41357. // the middle of the chart.
  41358. point.isNull ? translatedThreshold : barY, barW, point.isNull ? 0 : barH);
  41359. });
  41360. // Fire a specific event after column translate. We could instead apply
  41361. // all the column logic in an `afterTranslate` event handler, but there
  41362. // are so many other series types that use the column translation, that
  41363. // it is more convenient to have a specific event for it.
  41364. fireEvent(this, 'afterColumnTranslate');
  41365. }
  41366. /**
  41367. * Columns have no graph
  41368. *
  41369. * @private
  41370. * @function Highcharts.seriesTypes.column#drawGraph
  41371. */
  41372. drawGraph() {
  41373. this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data');
  41374. }
  41375. /**
  41376. * Get presentational attributes
  41377. *
  41378. * @private
  41379. * @function Highcharts.seriesTypes.column#pointAttribs
  41380. */
  41381. pointAttribs(point, state) {
  41382. const options = this.options, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth';
  41383. let stateOptions, zone, brightness, fill = (point && point.color) || this.color,
  41384. // set to fill when borderColor null:
  41385. stroke = ((point && point[strokeOption]) ||
  41386. options[strokeOption] ||
  41387. fill), dashstyle = (point && point.options.dashStyle) || options.dashStyle, strokeWidth = (point && point[strokeWidthOption]) ||
  41388. options[strokeWidthOption] ||
  41389. this[strokeWidthOption] || 0, opacity = pick(point && point.opacity, options.opacity, 1);
  41390. // Handle zone colors
  41391. if (point && this.zones.length) {
  41392. zone = point.getZone();
  41393. // When zones are present, don't use point.color (#4267).
  41394. // Changed order (#6527), added support for colorAxis (#10670)
  41395. fill = (point.options.color ||
  41396. (zone && (zone.color || point.nonZonedColor)) ||
  41397. this.color);
  41398. if (zone) {
  41399. stroke = zone.borderColor || stroke;
  41400. dashstyle = zone.dashStyle || dashstyle;
  41401. strokeWidth = zone.borderWidth || strokeWidth;
  41402. }
  41403. }
  41404. // Select or hover states
  41405. if (state && point) {
  41406. stateOptions = merge(options.states[state],
  41407. // #6401
  41408. point.options.states &&
  41409. point.options.states[state] ||
  41410. {});
  41411. brightness = stateOptions.brightness;
  41412. fill =
  41413. stateOptions.color || (typeof brightness !== 'undefined' &&
  41414. color(fill)
  41415. .brighten(stateOptions.brightness)
  41416. .get()) || fill;
  41417. stroke = stateOptions[strokeOption] || stroke;
  41418. strokeWidth =
  41419. stateOptions[strokeWidthOption] || strokeWidth;
  41420. dashstyle = stateOptions.dashStyle || dashstyle;
  41421. opacity = pick(stateOptions.opacity, opacity);
  41422. }
  41423. const ret = {
  41424. fill: fill,
  41425. stroke: stroke,
  41426. 'stroke-width': strokeWidth,
  41427. opacity: opacity
  41428. };
  41429. if (dashstyle) {
  41430. ret.dashstyle = dashstyle;
  41431. }
  41432. return ret;
  41433. }
  41434. /**
  41435. * Draw the columns. For bars, the series.group is rotated, so the same
  41436. * coordinates apply for columns and bars. This method is inherited by
  41437. * scatter series.
  41438. *
  41439. * @private
  41440. * @function Highcharts.seriesTypes.column#drawPoints
  41441. */
  41442. drawPoints(points = this.points) {
  41443. const series = this, chart = this.chart, options = series.options, renderer = chart.renderer, animationLimit = options.animationLimit || 250;
  41444. let shapeArgs;
  41445. // draw the columns
  41446. points.forEach(function (point) {
  41447. const plotY = point.plotY;
  41448. let graphic = point.graphic, hasGraphic = !!graphic, verb = graphic && chart.pointCount < animationLimit ?
  41449. 'animate' : 'attr';
  41450. if (isNumber(plotY) && point.y !== null) {
  41451. shapeArgs = point.shapeArgs;
  41452. // When updating a series between 2d and 3d or cartesian and
  41453. // polar, the shape type changes.
  41454. if (graphic && point.hasNewShapeType()) {
  41455. graphic = graphic.destroy();
  41456. }
  41457. // Set starting position for point sliding animation.
  41458. if (series.enabledDataSorting) {
  41459. point.startXPos = series.xAxis.reversed ?
  41460. -(shapeArgs ? (shapeArgs.width || 0) : 0) :
  41461. series.xAxis.width;
  41462. }
  41463. if (!graphic) {
  41464. point.graphic = graphic =
  41465. renderer[point.shapeType](shapeArgs)
  41466. .add(point.group || series.group);
  41467. if (graphic &&
  41468. series.enabledDataSorting &&
  41469. chart.hasRendered &&
  41470. chart.pointCount < animationLimit) {
  41471. graphic.attr({
  41472. x: point.startXPos
  41473. });
  41474. hasGraphic = true;
  41475. verb = 'animate';
  41476. }
  41477. }
  41478. if (graphic && hasGraphic) { // update
  41479. graphic[verb](merge(shapeArgs));
  41480. }
  41481. // Presentational
  41482. if (!chart.styledMode) {
  41483. graphic[verb](series.pointAttribs(point, (point.selected && 'select')))
  41484. .shadow(point.allowShadow !== false && options.shadow);
  41485. }
  41486. if (graphic) {
  41487. graphic.addClass(point.getClassName(), true);
  41488. graphic.attr({
  41489. visibility: point.visible ? 'inherit' : 'hidden'
  41490. });
  41491. }
  41492. }
  41493. else if (graphic) {
  41494. point.graphic = graphic.destroy(); // #1269
  41495. }
  41496. });
  41497. }
  41498. /**
  41499. * Draw the tracker for a point.
  41500. * @private
  41501. */
  41502. drawTracker(points = this.points) {
  41503. const series = this, chart = series.chart, pointer = chart.pointer, onMouseOver = function (e) {
  41504. const point = pointer.getPointFromEvent(e);
  41505. // undefined on graph in scatterchart
  41506. if (typeof point !== 'undefined' &&
  41507. series.options.enableMouseTracking) {
  41508. pointer.isDirectTouch = true;
  41509. point.onMouseOver(e);
  41510. }
  41511. };
  41512. let dataLabels;
  41513. // Add reference to the point
  41514. points.forEach(function (point) {
  41515. dataLabels = (isArray(point.dataLabels) ?
  41516. point.dataLabels :
  41517. (point.dataLabel ? [point.dataLabel] : []));
  41518. if (point.graphic) {
  41519. point.graphic.element.point = point;
  41520. }
  41521. dataLabels.forEach(function (dataLabel) {
  41522. if (dataLabel.div) {
  41523. dataLabel.div.point = point;
  41524. }
  41525. else {
  41526. dataLabel.element.point = point;
  41527. }
  41528. });
  41529. });
  41530. // Add the event listeners, we need to do this only once
  41531. if (!series._hasTracking) {
  41532. series.trackerGroups.forEach(function (key) {
  41533. if (series[key]) {
  41534. // we don't always have dataLabelsGroup
  41535. series[key]
  41536. .addClass('highcharts-tracker')
  41537. .on('mouseover', onMouseOver)
  41538. .on('mouseout', function (e) {
  41539. pointer.onTrackerMouseOut(e);
  41540. });
  41541. if (hasTouch) {
  41542. series[key].on('touchstart', onMouseOver);
  41543. }
  41544. if (!chart.styledMode && series.options.cursor) {
  41545. series[key]
  41546. .css({ cursor: series.options.cursor });
  41547. }
  41548. }
  41549. });
  41550. series._hasTracking = true;
  41551. }
  41552. fireEvent(this, 'afterDrawTracker');
  41553. }
  41554. /**
  41555. * Remove this series from the chart
  41556. *
  41557. * @private
  41558. * @function Highcharts.seriesTypes.column#remove
  41559. */
  41560. remove() {
  41561. const series = this, chart = series.chart;
  41562. // column and bar series affects other series of the same type
  41563. // as they are either stacked or grouped
  41564. if (chart.hasRendered) {
  41565. chart.series.forEach(function (otherSeries) {
  41566. if (otherSeries.type === series.type) {
  41567. otherSeries.isDirty = true;
  41568. }
  41569. });
  41570. }
  41571. Series.prototype.remove.apply(series, arguments);
  41572. }
  41573. }
  41574. ColumnSeries.defaultOptions = merge(Series.defaultOptions, ColumnSeriesDefaults);
  41575. extend(ColumnSeries.prototype, {
  41576. cropShoulder: 0,
  41577. // When tooltip is not shared, this series (and derivatives) requires
  41578. // direct touch/hover. KD-tree does not apply.
  41579. directTouch: true,
  41580. getSymbol: noop,
  41581. // use separate negative stacks, unlike area stacks where a negative
  41582. // point is substracted from previous (#1910)
  41583. negStacks: true,
  41584. trackerGroups: ['group', 'dataLabelsGroup']
  41585. });
  41586. SeriesRegistry.registerSeriesType('column', ColumnSeries);
  41587. /* *
  41588. *
  41589. * Default Export
  41590. *
  41591. * */
  41592. /* *
  41593. *
  41594. * API Declarations
  41595. *
  41596. * */
  41597. /**
  41598. * Adjusted width and x offset of the columns for grouping.
  41599. *
  41600. * @private
  41601. * @interface Highcharts.ColumnMetricsObject
  41602. */ /**
  41603. * Width of the columns.
  41604. * @name Highcharts.ColumnMetricsObject#width
  41605. * @type {number}
  41606. */ /**
  41607. * Offset of the columns.
  41608. * @name Highcharts.ColumnMetricsObject#offset
  41609. * @type {number}
  41610. */
  41611. ''; // detach doclets above
  41612. return ColumnSeries;
  41613. });
  41614. _registerModule(_modules, 'Core/Series/DataLabel.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Templating.js'], _modules['Core/Utilities.js']], function (A, F, U) {
  41615. /* *
  41616. *
  41617. * (c) 2010-2021 Torstein Honsi
  41618. *
  41619. * License: www.highcharts.com/license
  41620. *
  41621. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41622. *
  41623. * */
  41624. const { getDeferredAnimation } = A;
  41625. const { format } = F;
  41626. const { defined, extend, fireEvent, isArray, isString, merge, objectEach, pick, splat } = U;
  41627. /* *
  41628. *
  41629. * Composition
  41630. *
  41631. * */
  41632. /* eslint-disable valid-jsdoc */
  41633. var DataLabel;
  41634. (function (DataLabel) {
  41635. /* *
  41636. *
  41637. * Declarations
  41638. *
  41639. * */
  41640. /* *
  41641. *
  41642. * Constants
  41643. *
  41644. * */
  41645. const composedMembers = [];
  41646. /* *
  41647. *
  41648. * Functions
  41649. *
  41650. * */
  41651. /* eslint-disable valid-jsdoc */
  41652. /**
  41653. * Align each individual data label.
  41654. * @private
  41655. */
  41656. function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
  41657. 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) &&
  41658. defined(plotY) &&
  41659. chart.isInsidePlot(plotX, Math.round(plotY), {
  41660. inverted,
  41661. paneCoordinates: true,
  41662. series
  41663. }), setStartPos = (alignOptions) => {
  41664. if (enabledDataSorting && series.xAxis && !justify) {
  41665. series.setDataLabelStartPos(point, dataLabel, isNew, isInsidePlot, alignOptions);
  41666. }
  41667. };
  41668. let baseline, rotCorr, // rotation correction
  41669. // Math.round for rounding errors (#2683), alignTo to allow column
  41670. // labels (#2700)
  41671. alignAttr, // the final position;
  41672. justify = pick(options.overflow, (enabledDataSorting ? 'none' : 'justify')) === 'justify', visible = this.visible &&
  41673. point.visible !== false &&
  41674. defined(plotX) &&
  41675. (point.series.forceDL ||
  41676. (enabledDataSorting && !justify) ||
  41677. isInsidePlot ||
  41678. (
  41679. // If the data label is inside the align box, it is
  41680. // enough that parts of the align box is inside the
  41681. // plot area (#12370). When stacking, it is always
  41682. // inside regardless of the option (#15148).
  41683. pick(options.inside, !!this.options.stacking) &&
  41684. alignTo &&
  41685. chart.isInsidePlot(plotX, inverted ?
  41686. alignTo.x + 1 :
  41687. alignTo.y + alignTo.height - 1, {
  41688. inverted,
  41689. paneCoordinates: true,
  41690. series
  41691. })));
  41692. const pos = point.pos();
  41693. if (visible && pos) {
  41694. if (rotation) {
  41695. dataLabel.attr({ align });
  41696. }
  41697. let bBox = dataLabel.getBBox(true), bBoxCorrection = [0, 0];
  41698. baseline = chart.renderer.fontMetrics(dataLabel).b;
  41699. // The alignment box is a singular point
  41700. alignTo = extend({
  41701. x: pos[0],
  41702. y: Math.round(pos[1]),
  41703. width: 0,
  41704. height: 0
  41705. }, alignTo);
  41706. // Add the text size for alignment calculation
  41707. extend(options, {
  41708. width: bBox.width,
  41709. height: bBox.height
  41710. });
  41711. // Allow a hook for changing alignment in the last moment, then do
  41712. // the alignment
  41713. if (rotation) {
  41714. justify = false; // Not supported for rotated text
  41715. rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
  41716. alignAttr = {
  41717. x: (alignTo.x +
  41718. (options.x || 0) +
  41719. alignTo.width / 2 +
  41720. rotCorr.x),
  41721. y: (alignTo.y +
  41722. (options.y || 0) +
  41723. { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
  41724. alignTo.height)
  41725. };
  41726. bBoxCorrection = [
  41727. bBox.x - Number(dataLabel.attr('x')),
  41728. bBox.y - Number(dataLabel.attr('y'))
  41729. ];
  41730. setStartPos(alignAttr); // data sorting
  41731. dataLabel[isNew ? 'attr' : 'animate'](alignAttr);
  41732. }
  41733. else {
  41734. setStartPos(alignTo); // data sorting
  41735. dataLabel.align(options, void 0, alignTo);
  41736. alignAttr = dataLabel.alignAttr;
  41737. }
  41738. // Handle justify or crop
  41739. if (justify && alignTo.height >= 0) { // #8830
  41740. this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew);
  41741. // Now check that the data label is within the plot area
  41742. }
  41743. else if (pick(options.crop, true)) {
  41744. let { x, y } = alignAttr;
  41745. x += bBoxCorrection[0];
  41746. y += bBoxCorrection[1];
  41747. // Uncomment this block to visualize the bounding boxes used for
  41748. // determining visibility
  41749. /*
  41750. chart.renderer.rect(
  41751. chart.plotLeft + alignAttr.x + bBox.x,
  41752. chart.plotTop + alignAttr.y + bBox.y + 9999,
  41753. bBox.width,
  41754. bBox.height
  41755. ).attr({
  41756. stroke: 'rgba(0, 0, 0, 0.3)',
  41757. 'stroke-width': 0.5
  41758. }).add();
  41759. chart.renderer.circle(
  41760. chart.plotLeft + alignAttr.x,
  41761. chart.plotTop + alignAttr.y,
  41762. 2
  41763. ).attr({
  41764. fill: 'red',
  41765. zIndex: 20
  41766. }).add();
  41767. // */
  41768. visible =
  41769. chart.isInsidePlot(x, y, {
  41770. paneCoordinates: true,
  41771. series
  41772. }) &&
  41773. chart.isInsidePlot(x + bBox.width, y + bBox.height, {
  41774. paneCoordinates: true,
  41775. series
  41776. });
  41777. }
  41778. // When we're using a shape, make it possible with a connector or an
  41779. // arrow pointing to thie point
  41780. if (options.shape && !rotation) {
  41781. dataLabel[isNew ? 'attr' : 'animate']({
  41782. anchorX: pos[0],
  41783. anchorY: pos[1]
  41784. });
  41785. }
  41786. }
  41787. // To use alignAttr property in hideOverlappingLabels
  41788. if (isNew && enabledDataSorting) {
  41789. dataLabel.placed = false;
  41790. }
  41791. // Show or hide based on the final aligned position
  41792. if (!visible && (!enabledDataSorting || justify)) {
  41793. dataLabel.hide();
  41794. dataLabel.placed = false; // don't animate back in
  41795. }
  41796. else {
  41797. dataLabel.show();
  41798. }
  41799. }
  41800. /**
  41801. * Handle the dataLabels.filter option.
  41802. * @private
  41803. */
  41804. function applyFilter(point, options) {
  41805. const filter = options.filter;
  41806. if (filter) {
  41807. const op = filter.operator;
  41808. const prop = point[filter.property];
  41809. const val = filter.value;
  41810. if ((op === '>' && prop > val) ||
  41811. (op === '<' && prop < val) ||
  41812. (op === '>=' && prop >= val) ||
  41813. (op === '<=' && prop <= val) ||
  41814. (op === '==' && prop == val) || // eslint-disable-line eqeqeq
  41815. (op === '===' && prop === val)) {
  41816. return true;
  41817. }
  41818. return false;
  41819. }
  41820. return true;
  41821. }
  41822. /**
  41823. * @private
  41824. */
  41825. function compose(SeriesClass) {
  41826. if (U.pushUnique(composedMembers, SeriesClass)) {
  41827. const seriesProto = SeriesClass.prototype;
  41828. seriesProto.initDataLabelsGroup = initDataLabelsGroup;
  41829. seriesProto.initDataLabels = initDataLabels;
  41830. seriesProto.alignDataLabel = alignDataLabel;
  41831. seriesProto.drawDataLabels = drawDataLabels;
  41832. seriesProto.justifyDataLabel = justifyDataLabel;
  41833. seriesProto.setDataLabelStartPos = setDataLabelStartPos;
  41834. }
  41835. }
  41836. DataLabel.compose = compose;
  41837. /**
  41838. * Create the SVGElement group for dataLabels
  41839. * @private
  41840. */
  41841. function initDataLabelsGroup() {
  41842. return this.plotGroup('dataLabelsGroup', 'data-labels', this.hasRendered ? 'inherit' : 'hidden', // #5133, #10220
  41843. this.options.dataLabels.zIndex || 6);
  41844. }
  41845. /**
  41846. * Init the data labels with the correct animation
  41847. * @private
  41848. */
  41849. function initDataLabels(animationConfig) {
  41850. const series = this, hasRendered = series.hasRendered || 0;
  41851. // Create a separate group for the data labels to avoid rotation
  41852. const dataLabelsGroup = this.initDataLabelsGroup()
  41853. .attr({ opacity: +hasRendered }); // #3300
  41854. if (!hasRendered && dataLabelsGroup) {
  41855. if (series.visible) { // #2597, #3023, #3024
  41856. dataLabelsGroup.show();
  41857. }
  41858. if (series.options.animation) {
  41859. dataLabelsGroup.animate({ opacity: 1 }, animationConfig);
  41860. }
  41861. else {
  41862. dataLabelsGroup.attr({ opacity: 1 });
  41863. }
  41864. }
  41865. return dataLabelsGroup;
  41866. }
  41867. /**
  41868. * Draw the data labels
  41869. * @private
  41870. */
  41871. function drawDataLabels(points = this.points) {
  41872. var _a,
  41873. _b;
  41874. 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) ||
  41875. (isString(backgroundColor) && backgroundColor) ||
  41876. "#000000" /* Palette.neutralColor100 */);
  41877. let seriesDlOptions = seriesOptions.dataLabels, pointOptions, dataLabelsGroup;
  41878. const firstDLOptions = splat(seriesDlOptions)[0], dataLabelAnim = firstDLOptions.animation, animationConfig = firstDLOptions.defer ?
  41879. getDeferredAnimation(chart, dataLabelAnim, series) :
  41880. { defer: 0, duration: 0 };
  41881. // Merge in plotOptions.dataLabels for series
  41882. 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);
  41883. fireEvent(this, 'drawDataLabels');
  41884. if (isArray(seriesDlOptions) ||
  41885. seriesDlOptions.enabled ||
  41886. series._hasPointLabels) {
  41887. dataLabelsGroup = this.initDataLabels(animationConfig);
  41888. // Make the labels for each point
  41889. points.forEach((point) => {
  41890. var _a;
  41891. const dataLabels = point.dataLabels || [];
  41892. // Merge in series options for the point.
  41893. // @note dataLabelAttribs (like pointAttribs) would eradicate
  41894. // the need for dlOptions, and simplify the section below.
  41895. pointOptions = splat(mergeArrays(seriesDlOptions,
  41896. // dlOptions is used in treemaps
  41897. point.dlOptions || ((_a = point.options) === null || _a === void 0 ? void 0 : _a.dataLabels)));
  41898. // Handle each individual data label for this point
  41899. pointOptions.forEach((labelOptions, i) => {
  41900. var _a;
  41901. // Options for one datalabel
  41902. const labelEnabled = (labelOptions.enabled &&
  41903. // #2282, #4641, #7112, #10049
  41904. (!point.isNull || point.dataLabelOnNull) &&
  41905. applyFilter(point, labelOptions)), connector = point.connectors ?
  41906. point.connectors[i] :
  41907. point.connector, style = labelOptions.style || {};
  41908. let labelConfig, formatString, labelText, rotation, attr = {}, dataLabel = dataLabels[i], isNew = !dataLabel;
  41909. const labelDistance = pick(labelOptions.distance, point.labelDistance);
  41910. if (labelEnabled) {
  41911. // Create individual options structure that can be
  41912. // extended without affecting others
  41913. formatString = pick(labelOptions[point.formatPrefix + 'Format'], labelOptions.format);
  41914. labelConfig = point.getLabelConfig();
  41915. labelText = defined(formatString) ?
  41916. format(formatString, labelConfig, chart) :
  41917. (labelOptions[point.formatPrefix + 'Formatter'] ||
  41918. labelOptions.formatter).call(labelConfig, labelOptions);
  41919. rotation = labelOptions.rotation;
  41920. if (!chart.styledMode) {
  41921. // Determine the color
  41922. style.color = pick(labelOptions.color, style.color, isString(series.color) ? series.color : void 0, "#000000" /* Palette.neutralColor100 */);
  41923. // Get automated contrast color
  41924. if (style.color === 'contrast') {
  41925. point.contrastColor = renderer.getContrast((point.color || series.color));
  41926. style.color = ((!defined(labelDistance) &&
  41927. labelOptions.inside) ||
  41928. (labelDistance || 0) < 0 ||
  41929. seriesOptions.stacking) ?
  41930. point.contrastColor :
  41931. contrastColor;
  41932. }
  41933. else {
  41934. delete point.contrastColor;
  41935. }
  41936. if (seriesOptions.cursor) {
  41937. style.cursor = seriesOptions.cursor;
  41938. }
  41939. }
  41940. attr = {
  41941. r: labelOptions.borderRadius || 0,
  41942. rotation,
  41943. padding: labelOptions.padding,
  41944. zIndex: 1
  41945. };
  41946. if (!chart.styledMode) {
  41947. const { backgroundColor, borderColor } = labelOptions;
  41948. attr.fill = backgroundColor === 'auto' ?
  41949. point.color :
  41950. backgroundColor;
  41951. attr.stroke = borderColor === 'auto' ?
  41952. point.color :
  41953. borderColor;
  41954. attr['stroke-width'] = labelOptions.borderWidth;
  41955. }
  41956. // Remove unused attributes (#947)
  41957. objectEach(attr, (val, name) => {
  41958. if (typeof val === 'undefined') {
  41959. delete attr[name];
  41960. }
  41961. });
  41962. }
  41963. // If the point is outside the plot area, or the label
  41964. // changes properties that we cannot change, destroy it and
  41965. // build a new one below. #678, #820.
  41966. if (dataLabel && (!labelEnabled ||
  41967. !defined(labelText) ||
  41968. !!dataLabel.div !== !!labelOptions.useHTML ||
  41969. (
  41970. // Change from no rotation to rotation and
  41971. // vice versa. Don't use defined() because
  41972. // rotation = 0 means also rotation = undefined
  41973. (!dataLabel.rotation ||
  41974. !labelOptions.rotation) &&
  41975. dataLabel.rotation !== labelOptions.rotation))) {
  41976. dataLabel = void 0;
  41977. isNew = true;
  41978. if (connector && point.connector) {
  41979. point.connector = point.connector.destroy();
  41980. if (point.connectors) {
  41981. // Remove point.connectors if this was the last
  41982. // one
  41983. if (point.connectors.length === 1) {
  41984. delete point.connectors;
  41985. }
  41986. else {
  41987. delete point.connectors[i];
  41988. }
  41989. }
  41990. }
  41991. }
  41992. // Individual labels are disabled if the are explicitly
  41993. // disabled in the point options, or if they fall outside
  41994. // the plot area.
  41995. if (labelEnabled && defined(labelText)) {
  41996. if (!dataLabel) {
  41997. // Create new label element
  41998. dataLabel = rotation ?
  41999. // Labels don't rotate, use text element
  42000. renderer.text(labelText, 0, 0, labelOptions.useHTML)
  42001. .addClass('highcharts-data-label') :
  42002. // We can use label
  42003. renderer.label(labelText, 0, 0, labelOptions.shape, void 0, void 0, labelOptions.useHTML, void 0, 'data-label');
  42004. if (dataLabel) {
  42005. dataLabel.addClass(' highcharts-data-label-color-' +
  42006. point.colorIndex +
  42007. ' ' + (labelOptions.className || '') +
  42008. ( // #3398
  42009. labelOptions.useHTML ?
  42010. ' highcharts-tracker' :
  42011. ''));
  42012. }
  42013. }
  42014. else {
  42015. // Use old element and just update text
  42016. attr.text = labelText;
  42017. }
  42018. // Store data label options for later access
  42019. if (dataLabel) {
  42020. dataLabel.options = labelOptions;
  42021. dataLabel.attr(attr);
  42022. if (!chart.styledMode) {
  42023. // Styles must be applied before add in order to
  42024. // read text bounding box
  42025. dataLabel.css(style).shadow(labelOptions.shadow);
  42026. }
  42027. const textPathOptions = labelOptions[point.formatPrefix + 'TextPath'] || labelOptions.textPath;
  42028. if (textPathOptions && !labelOptions.useHTML) {
  42029. dataLabel.setTextPath(((_a = point.getDataLabelPath) === null || _a === void 0 ? void 0 : _a.call(point, dataLabel)) ||
  42030. point.graphic, textPathOptions);
  42031. if (point.dataLabelPath &&
  42032. !textPathOptions.enabled) {
  42033. // clean the DOM
  42034. point.dataLabelPath = (point.dataLabelPath.destroy());
  42035. }
  42036. }
  42037. if (!dataLabel.added) {
  42038. dataLabel.add(dataLabelsGroup);
  42039. }
  42040. // Now the data label is created and placed at 0,0,
  42041. // so we need to align it
  42042. series.alignDataLabel(point, dataLabel, labelOptions, void 0, isNew);
  42043. dataLabel.isActive = true;
  42044. if (dataLabels[i] && dataLabels[i] !== dataLabel) {
  42045. dataLabels[i].destroy();
  42046. }
  42047. dataLabels[i] = dataLabel;
  42048. }
  42049. }
  42050. });
  42051. // Destroy and remove the inactive ones
  42052. let j = dataLabels.length;
  42053. while (j--) {
  42054. if (dataLabels[j].isActive) {
  42055. dataLabels[j].isActive = false;
  42056. }
  42057. else {
  42058. dataLabels[j].destroy();
  42059. dataLabels.splice(j, 1);
  42060. }
  42061. }
  42062. // Write back
  42063. point.dataLabel = dataLabels[0];
  42064. point.dataLabels = dataLabels;
  42065. });
  42066. }
  42067. fireEvent(this, 'afterDrawDataLabels');
  42068. }
  42069. /**
  42070. * If data labels fall partly outside the plot area, align them back in, in
  42071. * a way that doesn't hide the point.
  42072. * @private
  42073. */
  42074. function justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew) {
  42075. const chart = this.chart, align = options.align, verticalAlign = options.verticalAlign, padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
  42076. let { x = 0, y = 0 } = options, off, justified;
  42077. // Off left
  42078. off = (alignAttr.x || 0) + padding;
  42079. if (off < 0) {
  42080. if (align === 'right' && x >= 0) {
  42081. options.align = 'left';
  42082. options.inside = true;
  42083. }
  42084. else {
  42085. x -= off;
  42086. }
  42087. justified = true;
  42088. }
  42089. // Off right
  42090. off = (alignAttr.x || 0) + bBox.width - padding;
  42091. if (off > chart.plotWidth) {
  42092. if (align === 'left' && x <= 0) {
  42093. options.align = 'right';
  42094. options.inside = true;
  42095. }
  42096. else {
  42097. x += chart.plotWidth - off;
  42098. }
  42099. justified = true;
  42100. }
  42101. // Off top
  42102. off = alignAttr.y + padding;
  42103. if (off < 0) {
  42104. if (verticalAlign === 'bottom' && y >= 0) {
  42105. options.verticalAlign = 'top';
  42106. options.inside = true;
  42107. }
  42108. else {
  42109. y -= off;
  42110. }
  42111. justified = true;
  42112. }
  42113. // Off bottom
  42114. off = (alignAttr.y || 0) + bBox.height - padding;
  42115. if (off > chart.plotHeight) {
  42116. if (verticalAlign === 'top' && y <= 0) {
  42117. options.verticalAlign = 'bottom';
  42118. options.inside = true;
  42119. }
  42120. else {
  42121. y += chart.plotHeight - off;
  42122. }
  42123. justified = true;
  42124. }
  42125. if (justified) {
  42126. options.x = x;
  42127. options.y = y;
  42128. dataLabel.placed = !isNew;
  42129. dataLabel.align(options, void 0, alignTo);
  42130. }
  42131. return justified;
  42132. }
  42133. /**
  42134. * Merge two objects that can be arrays. If one of them is an array, the
  42135. * other is merged into each element. If both are arrays, each element is
  42136. * merged by index. If neither are arrays, we use normal merge.
  42137. * @private
  42138. */
  42139. function mergeArrays(one, two) {
  42140. let res = [], i;
  42141. if (isArray(one) && !isArray(two)) {
  42142. res = one.map(function (el) {
  42143. return merge(el, two);
  42144. });
  42145. }
  42146. else if (isArray(two) && !isArray(one)) {
  42147. res = two.map(function (el) {
  42148. return merge(one, el);
  42149. });
  42150. }
  42151. else if (!isArray(one) && !isArray(two)) {
  42152. res = merge(one, two);
  42153. }
  42154. else if (isArray(one) && isArray(two)) {
  42155. i = Math.max(one.length, two.length);
  42156. while (i--) {
  42157. res[i] = merge(one[i], two[i]);
  42158. }
  42159. }
  42160. return res;
  42161. }
  42162. /**
  42163. * Set starting position for data label sorting animation.
  42164. * @private
  42165. */
  42166. function setDataLabelStartPos(point, dataLabel, isNew, isInside, alignOptions) {
  42167. 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;
  42168. dataLabel.startXPos = inverted ?
  42169. alignOptions.x :
  42170. (reversed ?
  42171. -labelCenter - halfWidth :
  42172. xAxis.width - labelCenter + halfWidth);
  42173. dataLabel.startYPos = inverted ?
  42174. (reversed ?
  42175. this.yAxis.height - labelCenter + halfWidth :
  42176. -labelCenter - halfWidth) : alignOptions.y;
  42177. // We need to handle visibility in case of sorting point outside plot
  42178. // area
  42179. if (!isInside) {
  42180. dataLabel
  42181. .attr({ opacity: 1 })
  42182. .animate({ opacity: 0 }, void 0, dataLabel.hide);
  42183. }
  42184. else if (dataLabel.visibility === 'hidden') {
  42185. dataLabel.show();
  42186. dataLabel
  42187. .attr({ opacity: 0 })
  42188. .animate({ opacity: 1 });
  42189. }
  42190. // Save start position on first render, but do not change position
  42191. if (!chart.hasRendered) {
  42192. return;
  42193. }
  42194. // Set start position
  42195. if (isNew) {
  42196. dataLabel.attr({ x: dataLabel.startXPos, y: dataLabel.startYPos });
  42197. }
  42198. dataLabel.placed = true;
  42199. }
  42200. })(DataLabel || (DataLabel = {}));
  42201. /* *
  42202. *
  42203. * Default Export
  42204. *
  42205. * */
  42206. /* *
  42207. *
  42208. * API Declarations
  42209. *
  42210. * */
  42211. /**
  42212. * Callback JavaScript function to format the data label as a string. Note that
  42213. * if a `format` is defined, the format takes precedence and the formatter is
  42214. * ignored.
  42215. *
  42216. * @callback Highcharts.DataLabelsFormatterCallbackFunction
  42217. *
  42218. * @param {Highcharts.PointLabelObject} this
  42219. * Data label context to format
  42220. *
  42221. * @param {Highcharts.DataLabelsOptions} options
  42222. * [API options](/highcharts/plotOptions.series.dataLabels) of the data label
  42223. *
  42224. * @return {number|string|null|undefined}
  42225. * Formatted data label text
  42226. */
  42227. /**
  42228. * Values for handling data labels that flow outside the plot area.
  42229. *
  42230. * @typedef {"allow"|"justify"} Highcharts.DataLabelsOverflowValue
  42231. */
  42232. ''; // keeps doclets above in JS file
  42233. return DataLabel;
  42234. });
  42235. _registerModule(_modules, 'Series/Column/ColumnDataLabel.js', [_modules['Core/Series/DataLabel.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DataLabel, SeriesRegistry, U) {
  42236. /* *
  42237. *
  42238. * (c) 2010-2021 Torstein Honsi
  42239. *
  42240. * License: www.highcharts.com/license
  42241. *
  42242. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42243. *
  42244. * */
  42245. const { series: Series } = SeriesRegistry;
  42246. const { merge, pick } = U;
  42247. /* *
  42248. *
  42249. * Composition
  42250. *
  42251. * */
  42252. var ColumnDataLabel;
  42253. (function (ColumnDataLabel) {
  42254. /* *
  42255. *
  42256. * Constants
  42257. *
  42258. * */
  42259. const composedMembers = [];
  42260. /* *
  42261. *
  42262. * Functions
  42263. *
  42264. * */
  42265. /* eslint-disable valid-jsdoc */
  42266. /**
  42267. * Override the basic data label alignment by adjusting for the position of
  42268. * the column.
  42269. * @private
  42270. */
  42271. function alignDataLabel(point, dataLabel, options, alignTo, isNew) {
  42272. 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,
  42273. // data label box for alignment
  42274. dlBox = point.dlBox || point.shapeArgs, below = pick(point.below, // range series
  42275. point.plotY >
  42276. pick(this.translatedThreshold, yLen)),
  42277. // draw it inside the box?
  42278. inside = pick(options.inside, !!this.options.stacking), overshoot;
  42279. // Align to the column itself, or the top of it
  42280. if (dlBox) { // Area range uses this method but not alignTo
  42281. alignTo = merge(dlBox);
  42282. if (alignTo.y < 0) {
  42283. alignTo.height += alignTo.y;
  42284. alignTo.y = 0;
  42285. }
  42286. // If parts of the box overshoots outside the plot area, modify the
  42287. // box to center the label inside
  42288. overshoot = alignTo.y + alignTo.height - yLen;
  42289. if (overshoot > 0 && overshoot < alignTo.height) {
  42290. alignTo.height -= overshoot;
  42291. }
  42292. if (inverted) {
  42293. alignTo = {
  42294. x: yLen - alignTo.y - alignTo.height,
  42295. y: xLen - alignTo.x - alignTo.width,
  42296. width: alignTo.height,
  42297. height: alignTo.width
  42298. };
  42299. }
  42300. // Compute the alignment box
  42301. if (!inside) {
  42302. if (inverted) {
  42303. alignTo.x += below ? 0 : alignTo.width;
  42304. alignTo.width = 0;
  42305. }
  42306. else {
  42307. alignTo.y += below ? alignTo.height : 0;
  42308. alignTo.height = 0;
  42309. }
  42310. }
  42311. }
  42312. // When alignment is undefined (typically columns and bars), display the
  42313. // individual point below or above the point depending on the threshold
  42314. options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
  42315. options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
  42316. // Call the parent method
  42317. Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
  42318. // If label was justified and we have contrast, set it:
  42319. if (options.inside && point.contrastColor) {
  42320. dataLabel.css({
  42321. color: point.contrastColor
  42322. });
  42323. }
  42324. }
  42325. /** @private */
  42326. function compose(ColumnSeriesClass) {
  42327. DataLabel.compose(Series);
  42328. if (U.pushUnique(composedMembers, ColumnSeriesClass)) {
  42329. ColumnSeriesClass.prototype.alignDataLabel = alignDataLabel;
  42330. }
  42331. }
  42332. ColumnDataLabel.compose = compose;
  42333. })(ColumnDataLabel || (ColumnDataLabel = {}));
  42334. /* *
  42335. *
  42336. * Default Export
  42337. *
  42338. * */
  42339. return ColumnDataLabel;
  42340. });
  42341. _registerModule(_modules, 'Series/Bar/BarSeries.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (ColumnSeries, SeriesRegistry, U) {
  42342. /* *
  42343. *
  42344. * (c) 2010-2021 Torstein Honsi
  42345. *
  42346. * License: www.highcharts.com/license
  42347. *
  42348. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42349. *
  42350. * */
  42351. const { extend, merge } = U;
  42352. /* *
  42353. *
  42354. * Class
  42355. *
  42356. * */
  42357. /**
  42358. * Bar series type.
  42359. *
  42360. * @private
  42361. * @class
  42362. * @name Highcharts.seriesTypes.bar
  42363. *
  42364. * @augments Highcharts.Series
  42365. */
  42366. class BarSeries extends ColumnSeries {
  42367. constructor() {
  42368. /* *
  42369. *
  42370. * Static Properties
  42371. *
  42372. * */
  42373. super(...arguments);
  42374. /* *
  42375. *
  42376. * Properties
  42377. *
  42378. * */
  42379. this.data = void 0;
  42380. this.options = void 0;
  42381. this.points = void 0;
  42382. }
  42383. }
  42384. /**
  42385. * A bar series is a special type of column series where the columns are
  42386. * horizontal.
  42387. *
  42388. * @sample highcharts/demo/bar-basic/
  42389. * Bar chart
  42390. *
  42391. * @extends plotOptions.column
  42392. * @product highcharts
  42393. * @optionparent plotOptions.bar
  42394. */
  42395. BarSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  42396. // nothing here yet
  42397. });
  42398. extend(BarSeries.prototype, {
  42399. inverted: true
  42400. });
  42401. SeriesRegistry.registerSeriesType('bar', BarSeries);
  42402. /* *
  42403. *
  42404. * Default Export
  42405. *
  42406. * */
  42407. /* *
  42408. *
  42409. * API Options
  42410. *
  42411. * */
  42412. /**
  42413. * A `bar` series. If the [type](#series.bar.type) option is not specified,
  42414. * it is inherited from [chart.type](#chart.type).
  42415. *
  42416. * @extends series,plotOptions.bar
  42417. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  42418. * linecap, lineWidth, marker, connectEnds, step
  42419. * @product highcharts
  42420. * @apioption series.bar
  42421. */
  42422. /**
  42423. * An array of data points for the series. For the `bar` series type,
  42424. * points can be given in the following ways:
  42425. *
  42426. * 1. An array of numerical values. In this case, the numerical values will be
  42427. * interpreted as `y` options. The `x` values will be automatically
  42428. * calculated, either starting at 0 and incremented by 1, or from
  42429. * `pointStart` and `pointInterval` given in the series options. If the axis
  42430. * has categories, these will be used. Example:
  42431. * ```js
  42432. * data: [0, 5, 3, 5]
  42433. * ```
  42434. *
  42435. * 2. An array of arrays with 2 values. In this case, the values correspond to
  42436. * `x,y`. If the first value is a string, it is applied as the name of the
  42437. * point, and the `x` value is inferred.
  42438. * ```js
  42439. * data: [
  42440. * [0, 5],
  42441. * [1, 10],
  42442. * [2, 3]
  42443. * ]
  42444. * ```
  42445. *
  42446. * 3. An array of objects with named values. The following snippet shows only a
  42447. * few settings, see the complete options set below. If the total number of
  42448. * data points exceeds the series'
  42449. * [turboThreshold](#series.bar.turboThreshold), this option is not
  42450. * available.
  42451. * ```js
  42452. * data: [{
  42453. * x: 1,
  42454. * y: 1,
  42455. * name: "Point2",
  42456. * color: "#00FF00"
  42457. * }, {
  42458. * x: 1,
  42459. * y: 10,
  42460. * name: "Point1",
  42461. * color: "#FF00FF"
  42462. * }]
  42463. * ```
  42464. *
  42465. * @sample {highcharts} highcharts/chart/reflow-true/
  42466. * Numerical values
  42467. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  42468. * Arrays of numeric x and y
  42469. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  42470. * Arrays of datetime x and y
  42471. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  42472. * Arrays of point.name and y
  42473. * @sample {highcharts} highcharts/series/data-array-of-objects/
  42474. * Config objects
  42475. *
  42476. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  42477. * @extends series.column.data
  42478. * @product highcharts
  42479. * @apioption series.bar.data
  42480. */
  42481. /**
  42482. * @excluding halo,lineWidth,lineWidthPlus,marker
  42483. * @product highcharts highstock
  42484. * @apioption series.bar.states.hover
  42485. */
  42486. /**
  42487. * @excluding halo,lineWidth,lineWidthPlus,marker
  42488. * @product highcharts highstock
  42489. * @apioption series.bar.states.select
  42490. */
  42491. ''; // gets doclets above into transpilat
  42492. return BarSeries;
  42493. });
  42494. _registerModule(_modules, 'Series/Scatter/ScatterSeriesDefaults.js', [], function () {
  42495. /* *
  42496. *
  42497. * (c) 2010-2021 Torstein Honsi
  42498. *
  42499. * License: www.highcharts.com/license
  42500. *
  42501. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42502. *
  42503. * */
  42504. /* *
  42505. *
  42506. * API Options
  42507. *
  42508. * */
  42509. /**
  42510. * A scatter plot uses cartesian coordinates to display values for two
  42511. * variables for a set of data.
  42512. *
  42513. * @sample {highcharts} highcharts/demo/scatter/
  42514. * Scatter plot
  42515. *
  42516. * @extends plotOptions.line
  42517. * @excluding cropThreshold, pointPlacement, shadow, useOhlcData
  42518. * @product highcharts highstock
  42519. * @optionparent plotOptions.scatter
  42520. */
  42521. const ScatterSeriesDefaults = {
  42522. /**
  42523. * The width of the line connecting the data points.
  42524. *
  42525. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
  42526. * 0 by default
  42527. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
  42528. * 1px
  42529. *
  42530. * @product highcharts highstock
  42531. */
  42532. lineWidth: 0,
  42533. findNearestPointBy: 'xy',
  42534. /**
  42535. * Apply a jitter effect for the rendered markers. When plotting
  42536. * discrete values, a little random noise may help telling the points
  42537. * apart. The jitter setting applies a random displacement of up to `n`
  42538. * axis units in either direction. So for example on a horizontal X
  42539. * axis, setting the `jitter.x` to 0.24 will render the point in a
  42540. * random position between 0.24 units to the left and 0.24 units to the
  42541. * right of the true axis position. On a category axis, setting it to
  42542. * 0.5 will fill up the bin and make the data appear continuous.
  42543. *
  42544. * When rendered on top of a box plot or a column series, a jitter value
  42545. * of 0.24 will correspond to the underlying series' default
  42546. * [groupPadding](
  42547. * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
  42548. * and [pointPadding](
  42549. * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
  42550. * settings.
  42551. *
  42552. * @sample {highcharts} highcharts/demo/scatter-jitter
  42553. * Jitter on a scatter plot
  42554. *
  42555. * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
  42556. * Jittered scatter plot on top of a box plot
  42557. *
  42558. * @product highcharts highstock
  42559. * @since 7.0.2
  42560. */
  42561. jitter: {
  42562. /**
  42563. * The maximal X offset for the random jitter effect.
  42564. */
  42565. x: 0,
  42566. /**
  42567. * The maximal Y offset for the random jitter effect.
  42568. */
  42569. y: 0
  42570. },
  42571. marker: {
  42572. enabled: true // Overrides auto-enabling in line series (#3647)
  42573. },
  42574. /**
  42575. * Sticky tracking of mouse events. When true, the `mouseOut` event
  42576. * on a series isn't triggered until the mouse moves over another
  42577. * series, or out of the plot area. When false, the `mouseOut` event on
  42578. * a series is triggered when the mouse leaves the area around the
  42579. * series' graph or markers. This also implies the tooltip. When
  42580. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  42581. * will be hidden when moving the mouse between series.
  42582. *
  42583. * @type {boolean}
  42584. * @default false
  42585. * @product highcharts highstock highmaps
  42586. * @apioption plotOptions.scatter.stickyTracking
  42587. */
  42588. /**
  42589. * A configuration object for the tooltip rendering of each single
  42590. * series. Properties are inherited from [tooltip](#tooltip).
  42591. * Overridable properties are `headerFormat`, `pointFormat`,
  42592. * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
  42593. * series, in a scatter plot the series.name by default shows in the
  42594. * headerFormat and point.x and point.y in the pointFormat.
  42595. *
  42596. * @product highcharts highstock highmaps
  42597. */
  42598. tooltip: {
  42599. /**
  42600. * @product highcharts highstock
  42601. */
  42602. headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  42603. '<span style="font-size: 0.8em"> {series.name}</span><br/>',
  42604. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
  42605. }
  42606. };
  42607. /**
  42608. * A `scatter` series. If the [type](#series.scatter.type) option is
  42609. * not specified, it is inherited from [chart.type](#chart.type).
  42610. *
  42611. * @extends series,plotOptions.scatter
  42612. * @excluding cropThreshold, dataParser, dataURL, useOhlcData
  42613. * @product highcharts highstock
  42614. * @apioption series.scatter
  42615. */
  42616. /**
  42617. * An array of data points for the series. For the `scatter` series
  42618. * type, points can be given in the following ways:
  42619. *
  42620. * 1. An array of numerical values. In this case, the numerical values will be
  42621. * interpreted as `y` options. The `x` values will be automatically
  42622. * calculated, either starting at 0 and incremented by 1, or from
  42623. * `pointStart` and `pointInterval` given in the series options. If the axis
  42624. * has categories, these will be used. Example:
  42625. * ```js
  42626. * data: [0, 5, 3, 5]
  42627. * ```
  42628. *
  42629. * 2. An array of arrays with 2 values. In this case, the values correspond to
  42630. * `x,y`. If the first value is a string, it is applied as the name of the
  42631. * point, and the `x` value is inferred.
  42632. * ```js
  42633. * data: [
  42634. * [0, 0],
  42635. * [1, 8],
  42636. * [2, 9]
  42637. * ]
  42638. * ```
  42639. *
  42640. * 3. An array of objects with named values. The following snippet shows only a
  42641. * few settings, see the complete options set below. If the total number of
  42642. * data points exceeds the series'
  42643. * [turboThreshold](#series.scatter.turboThreshold), this option is not
  42644. * available.
  42645. * ```js
  42646. * data: [{
  42647. * x: 1,
  42648. * y: 2,
  42649. * name: "Point2",
  42650. * color: "#00FF00"
  42651. * }, {
  42652. * x: 1,
  42653. * y: 4,
  42654. * name: "Point1",
  42655. * color: "#FF00FF"
  42656. * }]
  42657. * ```
  42658. *
  42659. * @sample {highcharts} highcharts/chart/reflow-true/
  42660. * Numerical values
  42661. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  42662. * Arrays of numeric x and y
  42663. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  42664. * Arrays of datetime x and y
  42665. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  42666. * Arrays of point.name and y
  42667. * @sample {highcharts} highcharts/series/data-array-of-objects/
  42668. * Config objects
  42669. *
  42670. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  42671. * @extends series.line.data
  42672. * @product highcharts highstock
  42673. * @apioption series.scatter.data
  42674. */
  42675. ''; // keeps doclets above in JS file
  42676. /* *
  42677. *
  42678. * Default Export
  42679. *
  42680. * */
  42681. return ScatterSeriesDefaults;
  42682. });
  42683. _registerModule(_modules, 'Series/Scatter/ScatterSeries.js', [_modules['Series/Scatter/ScatterSeriesDefaults.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (ScatterSeriesDefaults, SeriesRegistry, U) {
  42684. /* *
  42685. *
  42686. * (c) 2010-2021 Torstein Honsi
  42687. *
  42688. * License: www.highcharts.com/license
  42689. *
  42690. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42691. *
  42692. * */
  42693. const { column: ColumnSeries, line: LineSeries } = SeriesRegistry.seriesTypes;
  42694. const { addEvent, extend, merge } = U;
  42695. /* *
  42696. *
  42697. * Class
  42698. *
  42699. * */
  42700. /**
  42701. * Scatter series type.
  42702. *
  42703. * @private
  42704. */
  42705. class ScatterSeries extends LineSeries {
  42706. constructor() {
  42707. /* *
  42708. *
  42709. * Static Properties
  42710. *
  42711. * */
  42712. super(...arguments);
  42713. /* *
  42714. *
  42715. * Properties
  42716. *
  42717. * */
  42718. this.data = void 0;
  42719. this.options = void 0;
  42720. this.points = void 0;
  42721. /* eslint-enable valid-jsdoc */
  42722. }
  42723. /* *
  42724. *
  42725. * Functions
  42726. *
  42727. * */
  42728. /* eslint-disable valid-jsdoc */
  42729. /**
  42730. * Optionally add the jitter effect.
  42731. * @private
  42732. */
  42733. applyJitter() {
  42734. const series = this, jitter = this.options.jitter, len = this.points.length;
  42735. /**
  42736. * Return a repeatable, pseudo-random number based on an integer
  42737. * seed.
  42738. * @private
  42739. */
  42740. function unrandom(seed) {
  42741. const rand = Math.sin(seed) * 10000;
  42742. return rand - Math.floor(rand);
  42743. }
  42744. if (jitter) {
  42745. this.points.forEach(function (point, i) {
  42746. ['x', 'y'].forEach(function (dim, j) {
  42747. let axis, plotProp = 'plot' + dim.toUpperCase(), min, max, translatedJitter;
  42748. if (jitter[dim] && !point.isNull) {
  42749. axis = series[dim + 'Axis'];
  42750. translatedJitter =
  42751. jitter[dim] * axis.transA;
  42752. if (axis && !axis.isLog) {
  42753. // Identify the outer bounds of the jitter range
  42754. min = Math.max(0, point[plotProp] - translatedJitter);
  42755. max = Math.min(axis.len, point[plotProp] + translatedJitter);
  42756. // Find a random position within this range
  42757. point[plotProp] = min +
  42758. (max - min) * unrandom(i + j * len);
  42759. // Update clientX for the tooltip k-d-tree
  42760. if (dim === 'x') {
  42761. point.clientX = point.plotX;
  42762. }
  42763. }
  42764. }
  42765. });
  42766. });
  42767. }
  42768. }
  42769. /**
  42770. * @private
  42771. */
  42772. drawGraph() {
  42773. if (this.options.lineWidth) {
  42774. super.drawGraph();
  42775. }
  42776. else if (this.graph) {
  42777. this.graph = this.graph.destroy();
  42778. }
  42779. }
  42780. }
  42781. ScatterSeries.defaultOptions = merge(LineSeries.defaultOptions, ScatterSeriesDefaults);
  42782. extend(ScatterSeries.prototype, {
  42783. drawTracker: ColumnSeries.prototype.drawTracker,
  42784. sorted: false,
  42785. requireSorting: false,
  42786. noSharedTooltip: true,
  42787. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  42788. takeOrdinalPosition: false // #2342
  42789. });
  42790. /* *
  42791. *
  42792. * Events
  42793. *
  42794. * */
  42795. /* eslint-disable no-invalid-this */
  42796. addEvent(ScatterSeries, 'afterTranslate', function () {
  42797. this.applyJitter();
  42798. });
  42799. SeriesRegistry.registerSeriesType('scatter', ScatterSeries);
  42800. /* *
  42801. *
  42802. * Default Export
  42803. *
  42804. * */
  42805. return ScatterSeries;
  42806. });
  42807. _registerModule(_modules, 'Series/CenteredUtilities.js', [_modules['Core/Globals.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (H, Series, U) {
  42808. /* *
  42809. *
  42810. * (c) 2010-2021 Torstein Honsi
  42811. *
  42812. * License: www.highcharts.com/license
  42813. *
  42814. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42815. *
  42816. * */
  42817. const { deg2rad } = H;
  42818. const { fireEvent, isNumber, pick, relativeLength } = U;
  42819. /**
  42820. * @private
  42821. */
  42822. var CenteredUtilities;
  42823. (function (CenteredUtilities) {
  42824. /* *
  42825. *
  42826. * Declarations
  42827. *
  42828. * */
  42829. /* *
  42830. *
  42831. * Functions
  42832. *
  42833. * */
  42834. /* eslint-disable valid-jsdoc */
  42835. /**
  42836. * Get the center of the pie based on the size and center options relative
  42837. * to the plot area. Borrowed by the polar and gauge series types.
  42838. *
  42839. * @private
  42840. * @function Highcharts.CenteredSeriesMixin.getCenter
  42841. */
  42842. function getCenter() {
  42843. 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;
  42844. let handleSlicingRoom, size = options.size, innerSize = options.innerSize || 0, i, value;
  42845. if (typeof size === 'string') {
  42846. size = parseFloat(size);
  42847. }
  42848. if (typeof innerSize === 'string') {
  42849. innerSize = parseFloat(innerSize);
  42850. }
  42851. const positions = [
  42852. pick(centerOption[0], '50%'),
  42853. pick(centerOption[1], '50%'),
  42854. // Prevent from negative values
  42855. pick(size && size < 0 ? void 0 : options.size, '100%'),
  42856. pick(innerSize && innerSize < 0 ? void 0 : options.innerSize || 0, '0%')
  42857. ];
  42858. // No need for inner size in angular (gauges) series but still required
  42859. // for pie series
  42860. if (chart.angular && !(this instanceof Series)) {
  42861. positions[3] = 0;
  42862. }
  42863. for (i = 0; i < 4; ++i) {
  42864. value = positions[i];
  42865. handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
  42866. // i == 0: centerX, relative to width
  42867. // i == 1: centerY, relative to height
  42868. // i == 2: size, relative to smallestSize
  42869. // i == 3: innerSize, relative to size
  42870. positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) + (handleSlicingRoom ? slicingRoom : 0);
  42871. }
  42872. // innerSize cannot be larger than size (#3632)
  42873. if (positions[3] > positions[2]) {
  42874. positions[3] = positions[2];
  42875. }
  42876. // thickness overrides innerSize, need to be less than pie size (#6647)
  42877. if (isNumber(thickness) &&
  42878. thickness * 2 < positions[2] && thickness > 0) {
  42879. positions[3] = positions[2] - thickness * 2;
  42880. }
  42881. fireEvent(this, 'afterGetCenter', { positions });
  42882. return positions;
  42883. }
  42884. CenteredUtilities.getCenter = getCenter;
  42885. /**
  42886. * getStartAndEndRadians - Calculates start and end angles in radians.
  42887. * Used in series types such as pie and sunburst.
  42888. *
  42889. * @private
  42890. * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
  42891. *
  42892. * @param {number} [start]
  42893. * Start angle in degrees.
  42894. *
  42895. * @param {number} [end]
  42896. * Start angle in degrees.
  42897. *
  42898. * @return {Highcharts.RadianAngles}
  42899. * Returns an object containing start and end angles as radians.
  42900. */
  42901. function getStartAndEndRadians(start, end) {
  42902. const startAngle = isNumber(start) ? start : 0, // must be a number
  42903. endAngle = ((isNumber(end) && // must be a number
  42904. end > startAngle && // must be larger than the start angle
  42905. // difference must be less than 360 degrees
  42906. (end - startAngle) < 360) ?
  42907. end :
  42908. startAngle + 360), correction = -90;
  42909. return {
  42910. start: deg2rad * (startAngle + correction),
  42911. end: deg2rad * (endAngle + correction)
  42912. };
  42913. }
  42914. CenteredUtilities.getStartAndEndRadians = getStartAndEndRadians;
  42915. })(CenteredUtilities || (CenteredUtilities = {}));
  42916. /* *
  42917. *
  42918. * Default Export
  42919. *
  42920. * */
  42921. /* *
  42922. *
  42923. * API Declarations
  42924. *
  42925. * */
  42926. /**
  42927. * @private
  42928. * @interface Highcharts.RadianAngles
  42929. */ /**
  42930. * @name Highcharts.RadianAngles#end
  42931. * @type {number}
  42932. */ /**
  42933. * @name Highcharts.RadianAngles#start
  42934. * @type {number}
  42935. */
  42936. ''; // keeps doclets above in JS file
  42937. return CenteredUtilities;
  42938. });
  42939. _registerModule(_modules, 'Series/Pie/PiePoint.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Series/Point.js'], _modules['Core/Utilities.js']], function (A, Point, U) {
  42940. /* *
  42941. *
  42942. * (c) 2010-2021 Torstein Honsi
  42943. *
  42944. * License: www.highcharts.com/license
  42945. *
  42946. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42947. *
  42948. * */
  42949. const { setAnimation } = A;
  42950. const { addEvent, defined, extend, isNumber, pick, relativeLength } = U;
  42951. /* *
  42952. *
  42953. * Class
  42954. *
  42955. * */
  42956. class PiePoint extends Point {
  42957. constructor() {
  42958. /* *
  42959. *
  42960. * Properties
  42961. *
  42962. * */
  42963. super(...arguments);
  42964. this.labelDistance = void 0;
  42965. this.options = void 0;
  42966. this.series = void 0;
  42967. }
  42968. /* *
  42969. *
  42970. * Functions
  42971. *
  42972. * */
  42973. /* eslint-disable valid-jsdoc */
  42974. /**
  42975. * Extendable method for getting the path of the connector between the
  42976. * data label and the pie slice.
  42977. * @private
  42978. */
  42979. getConnectorPath() {
  42980. const labelPosition = this.labelPosition, options = this.series.options.dataLabels, predefinedShapes = this.connectorShapes;
  42981. let connectorShape = options.connectorShape;
  42982. // find out whether to use the predefined shape
  42983. if (predefinedShapes[connectorShape]) {
  42984. connectorShape = predefinedShapes[connectorShape];
  42985. }
  42986. return connectorShape.call(this, {
  42987. // pass simplified label position object for user's convenience
  42988. x: labelPosition.computed.x,
  42989. y: labelPosition.computed.y,
  42990. alignment: labelPosition.alignment
  42991. }, labelPosition.connectorPosition, options);
  42992. }
  42993. /**
  42994. * @private
  42995. */
  42996. getTranslate() {
  42997. return this.sliced ? this.slicedTranslation : {
  42998. translateX: 0,
  42999. translateY: 0
  43000. };
  43001. }
  43002. /**
  43003. * @private
  43004. */
  43005. haloPath(size) {
  43006. const shapeArgs = this.shapeArgs;
  43007. return this.sliced || !this.visible ?
  43008. [] :
  43009. this.series.chart.renderer.symbols.arc(shapeArgs.x, shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
  43010. // Substract 1px to ensure the background is not bleeding
  43011. // through between the halo and the slice (#7495).
  43012. innerR: shapeArgs.r - 1,
  43013. start: shapeArgs.start,
  43014. end: shapeArgs.end,
  43015. borderRadius: shapeArgs.borderRadius
  43016. });
  43017. }
  43018. /**
  43019. * Initialize the pie slice.
  43020. * @private
  43021. */
  43022. init() {
  43023. super.init.apply(this, arguments);
  43024. this.name = pick(this.name, 'Slice');
  43025. // add event listener for select
  43026. const toggleSlice = (e) => {
  43027. this.slice(e.type === 'select');
  43028. };
  43029. addEvent(this, 'select', toggleSlice);
  43030. addEvent(this, 'unselect', toggleSlice);
  43031. return this;
  43032. }
  43033. /**
  43034. * Negative points are not valid (#1530, #3623, #5322)
  43035. * @private
  43036. */
  43037. isValid() {
  43038. return isNumber(this.y) && this.y >= 0;
  43039. }
  43040. /**
  43041. * Toggle the visibility of a pie slice or other data point. Note that this
  43042. * method is available only for some series, like pie, treemap and sunburst.
  43043. *
  43044. * @function Highcharts.Point#setVisible
  43045. *
  43046. * @param {boolean} [vis]
  43047. * True to show the pie slice or other data point, false to hide. If
  43048. * undefined, the visibility is toggled.
  43049. *
  43050. * @param {boolean} [redraw] Whether to redraw the chart after the point is
  43051. * altered. If doing more operations on the chart, it is a good idea to set
  43052. * redraw to false and call {@link Chart#redraw|chart.redraw()} after.
  43053. *
  43054. */
  43055. setVisible(vis, redraw) {
  43056. const series = this.series, chart = series.chart, ignoreHiddenPoint = series.options.ignoreHiddenPoint;
  43057. redraw = pick(redraw, ignoreHiddenPoint);
  43058. if (vis !== this.visible) {
  43059. // If called without an argument, toggle visibility
  43060. this.visible = this.options.visible = vis =
  43061. typeof vis === 'undefined' ? !this.visible : vis;
  43062. // update userOptions.data
  43063. series.options.data[series.data.indexOf(this)] =
  43064. this.options;
  43065. // Show and hide associated elements. This is performed
  43066. // regardless of redraw or not, because chart.redraw only
  43067. // handles full series.
  43068. ['graphic', 'dataLabel', 'connector'].forEach((key) => {
  43069. if (this[key]) {
  43070. this[key][vis ? 'show' : 'hide'](vis);
  43071. }
  43072. });
  43073. if (this.legendItem) {
  43074. chart.legend.colorizeItem(this, vis);
  43075. }
  43076. // #4170, hide halo after hiding point
  43077. if (!vis && this.state === 'hover') {
  43078. this.setState('');
  43079. }
  43080. // Handle ignore hidden slices
  43081. if (ignoreHiddenPoint) {
  43082. series.isDirty = true;
  43083. }
  43084. if (redraw) {
  43085. chart.redraw();
  43086. }
  43087. }
  43088. }
  43089. /**
  43090. * Set or toggle whether the slice is cut out from the pie.
  43091. * @private
  43092. *
  43093. * @param {boolean} sliced
  43094. * When undefined, the slice state is toggled.
  43095. *
  43096. * @param {boolean} [redraw]
  43097. * Whether to redraw the chart. True by default.
  43098. *
  43099. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
  43100. * Animation options.
  43101. */
  43102. slice(sliced, redraw, animation) {
  43103. const series = this.series, chart = series.chart;
  43104. setAnimation(animation, chart);
  43105. // redraw is true by default
  43106. redraw = pick(redraw, true);
  43107. /**
  43108. * Pie series only. Whether to display a slice offset from the
  43109. * center.
  43110. * @name Highcharts.Point#sliced
  43111. * @type {boolean|undefined}
  43112. */
  43113. // if called without an argument, toggle
  43114. this.sliced = this.options.sliced = sliced =
  43115. defined(sliced) ? sliced : !this.sliced;
  43116. // update userOptions.data
  43117. series.options.data[series.data.indexOf(this)] =
  43118. this.options;
  43119. if (this.graphic) {
  43120. this.graphic.animate(this.getTranslate());
  43121. }
  43122. }
  43123. }
  43124. extend(PiePoint.prototype, {
  43125. connectorShapes: {
  43126. // only one available before v7.0.0
  43127. fixedOffset: function (labelPosition, connectorPosition, options) {
  43128. const breakAt = connectorPosition.breakAt, touchingSliceAt = connectorPosition.touchingSliceAt, lineSegment = options.softConnector ? [
  43129. 'C',
  43130. // 1st control point (of the curve)
  43131. labelPosition.x +
  43132. // 5 gives the connector a little horizontal bend
  43133. (labelPosition.alignment === 'left' ? -5 : 5),
  43134. labelPosition.y,
  43135. 2 * breakAt.x - touchingSliceAt.x,
  43136. 2 * breakAt.y - touchingSliceAt.y,
  43137. breakAt.x,
  43138. breakAt.y //
  43139. ] : [
  43140. 'L',
  43141. breakAt.x,
  43142. breakAt.y
  43143. ];
  43144. // assemble the path
  43145. return ([
  43146. ['M', labelPosition.x, labelPosition.y],
  43147. lineSegment,
  43148. ['L', touchingSliceAt.x, touchingSliceAt.y]
  43149. ]);
  43150. },
  43151. straight: function (labelPosition, connectorPosition) {
  43152. const touchingSliceAt = connectorPosition.touchingSliceAt;
  43153. // direct line to the slice
  43154. return [
  43155. ['M', labelPosition.x, labelPosition.y],
  43156. ['L', touchingSliceAt.x, touchingSliceAt.y]
  43157. ];
  43158. },
  43159. crookedLine: function (labelPosition, connectorPosition, options) {
  43160. 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;
  43161. let crookX = breakAt.x;
  43162. if (options.crookDistance) {
  43163. const crookDistance = relativeLength(// % to fraction
  43164. options.crookDistance, 1);
  43165. crookX = leftAligned ?
  43166. cx +
  43167. r +
  43168. (plotWidth + plotLeft - cx - r) * (1 - crookDistance) :
  43169. plotLeft + (cx - r) * crookDistance;
  43170. // When the crookDistance option is undefined, make the bend in the
  43171. // intersection between the radial line in the middle of the slice,
  43172. // and the extension of the label position.
  43173. }
  43174. else {
  43175. crookX = cx + (cy - y) * Math.tan((this.angle || 0) - Math.PI / 2);
  43176. }
  43177. const path = [['M', x, y]];
  43178. // The crookedLine formula doesn't make sense if the path overlaps
  43179. // the label - use straight line instead in that case
  43180. if (leftAligned ?
  43181. (crookX <= x && crookX >= breakAt.x) :
  43182. (crookX >= x && crookX <= breakAt.x)) {
  43183. path.push(['L', crookX, y]);
  43184. }
  43185. path.push(['L', breakAt.x, breakAt.y], ['L', touchingSliceAt.x, touchingSliceAt.y]);
  43186. return path;
  43187. }
  43188. }
  43189. });
  43190. /* *
  43191. *
  43192. * Default Export
  43193. *
  43194. * */
  43195. return PiePoint;
  43196. });
  43197. _registerModule(_modules, 'Series/Pie/PieSeriesDefaults.js', [], function () {
  43198. /* *
  43199. *
  43200. * (c) 2010-2021 Torstein Honsi
  43201. *
  43202. * License: www.highcharts.com/license
  43203. *
  43204. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43205. *
  43206. * */
  43207. /* *
  43208. *
  43209. * API Options
  43210. *
  43211. * */
  43212. /**
  43213. * A pie chart is a circular graphic which is divided into slices to
  43214. * illustrate numerical proportion.
  43215. *
  43216. * @sample highcharts/demo/pie-basic/
  43217. * Pie chart
  43218. *
  43219. * @extends plotOptions.line
  43220. * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
  43221. * cropThreshold, dashStyle, dataSorting, dragDrop,
  43222. * findNearestPointBy, getExtremesFromAll, label, lineWidth,
  43223. * linkedTo, marker, negativeColor, pointInterval,
  43224. * pointIntervalUnit, pointPlacement, pointStart,
  43225. * softThreshold, stacking, step, threshold, turboThreshold,
  43226. * zoneAxis, zones, dataSorting, boostBlending
  43227. * @product highcharts highmaps
  43228. * @optionparent plotOptions.pie
  43229. *
  43230. * @private
  43231. */
  43232. const PieSeriesDefaults = {
  43233. /**
  43234. * The corner radius of the border surrounding each slice. A number
  43235. * signifies pixels. A percentage string, like for example `50%`, signifies
  43236. * a size relative to the radius and the inner radius.
  43237. *
  43238. * @sample highcharts/plotoptions/series-border-radius
  43239. * Column and pie with rounded border
  43240. *
  43241. * @since 11.0.0
  43242. *
  43243. * @type {number|string|Highcharts.BorderRadiusOptionsObject}
  43244. */
  43245. borderRadius: 3,
  43246. /**
  43247. * @excluding legendItemClick
  43248. * @apioption plotOptions.pie.events
  43249. */
  43250. /**
  43251. * Fires when the checkbox next to the point name in the legend is
  43252. * clicked. One parameter, event, is passed to the function. The state
  43253. * of the checkbox is found by event.checked. The checked item is found
  43254. * by event.item. Return false to prevent the default action which is to
  43255. * toggle the select state of the series.
  43256. *
  43257. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  43258. * Alert checkbox status
  43259. *
  43260. * @type {Function}
  43261. * @since 1.2.0
  43262. * @product highcharts highmaps
  43263. * @context Highcharts.Point
  43264. * @apioption plotOptions.pie.events.checkboxClick
  43265. */
  43266. /**
  43267. * Fires when the legend item belonging to the pie point (slice) is
  43268. * clicked. The `this` keyword refers to the point itself. One
  43269. * parameter, `event`, is passed to the function, containing common
  43270. * event information. The default action is to toggle the visibility of
  43271. * the point. This can be prevented by calling `event.preventDefault()`.
  43272. *
  43273. * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
  43274. * Confirm toggle visibility
  43275. *
  43276. * @type {Highcharts.PointLegendItemClickCallbackFunction}
  43277. * @since 1.2.0
  43278. * @product highcharts highmaps
  43279. * @apioption plotOptions.pie.point.events.legendItemClick
  43280. */
  43281. /**
  43282. * The center of the pie chart relative to the plot area. Can be
  43283. * percentages or pixel values. The default behaviour (as of 3.0) is to
  43284. * center the pie so that all slices and data labels are within the plot
  43285. * area. As a consequence, the pie may actually jump around in a chart
  43286. * with dynamic values, as the data labels move. In that case, the
  43287. * center should be explicitly set, for example to `["50%", "50%"]`.
  43288. *
  43289. * @sample {highcharts} highcharts/plotoptions/pie-center/
  43290. * Centered at 100, 100
  43291. *
  43292. * @type {Array<(number|string|null),(number|string|null)>}
  43293. * @default [null, null]
  43294. * @product highcharts highmaps
  43295. *
  43296. * @private
  43297. */
  43298. center: [null, null],
  43299. /**
  43300. * The color of the pie series. A pie series is represented as an empty
  43301. * circle if the total sum of its values is 0. Use this property to
  43302. * define the color of its border.
  43303. *
  43304. * In styled mode, the color can be defined by the
  43305. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  43306. * color can be set with the `.highcharts-series`,
  43307. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  43308. * `.highcharts-series-{n}` class, or individual classes given by the
  43309. * `className` option.
  43310. *
  43311. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  43312. * Empty pie series
  43313. *
  43314. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43315. * @default #cccccc
  43316. * @apioption plotOptions.pie.color
  43317. */
  43318. /**
  43319. * @product highcharts
  43320. *
  43321. * @private
  43322. */
  43323. clip: false,
  43324. /**
  43325. * @ignore-option
  43326. *
  43327. * @private
  43328. */
  43329. colorByPoint: true,
  43330. /**
  43331. * A series specific or series type specific color set to use instead
  43332. * of the global [colors](#colors).
  43333. *
  43334. * @sample {highcharts} highcharts/demo/pie-monochrome/
  43335. * Set default colors for all pies
  43336. *
  43337. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  43338. * @since 3.0
  43339. * @product highcharts highmaps
  43340. * @apioption plotOptions.pie.colors
  43341. */
  43342. /**
  43343. * @declare Highcharts.SeriesPieDataLabelsOptionsObject
  43344. * @extends plotOptions.series.dataLabels
  43345. * @excluding align, allowOverlap, inside, staggerLines, step
  43346. * @private
  43347. */
  43348. dataLabels: {
  43349. /**
  43350. * Alignment method for data labels. Possible values are:
  43351. *
  43352. * - `plotEdges`: Each label touches the nearest vertical edge of
  43353. * the plot area.
  43354. *
  43355. * - `connectors`: Connectors have the same x position and the
  43356. * widest label of each half (left & right) touches the nearest
  43357. * vertical edge of the plot area.
  43358. *
  43359. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/
  43360. * alignTo: connectors
  43361. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/
  43362. * alignTo: plotEdges
  43363. *
  43364. * @type {string}
  43365. * @since 7.0.0
  43366. * @product highcharts highmaps
  43367. * @apioption plotOptions.pie.dataLabels.alignTo
  43368. */
  43369. allowOverlap: true,
  43370. /**
  43371. * The color of the line connecting the data label to the pie slice.
  43372. * The default color is the same as the point's color.
  43373. *
  43374. * In styled mode, the connector stroke is given in the
  43375. * `.highcharts-data-label-connector` class.
  43376. *
  43377. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
  43378. * Blue connectors
  43379. * @sample {highcharts} highcharts/css/pie-point/
  43380. * Styled connectors
  43381. *
  43382. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43383. * @since 2.1
  43384. * @product highcharts highmaps
  43385. * @apioption plotOptions.pie.dataLabels.connectorColor
  43386. */
  43387. /**
  43388. * The distance from the data label to the connector. Note that
  43389. * data labels also have a default `padding`, so in order for the
  43390. * connector to touch the text, the `padding` must also be 0.
  43391. *
  43392. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
  43393. * No padding
  43394. *
  43395. * @since 2.1
  43396. * @product highcharts highmaps
  43397. */
  43398. connectorPadding: 5,
  43399. /**
  43400. * Specifies the method that is used to generate the connector path.
  43401. * Highcharts provides 3 built-in connector shapes: `'crookedLine'`
  43402. * (default since v11), `'fixedOffset'` and `'straight'`.
  43403. *
  43404. * Users can provide their own method by passing a function instead of a
  43405. * string. Three arguments are passed to the callback:
  43406. *
  43407. * - An object that holds the information about the coordinates of the
  43408. * label (`x` & `y` properties) and how the label is located in
  43409. * relation to the pie (`alignment` property). `alignment` can by one
  43410. * of the following: `'left'` (pie on the left side of the data
  43411. * label), `'right'` (pie on the right side of the data label) or
  43412. * `'center'` (data label overlaps the pie).
  43413. *
  43414. * - An object that holds the information about the position of the
  43415. * connector. Its `touchingSliceAt` porperty tells the position of
  43416. * the place where the connector touches the slice.
  43417. *
  43418. * - Data label options
  43419. *
  43420. * The function has to return an SVG path definition in array form (see
  43421. * the example).
  43422. *
  43423. * @sample {highcharts}
  43424. * highcharts/plotoptions/pie-datalabels-connectorshape-string/
  43425. * connectorShape is a String
  43426. * @sample {highcharts}
  43427. * highcharts/plotoptions/pie-datalabels-connectorshape-function/
  43428. * connectorShape is a function
  43429. *
  43430. * @type {string|Function}
  43431. * @since 7.0.0
  43432. * @product highcharts highmaps
  43433. */
  43434. connectorShape: 'crookedLine',
  43435. /**
  43436. * The width of the line connecting the data label to the pie slice.
  43437. *
  43438. * In styled mode, the connector stroke width is given in the
  43439. * `.highcharts-data-label-connector` class.
  43440. *
  43441. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
  43442. * Disable the connector
  43443. * @sample {highcharts} highcharts/css/pie-point/
  43444. * Styled connectors
  43445. *
  43446. * @type {number}
  43447. * @default 1
  43448. * @since 2.1
  43449. * @product highcharts highmaps
  43450. * @apioption plotOptions.pie.dataLabels.connectorWidth
  43451. */
  43452. /**
  43453. * Works only if `connectorShape` is `'crookedLine'`. It defines how
  43454. * far from the vertical plot edge the coonnector path should be
  43455. * crooked. With the default, `undefined`, the crook is placed so that
  43456. * the horizontal line from the label intersects with the radial line
  43457. * extending through the center of the pie slice.
  43458. *
  43459. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
  43460. * crookDistance set to 90%
  43461. *
  43462. * @since 7.0.0
  43463. * @product highcharts highmaps
  43464. */
  43465. crookDistance: void 0,
  43466. /**
  43467. * The distance of the data label from the pie's edge. Negative
  43468. * numbers put the data label on top of the pie slices. Can also be
  43469. * defined as a percentage of pie's radius. Connectors are only
  43470. * shown for data labels outside the pie.
  43471. *
  43472. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
  43473. * Data labels on top of the pie
  43474. *
  43475. * @type {number|string}
  43476. * @since 2.1
  43477. * @product highcharts highmaps
  43478. */
  43479. distance: 30,
  43480. enabled: true,
  43481. /**
  43482. * A
  43483. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  43484. * for the data label. Available variables are the same as for
  43485. * `formatter`.
  43486. *
  43487. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  43488. * Add a unit
  43489. *
  43490. * @type {string}
  43491. * @default undefined
  43492. * @since 3.0
  43493. * @apioption plotOptions.pie.dataLabels.format
  43494. */
  43495. // eslint-disable-next-line valid-jsdoc
  43496. /**
  43497. * Callback JavaScript function to format the data label. Note that
  43498. * if a `format` is defined, the format takes precedence and the
  43499. * formatter is ignored.
  43500. *
  43501. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  43502. * @default function () { return this.point.isNull ? void 0 : this.point.name; }
  43503. */
  43504. formatter: function () {
  43505. return this.point.isNull ? void 0 : this.point.name;
  43506. },
  43507. /**
  43508. * Whether to render the connector as a soft arc or a line with a sharp
  43509. * break. Works only if `connectorShape` equals to `fixedOffset`.
  43510. *
  43511. * @sample {highcharts}
  43512. * highcharts/plotoptions/pie-datalabels-softconnector-true/
  43513. * Soft
  43514. * @sample {highcharts}
  43515. * highcharts/plotoptions/pie-datalabels-softconnector-false/
  43516. * Non soft
  43517. *
  43518. * @since 2.1.7
  43519. * @product highcharts highmaps
  43520. */
  43521. softConnector: true,
  43522. /**
  43523. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow
  43524. * Long labels truncated with an ellipsis
  43525. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap
  43526. * Long labels are wrapped
  43527. *
  43528. * @type {Highcharts.CSSObject}
  43529. * @apioption plotOptions.pie.dataLabels.style
  43530. */
  43531. x: 0
  43532. },
  43533. /**
  43534. * If the total sum of the pie's values is 0, the series is represented
  43535. * as an empty circle . The `fillColor` option defines the color of that
  43536. * circle. Use [pie.borderWidth](#plotOptions.pie.borderWidth) to set
  43537. * the border thickness.
  43538. *
  43539. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  43540. * Empty pie series
  43541. *
  43542. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43543. * @private
  43544. */
  43545. fillColor: void 0,
  43546. /**
  43547. * The end angle of the pie in degrees where 0 is top and 90 is right.
  43548. * Defaults to `startAngle` plus 360.
  43549. *
  43550. * @sample {highcharts} highcharts/demo/pie-semi-circle/
  43551. * Semi-circle donut
  43552. *
  43553. * @type {number}
  43554. * @since 1.3.6
  43555. * @product highcharts highmaps
  43556. * @apioption plotOptions.pie.endAngle
  43557. */
  43558. /**
  43559. * Thickness describing the ring size for a donut type chart,
  43560. * overriding [innerSize](#plotOptions.pie.innerSize).
  43561. *
  43562. * @type {number}
  43563. * @default undefined
  43564. * @product highcharts
  43565. * @since 10.1.0
  43566. * @apioption plotOptions.pie.thickness
  43567. * @private
  43568. */
  43569. /**
  43570. * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
  43571. * this option tells whether the series shall be redrawn as if the
  43572. * hidden point were `null`.
  43573. *
  43574. * The default value changed from `false` to `true` with Highcharts
  43575. * 3.0.
  43576. *
  43577. * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
  43578. * True, the hiddden point is ignored
  43579. *
  43580. * @since 2.3.0
  43581. * @product highcharts highmaps
  43582. *
  43583. * @private
  43584. */
  43585. ignoreHiddenPoint: true,
  43586. /**
  43587. * @ignore-option
  43588. *
  43589. * @private
  43590. */
  43591. inactiveOtherPoints: true,
  43592. /**
  43593. * The size of the inner diameter for the pie. A size greater than 0
  43594. * renders a donut chart. Can be a percentage or pixel value.
  43595. * Percentages are relative to the pie size. Pixel values are given as
  43596. * integers. Setting overridden by thickness.
  43597. *
  43598. *
  43599. * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
  43600. * area, not the pie size.
  43601. *
  43602. * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
  43603. * 80px inner size
  43604. * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
  43605. * 50% of the plot area
  43606. * @sample {highcharts} highcharts/demo/3d-pie-donut/
  43607. * 3D donut
  43608. *
  43609. * @type {number|string}
  43610. * @default 0
  43611. * @since 2.0
  43612. * @product highcharts highmaps
  43613. * @apioption plotOptions.pie.innerSize
  43614. */
  43615. /**
  43616. * @ignore-option
  43617. *
  43618. * @private
  43619. */
  43620. legendType: 'point',
  43621. /**
  43622. * @ignore-option
  43623. *
  43624. * @private
  43625. */
  43626. marker: null,
  43627. /**
  43628. * The minimum size for a pie in response to auto margins. The pie will
  43629. * try to shrink to make room for data labels in side the plot area,
  43630. * but only to this size.
  43631. *
  43632. * @type {number|string}
  43633. * @default 80
  43634. * @since 3.0
  43635. * @product highcharts highmaps
  43636. * @apioption plotOptions.pie.minSize
  43637. */
  43638. /**
  43639. * The diameter of the pie relative to the plot area. Can be a
  43640. * percentage or pixel value. Pixel values are given as integers. The
  43641. * default behaviour (as of 3.0) is to scale to the plot area and give
  43642. * room for data labels within the plot area.
  43643. * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
  43644. * default size calculation. As a consequence, the size of the pie may
  43645. * vary when points are updated and data labels more around. In that
  43646. * case it is best to set a fixed value, for example `"75%"`.
  43647. *
  43648. * @sample {highcharts} highcharts/plotoptions/pie-size/
  43649. * Smaller pie
  43650. *
  43651. * @type {number|string|null}
  43652. * @product highcharts highmaps
  43653. *
  43654. * @private
  43655. */
  43656. size: null,
  43657. /**
  43658. * Whether to display this particular series or series type in the
  43659. * legend. Since 2.1, pies are not shown in the legend by default.
  43660. *
  43661. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  43662. * One series in the legend, one hidden
  43663. *
  43664. * @product highcharts highmaps
  43665. *
  43666. * @private
  43667. */
  43668. showInLegend: false,
  43669. /**
  43670. * If a point is sliced, moved out from the center, how many pixels
  43671. * should it be moved?.
  43672. *
  43673. * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
  43674. * 20px offset
  43675. *
  43676. * @product highcharts highmaps
  43677. *
  43678. * @private
  43679. */
  43680. slicedOffset: 10,
  43681. /**
  43682. * The start angle of the pie slices in degrees where 0 is top and 90
  43683. * right.
  43684. *
  43685. * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
  43686. * Start from right
  43687. *
  43688. * @type {number}
  43689. * @default 0
  43690. * @since 2.3.4
  43691. * @product highcharts highmaps
  43692. * @apioption plotOptions.pie.startAngle
  43693. */
  43694. /**
  43695. * Sticky tracking of mouse events. When true, the `mouseOut` event
  43696. * on a series isn't triggered until the mouse moves over another
  43697. * series, or out of the plot area. When false, the `mouseOut` event on
  43698. * a series is triggered when the mouse leaves the area around the
  43699. * series' graph or markers. This also implies the tooltip. When
  43700. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  43701. * will be hidden when moving the mouse between series.
  43702. *
  43703. * @product highcharts highmaps
  43704. *
  43705. * @private
  43706. */
  43707. stickyTracking: false,
  43708. tooltip: {
  43709. followPointer: true
  43710. },
  43711. /**
  43712. * The color of the border surrounding each slice. When `null`, the
  43713. * border takes the same color as the slice fill. This can be used
  43714. * together with a `borderWidth` to fill drawing gaps created by
  43715. * antialiazing artefacts in borderless pies.
  43716. *
  43717. * In styled mode, the border stroke is given in the `.highcharts-point`
  43718. * class.
  43719. *
  43720. * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
  43721. * Black border
  43722. *
  43723. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43724. * @default #ffffff
  43725. * @product highcharts highmaps
  43726. *
  43727. * @private
  43728. */
  43729. borderColor: "#ffffff" /* Palette.backgroundColor */,
  43730. /**
  43731. * The width of the border surrounding each slice.
  43732. *
  43733. * When setting the border width to 0, there may be small gaps between
  43734. * the slices due to SVG antialiasing artefacts. To work around this,
  43735. * keep the border width at 0.5 or 1, but set the `borderColor` to
  43736. * `null` instead.
  43737. *
  43738. * In styled mode, the border stroke width is given in the
  43739. * `.highcharts-point` class.
  43740. *
  43741. * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
  43742. * 3px border
  43743. *
  43744. * @product highcharts highmaps
  43745. *
  43746. * @private
  43747. */
  43748. borderWidth: 1,
  43749. /**
  43750. * @ignore-option
  43751. * @private
  43752. */
  43753. lineWidth: void 0,
  43754. states: {
  43755. /**
  43756. * @extends plotOptions.series.states.hover
  43757. * @excluding marker, lineWidth, lineWidthPlus
  43758. * @product highcharts highmaps
  43759. */
  43760. hover: {
  43761. /**
  43762. * How much to brighten the point on interaction. Requires the
  43763. * main color to be defined in hex or rgb(a) format.
  43764. *
  43765. * In styled mode, the hover brightness is by default replaced
  43766. * by a fill-opacity given in the `.highcharts-point-hover`
  43767. * class.
  43768. *
  43769. * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
  43770. * Brightened by 0.5
  43771. *
  43772. * @product highcharts highmaps
  43773. */
  43774. brightness: 0.1
  43775. }
  43776. }
  43777. };
  43778. /**
  43779. * A `pie` series. If the [type](#series.pie.type) option is not specified,
  43780. * it is inherited from [chart.type](#chart.type).
  43781. *
  43782. * @extends series,plotOptions.pie
  43783. * @excluding cropThreshold, dataParser, dataURL, linkedTo, stack, xAxis, yAxis,
  43784. * dataSorting, step, boostThreshold, boostBlending
  43785. * @product highcharts highmaps
  43786. * @apioption series.pie
  43787. */
  43788. /**
  43789. * An array of data points for the series. For the `pie` series type,
  43790. * points can be given in the following ways:
  43791. *
  43792. * 1. An array of numerical values. In this case, the numerical values will be
  43793. * interpreted as `y` options. Example:
  43794. * ```js
  43795. * data: [0, 5, 3, 5]
  43796. * ```
  43797. *
  43798. * 2. An array of objects with named values. The following snippet shows only a
  43799. * few settings, see the complete options set below. If the total number of
  43800. * data points exceeds the series'
  43801. * [turboThreshold](#series.pie.turboThreshold),
  43802. * this option is not available.
  43803. * ```js
  43804. * data: [{
  43805. * y: 1,
  43806. * name: "Point2",
  43807. * color: "#00FF00"
  43808. * }, {
  43809. * y: 7,
  43810. * name: "Point1",
  43811. * color: "#FF00FF"
  43812. * }]
  43813. * ```
  43814. *
  43815. * @sample {highcharts} highcharts/chart/reflow-true/
  43816. * Numerical values
  43817. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  43818. * Arrays of numeric x and y
  43819. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  43820. * Arrays of datetime x and y
  43821. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  43822. * Arrays of point.name and y
  43823. * @sample {highcharts} highcharts/series/data-array-of-objects/
  43824. * Config objects
  43825. *
  43826. * @type {Array<number|Array<string,(number|null)>|null|*>}
  43827. * @extends series.line.data
  43828. * @excluding marker, x
  43829. * @product highcharts highmaps
  43830. * @apioption series.pie.data
  43831. */
  43832. /**
  43833. * @type {Highcharts.SeriesPieDataLabelsOptionsObject}
  43834. * @product highcharts highmaps
  43835. * @apioption series.pie.data.dataLabels
  43836. */
  43837. /**
  43838. * The sequential index of the data point in the legend.
  43839. *
  43840. * @type {number}
  43841. * @product highcharts highmaps
  43842. * @apioption series.pie.data.legendIndex
  43843. */
  43844. /**
  43845. * Whether to display a slice offset from the center.
  43846. *
  43847. * @sample {highcharts} highcharts/point/sliced/
  43848. * One sliced point
  43849. *
  43850. * @type {boolean}
  43851. * @product highcharts highmaps
  43852. * @apioption series.pie.data.sliced
  43853. */
  43854. /**
  43855. * @extends plotOptions.pie.dataLabels
  43856. * @excluding align, allowOverlap, inside, staggerLines, step
  43857. * @product highcharts highmaps
  43858. * @apioption series.pie.dataLabels
  43859. */
  43860. /**
  43861. * @excluding legendItemClick
  43862. * @product highcharts highmaps
  43863. * @apioption series.pie.events
  43864. */
  43865. ''; // placeholder for transpiled doclets above
  43866. /* *
  43867. *
  43868. * Default Export
  43869. *
  43870. * */
  43871. return PieSeriesDefaults;
  43872. });
  43873. _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) {
  43874. /* *
  43875. *
  43876. * (c) 2010-2021 Torstein Honsi
  43877. *
  43878. * License: www.highcharts.com/license
  43879. *
  43880. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43881. *
  43882. * */
  43883. const { getStartAndEndRadians } = CU;
  43884. const { noop } = H;
  43885. const { clamp, extend, fireEvent, merge, pick, relativeLength } = U;
  43886. /* *
  43887. *
  43888. * Class
  43889. *
  43890. * */
  43891. /**
  43892. * Pie series type.
  43893. *
  43894. * @private
  43895. * @class
  43896. * @name Highcharts.seriesTypes.pie
  43897. *
  43898. * @augments Highcharts.Series
  43899. */
  43900. class PieSeries extends Series {
  43901. constructor() {
  43902. /* *
  43903. *
  43904. * Static Properties
  43905. *
  43906. * */
  43907. super(...arguments);
  43908. /* *
  43909. *
  43910. * Properties
  43911. *
  43912. * */
  43913. this.center = void 0;
  43914. this.data = void 0;
  43915. this.maxLabelDistance = void 0;
  43916. this.options = void 0;
  43917. this.points = void 0;
  43918. /* eslint-enable valid-jsdoc */
  43919. }
  43920. /* *
  43921. *
  43922. * Functions
  43923. *
  43924. * */
  43925. /* eslint-disable valid-jsdoc */
  43926. /**
  43927. * Animates the pies in.
  43928. * @private
  43929. */
  43930. animate(init) {
  43931. const series = this, points = series.points, startAngleRad = series.startAngleRad;
  43932. if (!init) {
  43933. points.forEach(function (point) {
  43934. const graphic = point.graphic, args = point.shapeArgs;
  43935. if (graphic && args) {
  43936. // start values
  43937. graphic.attr({
  43938. // animate from inner radius (#779)
  43939. r: pick(point.startR, (series.center && series.center[3] / 2)),
  43940. start: startAngleRad,
  43941. end: startAngleRad
  43942. });
  43943. // animate
  43944. graphic.animate({
  43945. r: args.r,
  43946. start: args.start,
  43947. end: args.end
  43948. }, series.options.animation);
  43949. }
  43950. });
  43951. }
  43952. }
  43953. /**
  43954. * Called internally to draw auxiliary graph in pie-like series in
  43955. * situtation when the default graph is not sufficient enough to present
  43956. * the data well. Auxiliary graph is saved in the same object as
  43957. * regular graph.
  43958. * @private
  43959. */
  43960. drawEmpty() {
  43961. const start = this.startAngleRad, end = this.endAngleRad, options = this.options;
  43962. let centerX, centerY;
  43963. // Draw auxiliary graph if there're no visible points.
  43964. if (this.total === 0 && this.center) {
  43965. centerX = this.center[0];
  43966. centerY = this.center[1];
  43967. if (!this.graph) {
  43968. this.graph = this.chart.renderer
  43969. .arc(centerX, centerY, this.center[1] / 2, 0, start, end)
  43970. .addClass('highcharts-empty-series')
  43971. .add(this.group);
  43972. }
  43973. this.graph.attr({
  43974. d: Symbols.arc(centerX, centerY, this.center[2] / 2, 0, {
  43975. start,
  43976. end,
  43977. innerR: this.center[3] / 2
  43978. })
  43979. });
  43980. if (!this.chart.styledMode) {
  43981. this.graph.attr({
  43982. 'stroke-width': options.borderWidth,
  43983. fill: options.fillColor || 'none',
  43984. stroke: options.color || "#cccccc" /* Palette.neutralColor20 */
  43985. });
  43986. }
  43987. }
  43988. else if (this.graph) { // Destroy the graph object.
  43989. this.graph = this.graph.destroy();
  43990. }
  43991. }
  43992. /**
  43993. * Slices in pie chart are initialized in DOM, but it's shapes and
  43994. * animations are normally run in `drawPoints()`.
  43995. * @private
  43996. */
  43997. drawPoints() {
  43998. const renderer = this.chart.renderer;
  43999. this.points.forEach(function (point) {
  44000. // When updating a series between 2d and 3d or cartesian and
  44001. // polar, the shape type changes.
  44002. if (point.graphic && point.hasNewShapeType()) {
  44003. point.graphic = point.graphic.destroy();
  44004. }
  44005. if (!point.graphic) {
  44006. point.graphic = renderer[point.shapeType](point.shapeArgs)
  44007. .add(point.series.group);
  44008. point.delayedRendering = true;
  44009. }
  44010. });
  44011. }
  44012. /**
  44013. * Extend the generatePoints method by adding total and percentage
  44014. * properties to each point
  44015. * @private
  44016. */
  44017. generatePoints() {
  44018. super.generatePoints();
  44019. this.updateTotals();
  44020. }
  44021. /**
  44022. * Utility for getting the x value from a given y, used for
  44023. * anticollision logic in data labels. Added point for using specific
  44024. * points' label distance.
  44025. * @private
  44026. */
  44027. getX(y, left, point) {
  44028. const center = this.center,
  44029. // Variable pie has individual radius
  44030. radius = this.radii ?
  44031. this.radii[point.index] || 0 :
  44032. center[2] / 2;
  44033. const angle = Math.asin(clamp((y - center[1]) / (radius + point.labelDistance), -1, 1));
  44034. const x = center[0] +
  44035. (left ? -1 : 1) *
  44036. (Math.cos(angle) * (radius + point.labelDistance)) +
  44037. (point.labelDistance > 0 ?
  44038. (left ? -1 : 1) * this.options.dataLabels.padding :
  44039. 0);
  44040. return x;
  44041. }
  44042. /**
  44043. * Define hasData function for non-cartesian series. Returns true if the
  44044. * series has points at all.
  44045. * @private
  44046. */
  44047. hasData() {
  44048. return !!this.processedXData.length; // != 0
  44049. }
  44050. /**
  44051. * Draw the data points
  44052. * @private
  44053. */
  44054. redrawPoints() {
  44055. const series = this, chart = series.chart;
  44056. let groupTranslation, graphic, pointAttr, shapeArgs;
  44057. this.drawEmpty();
  44058. // Apply the drop-shadow to the group because otherwise each element
  44059. // would cast a shadow on others
  44060. if (series.group && !chart.styledMode) {
  44061. series.group.shadow(series.options.shadow);
  44062. }
  44063. // draw the slices
  44064. series.points.forEach(function (point) {
  44065. const animateTo = {};
  44066. graphic = point.graphic;
  44067. if (!point.isNull && graphic) {
  44068. shapeArgs = point.shapeArgs;
  44069. // If the point is sliced, use special translation, else use
  44070. // plot area translation
  44071. groupTranslation = point.getTranslate();
  44072. if (!chart.styledMode) {
  44073. pointAttr = series.pointAttribs(point, (point.selected && 'select'));
  44074. }
  44075. // Draw the slice
  44076. if (!point.delayedRendering) {
  44077. graphic
  44078. .setRadialReference(series.center);
  44079. if (!chart.styledMode) {
  44080. merge(true, animateTo, pointAttr);
  44081. }
  44082. merge(true, animateTo, shapeArgs, groupTranslation);
  44083. graphic.animate(animateTo);
  44084. }
  44085. else {
  44086. graphic
  44087. .setRadialReference(series.center)
  44088. .attr(shapeArgs)
  44089. .attr(groupTranslation);
  44090. if (!chart.styledMode) {
  44091. graphic
  44092. .attr(pointAttr)
  44093. .attr({ 'stroke-linejoin': 'round' });
  44094. }
  44095. point.delayedRendering = false;
  44096. }
  44097. graphic
  44098. .attr({
  44099. visibility: point.visible ? 'inherit' : 'hidden'
  44100. });
  44101. graphic.addClass(point.getClassName(), true);
  44102. }
  44103. else if (graphic) {
  44104. point.graphic = graphic.destroy();
  44105. }
  44106. });
  44107. }
  44108. /**
  44109. * Utility for sorting data labels.
  44110. * @private
  44111. */
  44112. sortByAngle(points, sign) {
  44113. points.sort(function (a, b) {
  44114. return ((typeof a.angle !== 'undefined') &&
  44115. (b.angle - a.angle) * sign);
  44116. });
  44117. }
  44118. /**
  44119. * Do translation for pie slices
  44120. * @private
  44121. */
  44122. translate(positions) {
  44123. fireEvent(this, 'translate');
  44124. this.generatePoints();
  44125. const series = this, precision = 1000, // issue #172
  44126. 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,
  44127. points = series.points, labelDistance = options.dataLabels.distance, ignoreHiddenPoint = options.ignoreHiddenPoint, len = points.length;
  44128. let finalConnectorOffset, start, end, angle,
  44129. // the x component of the radius vector for a given point
  44130. radiusX, radiusY, i, point, cumulative = 0;
  44131. // Get positions - either an integer or a percentage string must be
  44132. // given. If positions are passed as a parameter, we're in a
  44133. // recursive loop for adjusting space for data labels.
  44134. if (!positions) {
  44135. series.center = positions = series.getCenter();
  44136. }
  44137. // Calculate the geometry for each point
  44138. for (i = 0; i < len; i++) {
  44139. point = points[i];
  44140. // set start and end angle
  44141. start = startAngleRad + (cumulative * circ);
  44142. if (point.isValid() &&
  44143. (!ignoreHiddenPoint || point.visible)) {
  44144. cumulative += point.percentage / 100;
  44145. }
  44146. end = startAngleRad + (cumulative * circ);
  44147. // set the shape
  44148. const shapeArgs = {
  44149. x: positions[0],
  44150. y: positions[1],
  44151. r: positions[2] / 2,
  44152. innerR: positions[3] / 2,
  44153. start: Math.round(start * precision) / precision,
  44154. end: Math.round(end * precision) / precision
  44155. };
  44156. point.shapeType = 'arc';
  44157. point.shapeArgs = shapeArgs;
  44158. // Used for distance calculation for specific point.
  44159. point.labelDistance = pick((point.options.dataLabels &&
  44160. point.options.dataLabels.distance), labelDistance);
  44161. // Compute point.labelDistance if it's defined as percentage
  44162. // of slice radius (#8854)
  44163. point.labelDistance = relativeLength(point.labelDistance, shapeArgs.r);
  44164. // Saved for later dataLabels distance calculation.
  44165. series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
  44166. // The angle must stay within -90 and 270 (#2645)
  44167. angle = (end + start) / 2;
  44168. if (angle > 1.5 * Math.PI) {
  44169. angle -= 2 * Math.PI;
  44170. }
  44171. else if (angle < -Math.PI / 2) {
  44172. angle += 2 * Math.PI;
  44173. }
  44174. // Center for the sliced out slice
  44175. point.slicedTranslation = {
  44176. translateX: Math.round(Math.cos(angle) * slicedOffset),
  44177. translateY: Math.round(Math.sin(angle) * slicedOffset)
  44178. };
  44179. // set the anchor point for tooltips
  44180. radiusX = Math.cos(angle) * positions[2] / 2;
  44181. radiusY = Math.sin(angle) * positions[2] / 2;
  44182. point.tooltipPos = [
  44183. positions[0] + radiusX * 0.7,
  44184. positions[1] + radiusY * 0.7
  44185. ];
  44186. point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
  44187. 1 :
  44188. 0;
  44189. point.angle = angle;
  44190. // Set the anchor point for data labels. Use point.labelDistance
  44191. // instead of labelDistance // #1174
  44192. // finalConnectorOffset - not override connectorOffset value.
  44193. finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
  44194. point.labelPosition = {
  44195. natural: {
  44196. // initial position of the data label - it's utilized for
  44197. // finding the final position for the label
  44198. x: positions[0] + radiusX + Math.cos(angle) *
  44199. point.labelDistance,
  44200. y: positions[1] + radiusY + Math.sin(angle) *
  44201. point.labelDistance
  44202. },
  44203. computed: {
  44204. // used for generating connector path -
  44205. // initialized later in drawDataLabels function
  44206. // x: undefined,
  44207. // y: undefined
  44208. },
  44209. // left - pie on the left side of the data label
  44210. // right - pie on the right side of the data label
  44211. // center - data label overlaps the pie
  44212. alignment: point.labelDistance < 0 ?
  44213. 'center' : point.half ? 'right' : 'left',
  44214. connectorPosition: {
  44215. breakAt: {
  44216. x: positions[0] + radiusX + Math.cos(angle) *
  44217. finalConnectorOffset,
  44218. y: positions[1] + radiusY + Math.sin(angle) *
  44219. finalConnectorOffset
  44220. },
  44221. touchingSliceAt: {
  44222. x: positions[0] + radiusX,
  44223. y: positions[1] + radiusY
  44224. }
  44225. }
  44226. };
  44227. }
  44228. fireEvent(series, 'afterTranslate');
  44229. }
  44230. /**
  44231. * Recompute total chart sum and update percentages of points.
  44232. * @private
  44233. */
  44234. updateTotals() {
  44235. const points = this.points, len = points.length, ignoreHiddenPoint = this.options.ignoreHiddenPoint;
  44236. let i, point, total = 0;
  44237. // Get the total sum
  44238. for (i = 0; i < len; i++) {
  44239. point = points[i];
  44240. if (point.isValid() &&
  44241. (!ignoreHiddenPoint || point.visible)) {
  44242. total += point.y;
  44243. }
  44244. }
  44245. this.total = total;
  44246. // Set each point's properties
  44247. for (i = 0; i < len; i++) {
  44248. point = points[i];
  44249. point.percentage =
  44250. (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
  44251. point.y / total * 100 :
  44252. 0;
  44253. point.total = total;
  44254. }
  44255. }
  44256. }
  44257. PieSeries.defaultOptions = merge(Series.defaultOptions, PieSeriesDefaults);
  44258. extend(PieSeries.prototype, {
  44259. axisTypes: [],
  44260. directTouch: true,
  44261. drawGraph: void 0,
  44262. drawTracker: ColumnSeries.prototype.drawTracker,
  44263. getCenter: CU.getCenter,
  44264. getSymbol: noop,
  44265. isCartesian: false,
  44266. noSharedTooltip: true,
  44267. pointAttribs: ColumnSeries.prototype.pointAttribs,
  44268. pointClass: PiePoint,
  44269. requireSorting: false,
  44270. searchPoint: noop,
  44271. trackerGroups: ['group', 'dataLabelsGroup']
  44272. });
  44273. SeriesRegistry.registerSeriesType('pie', PieSeries);
  44274. /* *
  44275. *
  44276. * Default Export
  44277. *
  44278. * */
  44279. return PieSeries;
  44280. });
  44281. _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) {
  44282. /* *
  44283. *
  44284. * (c) 2010-2021 Torstein Honsi
  44285. *
  44286. * License: www.highcharts.com/license
  44287. *
  44288. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44289. *
  44290. * */
  44291. const { noop } = H;
  44292. const { distribute } = R;
  44293. const { series: Series } = SeriesRegistry;
  44294. const { arrayMax, clamp, defined, merge, pick, relativeLength } = U;
  44295. /* *
  44296. *
  44297. * Composition
  44298. *
  44299. * */
  44300. var ColumnDataLabel;
  44301. (function (ColumnDataLabel) {
  44302. /* *
  44303. *
  44304. * Constants
  44305. *
  44306. * */
  44307. const composedMembers = [];
  44308. const dataLabelPositioners = {
  44309. // Based on the value computed in Highcharts' distribute algorithm.
  44310. radialDistributionY: function (point) {
  44311. return point.top + point.distributeBox.pos;
  44312. },
  44313. // get the x - use the natural x position for labels near the
  44314. // top and bottom, to prevent the top and botton slice
  44315. // connectors from touching each other on either side
  44316. // Based on the value computed in Highcharts' distribute algorithm.
  44317. radialDistributionX: function (series, point, y, naturalY) {
  44318. return series.getX(y < point.top + 2 || y > point.bottom - 2 ?
  44319. naturalY :
  44320. y, point.half, point);
  44321. },
  44322. // dataLabels.distance determines the x position of the label
  44323. justify: function (point, radius, seriesCenter) {
  44324. return seriesCenter[0] + (point.half ? -1 : 1) *
  44325. (radius + point.labelDistance);
  44326. },
  44327. // Left edges of the left-half labels touch the left edge of the plot
  44328. // area. Right edges of the right-half labels touch the right edge of
  44329. // the plot area.
  44330. alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
  44331. const dataLabelWidth = dataLabel.getBBox().width;
  44332. return half ? dataLabelWidth + plotLeft :
  44333. plotWidth - dataLabelWidth - plotLeft;
  44334. },
  44335. // Connectors of each side end in the same x position. Labels are
  44336. // aligned to them. Left edge of the widest left-half label touches the
  44337. // left edge of the plot area. Right edge of the widest right-half label
  44338. // touches the right edge of the plot area.
  44339. alignToConnectors: function (points, half, plotWidth, plotLeft) {
  44340. let maxDataLabelWidth = 0, dataLabelWidth;
  44341. // find widest data label
  44342. points.forEach(function (point) {
  44343. dataLabelWidth = point.dataLabel.getBBox().width;
  44344. if (dataLabelWidth > maxDataLabelWidth) {
  44345. maxDataLabelWidth = dataLabelWidth;
  44346. }
  44347. });
  44348. return half ? maxDataLabelWidth + plotLeft :
  44349. plotWidth - maxDataLabelWidth - plotLeft;
  44350. }
  44351. };
  44352. /* *
  44353. *
  44354. * Functions
  44355. *
  44356. * */
  44357. /* eslint-disable valid-jsdoc */
  44358. /** @private */
  44359. function compose(PieSeriesClass) {
  44360. DataLabel.compose(Series);
  44361. if (U.pushUnique(composedMembers, PieSeriesClass)) {
  44362. const pieProto = PieSeriesClass.prototype;
  44363. pieProto.dataLabelPositioners = dataLabelPositioners;
  44364. pieProto.alignDataLabel = noop;
  44365. pieProto.drawDataLabels = drawDataLabels;
  44366. pieProto.placeDataLabels = placeDataLabels;
  44367. pieProto.verifyDataLabelOverflow = verifyDataLabelOverflow;
  44368. }
  44369. }
  44370. ColumnDataLabel.compose = compose;
  44371. /**
  44372. * Override the base drawDataLabels method by pie specific functionality
  44373. * @private
  44374. */
  44375. function drawDataLabels() {
  44376. 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 = [
  44377. [],
  44378. [] // left
  44379. ], overflow = [0, 0, 0, 0], // top, right, bottom, left
  44380. dataLabelPositioners = series.dataLabelPositioners;
  44381. let point, connectorWidth, connector, dataLabel, dataLabelWidth,
  44382. // labelPos,
  44383. labelPosition, labelHeight,
  44384. // divide the points into right and left halves for anti collision
  44385. x, y, visibility, j, pointDataLabelsOptions;
  44386. // get out if not enabled
  44387. if (!series.visible ||
  44388. (!options.enabled &&
  44389. !series._hasPointLabels)) {
  44390. return;
  44391. }
  44392. // Reset all labels that have been shortened
  44393. data.forEach(function (point) {
  44394. if (point.dataLabel && point.visible && point.dataLabel.shortened) {
  44395. point.dataLabel
  44396. .attr({
  44397. width: 'auto'
  44398. }).css({
  44399. width: 'auto',
  44400. textOverflow: 'clip'
  44401. });
  44402. point.dataLabel.shortened = false;
  44403. }
  44404. });
  44405. // run parent method
  44406. Series.prototype.drawDataLabels.apply(series);
  44407. data.forEach(function (point) {
  44408. if (point.dataLabel) {
  44409. if (point.visible) { // #407, #2510
  44410. // Arrange points for detection collision
  44411. halves[point.half].push(point);
  44412. // Reset positions (#4905)
  44413. point.dataLabel._pos = null;
  44414. // Avoid long labels squeezing the pie size too far down
  44415. if (!defined(options.style.width) &&
  44416. !defined(point.options.dataLabels &&
  44417. point.options.dataLabels.style &&
  44418. point.options.dataLabels.style.width)) {
  44419. if (point.dataLabel.getBBox().width > maxWidth) {
  44420. point.dataLabel.css({
  44421. // Use a fraction of the maxWidth to avoid
  44422. // wrapping close to the end of the string.
  44423. width: Math.round(maxWidth * 0.7) + 'px'
  44424. });
  44425. point.dataLabel.shortened = true;
  44426. }
  44427. }
  44428. }
  44429. else {
  44430. point.dataLabel = point.dataLabel.destroy();
  44431. // Workaround to make pies destroy multiple datalabels
  44432. // correctly. This logic needs rewriting to support multiple
  44433. // datalabels fully.
  44434. if (point.dataLabels && point.dataLabels.length === 1) {
  44435. delete point.dataLabels;
  44436. }
  44437. }
  44438. }
  44439. });
  44440. /* Loop over the points in each half, starting from the top and bottom
  44441. * of the pie to detect overlapping labels.
  44442. */
  44443. halves.forEach((points, i) => {
  44444. const length = points.length, positions = [];
  44445. let top, bottom, naturalY, sideOverflow, size = 0, distributionLength;
  44446. if (!length) {
  44447. return;
  44448. }
  44449. // Sort by angle
  44450. series.sortByAngle(points, i - 0.5);
  44451. // Only do anti-collision when we have dataLabels outside the pie
  44452. // and have connectors. (#856)
  44453. if (series.maxLabelDistance > 0) {
  44454. top = Math.max(0, centerY - radius - series.maxLabelDistance);
  44455. bottom = Math.min(centerY + radius + series.maxLabelDistance, chart.plotHeight);
  44456. points.forEach(function (point) {
  44457. // check if specific points' label is outside the pie
  44458. if (point.labelDistance > 0 && point.dataLabel) {
  44459. // point.top depends on point.labelDistance value
  44460. // Used for calculation of y value in getX method
  44461. point.top = Math.max(0, centerY - radius - point.labelDistance);
  44462. point.bottom = Math.min(centerY + radius + point.labelDistance, chart.plotHeight);
  44463. size = point.dataLabel.getBBox().height || 21;
  44464. // point.positionsIndex is needed for getting index of
  44465. // parameter related to specific point inside positions
  44466. // array - not every point is in positions array.
  44467. point.distributeBox = {
  44468. target: point.labelPosition.natural.y -
  44469. point.top + size / 2,
  44470. size,
  44471. rank: point.y
  44472. };
  44473. positions.push(point.distributeBox);
  44474. }
  44475. });
  44476. distributionLength = bottom + size - top;
  44477. distribute(positions, distributionLength, distributionLength / 5);
  44478. }
  44479. // Now the used slots are sorted, fill them up sequentially
  44480. for (j = 0; j < length; j++) {
  44481. point = points[j];
  44482. // labelPos = point.labelPos;
  44483. labelPosition = point.labelPosition;
  44484. dataLabel = point.dataLabel;
  44485. visibility = point.visible === false ? 'hidden' : 'inherit';
  44486. naturalY = labelPosition.natural.y;
  44487. y = naturalY;
  44488. if (positions && defined(point.distributeBox)) {
  44489. if (typeof point.distributeBox.pos === 'undefined') {
  44490. visibility = 'hidden';
  44491. }
  44492. else {
  44493. labelHeight = point.distributeBox.size;
  44494. // Find label's y position
  44495. y = dataLabelPositioners
  44496. .radialDistributionY(point);
  44497. }
  44498. }
  44499. // It is needed to delete point.positionIndex for
  44500. // dynamically added points etc.
  44501. delete point.positionIndex; // @todo unused
  44502. // Find label's x position
  44503. // justify is undocumented in the API - preserve support for it
  44504. if (options.justify) {
  44505. x = dataLabelPositioners.justify(point, radius, seriesCenter);
  44506. }
  44507. else {
  44508. switch (options.alignTo) {
  44509. case 'connectors':
  44510. x = dataLabelPositioners.alignToConnectors(points, i, plotWidth, plotLeft);
  44511. break;
  44512. case 'plotEdges':
  44513. x = dataLabelPositioners.alignToPlotEdges(dataLabel, i, plotWidth, plotLeft);
  44514. break;
  44515. default:
  44516. x = dataLabelPositioners.radialDistributionX(series, point, y, naturalY);
  44517. }
  44518. }
  44519. // Record the placement and visibility
  44520. dataLabel._attr = {
  44521. visibility: visibility,
  44522. align: labelPosition.alignment
  44523. };
  44524. pointDataLabelsOptions = point.options.dataLabels || {};
  44525. dataLabel._pos = {
  44526. x: (x +
  44527. pick(pointDataLabelsOptions.x, options.x) + // (#12985)
  44528. ({
  44529. left: connectorPadding,
  44530. right: -connectorPadding
  44531. }[labelPosition.alignment] || 0)),
  44532. y: (y +
  44533. pick(pointDataLabelsOptions.y, options.y) - // (#12985)
  44534. // Vertically center
  44535. dataLabel.getBBox().height / 2)
  44536. };
  44537. // labelPos.x = x;
  44538. // labelPos.y = y;
  44539. if (labelPosition) {
  44540. labelPosition.computed.x = x;
  44541. labelPosition.computed.y = y;
  44542. }
  44543. // Detect overflowing data labels
  44544. if (pick(options.crop, true)) {
  44545. dataLabelWidth = dataLabel.getBBox().width;
  44546. sideOverflow = null;
  44547. // Overflow left
  44548. if (x - dataLabelWidth < connectorPadding &&
  44549. i === 1 // left half
  44550. ) {
  44551. sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
  44552. overflow[3] = Math.max(sideOverflow, overflow[3]);
  44553. // Overflow right
  44554. }
  44555. else if (x + dataLabelWidth > plotWidth - connectorPadding &&
  44556. i === 0 // right half
  44557. ) {
  44558. sideOverflow = Math.round(x + dataLabelWidth - plotWidth + connectorPadding);
  44559. overflow[1] = Math.max(sideOverflow, overflow[1]);
  44560. }
  44561. // Overflow top
  44562. if (y - labelHeight / 2 < 0) {
  44563. overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
  44564. // Overflow left
  44565. }
  44566. else if (y + labelHeight / 2 > plotHeight) {
  44567. overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
  44568. }
  44569. dataLabel.sideOverflow = sideOverflow;
  44570. }
  44571. } // for each point
  44572. }); // for each half
  44573. // Do not apply the final placement and draw the connectors until we
  44574. // have verified that labels are not spilling over.
  44575. if (arrayMax(overflow) === 0 ||
  44576. this.verifyDataLabelOverflow(overflow)) {
  44577. // Place the labels in the final position
  44578. this.placeDataLabels();
  44579. this.points.forEach(function (point) {
  44580. // #8864: every connector can have individual options
  44581. pointDataLabelsOptions =
  44582. merge(options, point.options.dataLabels);
  44583. connectorWidth =
  44584. pick(pointDataLabelsOptions.connectorWidth, 1);
  44585. // Draw the connector
  44586. if (connectorWidth) {
  44587. let isNew;
  44588. connector = point.connector;
  44589. dataLabel = point.dataLabel;
  44590. if (dataLabel &&
  44591. dataLabel._pos &&
  44592. point.visible &&
  44593. point.labelDistance > 0) {
  44594. visibility = dataLabel._attr.visibility;
  44595. isNew = !connector;
  44596. if (isNew) {
  44597. point.connector = connector = chart.renderer
  44598. .path()
  44599. .addClass('highcharts-data-label-connector ' +
  44600. ' highcharts-color-' + point.colorIndex +
  44601. (point.className ?
  44602. ' ' + point.className :
  44603. ''))
  44604. .add(series.dataLabelsGroup);
  44605. if (!chart.styledMode) {
  44606. connector.attr({
  44607. 'stroke-width': connectorWidth,
  44608. 'stroke': (pointDataLabelsOptions.connectorColor ||
  44609. point.color ||
  44610. "#666666" /* Palette.neutralColor60 */)
  44611. });
  44612. }
  44613. }
  44614. connector[isNew ? 'attr' : 'animate']({
  44615. d: point.getConnectorPath()
  44616. });
  44617. connector.attr('visibility', visibility);
  44618. }
  44619. else if (connector) {
  44620. point.connector = connector.destroy();
  44621. }
  44622. }
  44623. });
  44624. }
  44625. }
  44626. /**
  44627. * Perform the final placement of the data labels after we have verified
  44628. * that they fall within the plot area.
  44629. * @private
  44630. */
  44631. function placeDataLabels() {
  44632. this.points.forEach(function (point) {
  44633. let dataLabel = point.dataLabel, _pos;
  44634. if (dataLabel && point.visible) {
  44635. _pos = dataLabel._pos;
  44636. if (_pos) {
  44637. // Shorten data labels with ellipsis if they still overflow
  44638. // after the pie has reached minSize (#223).
  44639. if (dataLabel.sideOverflow) {
  44640. dataLabel._attr.width =
  44641. Math.max(dataLabel.getBBox().width -
  44642. dataLabel.sideOverflow, 0);
  44643. dataLabel.css({
  44644. width: dataLabel._attr.width + 'px',
  44645. textOverflow: ((this.options.dataLabels.style || {})
  44646. .textOverflow ||
  44647. 'ellipsis')
  44648. });
  44649. dataLabel.shortened = true;
  44650. }
  44651. dataLabel.attr(dataLabel._attr);
  44652. dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
  44653. dataLabel.moved = true;
  44654. }
  44655. else if (dataLabel) {
  44656. dataLabel.attr({ y: -9999 });
  44657. }
  44658. }
  44659. // Clear for update
  44660. delete point.distributeBox;
  44661. }, this);
  44662. }
  44663. /**
  44664. * Verify whether the data labels are allowed to draw, or we should run more
  44665. * translation and data label positioning to keep them inside the plot area.
  44666. * Returns true when data labels are ready to draw.
  44667. * @private
  44668. */
  44669. function verifyDataLabelOverflow(overflow) {
  44670. let center = this.center, options = this.options, centerOption = options.center, minSize = options.minSize || 80, newSize = minSize,
  44671. // If a size is set, return true and don't try to shrink the pie
  44672. // to fit the labels.
  44673. ret = options.size !== null;
  44674. if (!ret) {
  44675. // Handle horizontal size and center
  44676. if (centerOption[0] !== null) { // Fixed center
  44677. newSize = Math.max(center[2] -
  44678. Math.max(overflow[1], overflow[3]), minSize);
  44679. }
  44680. else { // Auto center
  44681. newSize = Math.max(
  44682. // horizontal overflow
  44683. center[2] - overflow[1] - overflow[3], minSize);
  44684. // horizontal center
  44685. center[0] += (overflow[3] - overflow[1]) / 2;
  44686. }
  44687. // Handle vertical size and center
  44688. if (centerOption[1] !== null) { // Fixed center
  44689. newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
  44690. }
  44691. else { // Auto center
  44692. newSize = clamp(newSize, minSize,
  44693. // vertical overflow
  44694. center[2] - overflow[0] - overflow[2]);
  44695. // vertical center
  44696. center[1] += (overflow[0] - overflow[2]) / 2;
  44697. }
  44698. // If the size must be decreased, we need to run translate and
  44699. // drawDataLabels again
  44700. if (newSize < center[2]) {
  44701. center[2] = newSize;
  44702. center[3] = Math.min(// #3632
  44703. options.thickness ?
  44704. Math.max(0, newSize - options.thickness * 2) :
  44705. Math.max(0, relativeLength(options.innerSize || 0, newSize)), newSize); // #6647
  44706. this.translate(center);
  44707. if (this.drawDataLabels) {
  44708. this.drawDataLabels();
  44709. }
  44710. // Else, return true to indicate that the pie and its labels is
  44711. // within the plot area
  44712. }
  44713. else {
  44714. ret = true;
  44715. }
  44716. }
  44717. return ret;
  44718. }
  44719. })(ColumnDataLabel || (ColumnDataLabel = {}));
  44720. /* *
  44721. *
  44722. * Default Export
  44723. *
  44724. * */
  44725. return ColumnDataLabel;
  44726. });
  44727. _registerModule(_modules, 'Extensions/OverlappingDataLabels.js', [_modules['Core/Chart/Chart.js'], _modules['Core/Utilities.js']], function (Chart, U) {
  44728. /* *
  44729. *
  44730. * Highcharts module to hide overlapping data labels.
  44731. * This module is included in Highcharts.
  44732. *
  44733. * (c) 2009-2021 Torstein Honsi
  44734. *
  44735. * License: www.highcharts.com/license
  44736. *
  44737. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44738. *
  44739. * */
  44740. const { addEvent, fireEvent, isArray, isNumber, objectEach, pick } = U;
  44741. /**
  44742. * Internal type
  44743. * @private
  44744. */
  44745. /* eslint-disable no-invalid-this */
  44746. // Collect potensial overlapping data labels. Stack labels probably don't need
  44747. // to be considered because they are usually accompanied by data labels that lie
  44748. // inside the columns.
  44749. addEvent(Chart, 'render', function collectAndHide() {
  44750. let chart = this, labels = [];
  44751. // Consider external label collectors
  44752. (this.labelCollectors || []).forEach(function (collector) {
  44753. labels = labels.concat(collector());
  44754. });
  44755. (this.yAxis || []).forEach(function (yAxis) {
  44756. if (yAxis.stacking &&
  44757. yAxis.options.stackLabels &&
  44758. !yAxis.options.stackLabels.allowOverlap) {
  44759. objectEach(yAxis.stacking.stacks, function (stack) {
  44760. objectEach(stack, function (stackItem) {
  44761. if (stackItem.label) {
  44762. labels.push(stackItem.label);
  44763. }
  44764. });
  44765. });
  44766. }
  44767. });
  44768. (this.series || []).forEach(function (series) {
  44769. const dlOptions = series.options.dataLabels;
  44770. if (series.visible &&
  44771. !(dlOptions.enabled === false && !series._hasPointLabels)) { // #3866
  44772. const push = (points) => points.forEach((point) => {
  44773. if (point.visible) {
  44774. const dataLabels = (isArray(point.dataLabels) ?
  44775. point.dataLabels :
  44776. (point.dataLabel ? [point.dataLabel] : []));
  44777. dataLabels.forEach(function (label) {
  44778. const options = label.options;
  44779. label.labelrank = pick(options.labelrank, point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
  44780. if (!options.allowOverlap) {
  44781. labels.push(label);
  44782. }
  44783. else { // #13449
  44784. label.oldOpacity = label.opacity;
  44785. label.newOpacity = 1;
  44786. hideOrShow(label, chart);
  44787. }
  44788. });
  44789. }
  44790. });
  44791. push(series.nodes || []);
  44792. push(series.points);
  44793. }
  44794. });
  44795. this.hideOverlappingLabels(labels);
  44796. });
  44797. /**
  44798. * Hide overlapping labels. Labels are moved and faded in and out on zoom to
  44799. * provide a smooth visual imression.
  44800. *
  44801. * @private
  44802. * @function Highcharts.Chart#hideOverlappingLabels
  44803. * @param {Array<Highcharts.SVGElement>} labels
  44804. * Rendered data labels
  44805. * @requires modules/overlapping-datalabels
  44806. */
  44807. Chart.prototype.hideOverlappingLabels = function (labels) {
  44808. let chart = this, len = labels.length, ren = chart.renderer, label, i, j, label1, label2, box1, box2, isLabelAffected = false, isIntersectRect = function (box1, box2) {
  44809. return !(box2.x >= box1.x + box1.width ||
  44810. box2.x + box2.width <= box1.x ||
  44811. box2.y >= box1.y + box1.height ||
  44812. box2.y + box2.height <= box1.y);
  44813. },
  44814. // Get the box with its position inside the chart, as opposed to getBBox
  44815. // that only reports the position relative to the parent.
  44816. getAbsoluteBox = function (label) {
  44817. let pos, parent, bBox,
  44818. // Substract the padding if no background or border (#4333)
  44819. padding = label.box ? 0 : (label.padding || 0), lineHeightCorrection = 0, xOffset = 0, boxWidth, alignValue;
  44820. if (label &&
  44821. (!label.alignAttr || label.placed)) {
  44822. pos = label.alignAttr || {
  44823. x: label.attr('x'),
  44824. y: label.attr('y')
  44825. };
  44826. parent = label.parentGroup;
  44827. // Get width and height if pure text nodes (stack labels)
  44828. if (!label.width) {
  44829. bBox = label.getBBox();
  44830. label.width = bBox.width;
  44831. label.height = bBox.height;
  44832. // Labels positions are computed from top left corner, so we
  44833. // need to substract the text height from text nodes too.
  44834. lineHeightCorrection = ren.fontMetrics(label.element).h;
  44835. }
  44836. boxWidth = label.width - 2 * padding;
  44837. alignValue = {
  44838. left: '0',
  44839. center: '0.5',
  44840. right: '1'
  44841. }[label.alignValue];
  44842. if (alignValue) {
  44843. xOffset = +alignValue * boxWidth;
  44844. }
  44845. else if (isNumber(label.x) &&
  44846. Math.round(label.x) !== label.translateX) {
  44847. xOffset = label.x - label.translateX;
  44848. }
  44849. return {
  44850. x: pos.x + (parent.translateX || 0) + padding -
  44851. (xOffset || 0),
  44852. y: pos.y + (parent.translateY || 0) + padding -
  44853. lineHeightCorrection,
  44854. width: label.width - 2 * padding,
  44855. height: label.height - 2 * padding
  44856. };
  44857. }
  44858. };
  44859. for (i = 0; i < len; i++) {
  44860. label = labels[i];
  44861. if (label) {
  44862. // Mark with initial opacity
  44863. label.oldOpacity = label.opacity;
  44864. label.newOpacity = 1;
  44865. label.absoluteBox = getAbsoluteBox(label);
  44866. }
  44867. }
  44868. // Prevent a situation in a gradually rising slope, that each label will
  44869. // hide the previous one because the previous one always has lower rank.
  44870. labels.sort(function (a, b) {
  44871. return (b.labelrank || 0) - (a.labelrank || 0);
  44872. });
  44873. // Detect overlapping labels
  44874. for (i = 0; i < len; i++) {
  44875. label1 = labels[i];
  44876. box1 = label1 && label1.absoluteBox;
  44877. for (j = i + 1; j < len; ++j) {
  44878. label2 = labels[j];
  44879. box2 = label2 && label2.absoluteBox;
  44880. if (box1 &&
  44881. box2 &&
  44882. label1 !== label2 && // #6465, polar chart with connectEnds
  44883. label1.newOpacity !== 0 &&
  44884. label2.newOpacity !== 0 &&
  44885. // #15863 dataLabels are no longer hidden by translation
  44886. label1.visibility !== 'hidden' &&
  44887. label2.visibility !== 'hidden') {
  44888. if (isIntersectRect(box1, box2)) {
  44889. (label1.labelrank < label2.labelrank ? label1 : label2)
  44890. .newOpacity = 0;
  44891. }
  44892. }
  44893. }
  44894. }
  44895. // Hide or show
  44896. labels.forEach(function (label) {
  44897. if (hideOrShow(label, chart)) {
  44898. isLabelAffected = true;
  44899. }
  44900. });
  44901. if (isLabelAffected) {
  44902. fireEvent(chart, 'afterHideAllOverlappingLabels');
  44903. }
  44904. };
  44905. /**
  44906. * Hide or show labels based on opacity.
  44907. *
  44908. * @private
  44909. * @function hideOrShow
  44910. * @param {Highcharts.SVGElement} label
  44911. * The label.
  44912. * @param {Highcharts.Chart} chart
  44913. * The chart that contains the label.
  44914. * @return {boolean}
  44915. * Whether label is affected
  44916. */
  44917. function hideOrShow(label, chart) {
  44918. let complete, newOpacity, isLabelAffected = false;
  44919. if (label) {
  44920. newOpacity = label.newOpacity;
  44921. if (label.oldOpacity !== newOpacity) {
  44922. // Make sure the label is completely hidden to avoid catching clicks
  44923. // (#4362)
  44924. if (label.alignAttr && label.placed) { // data labels
  44925. label[newOpacity ? 'removeClass' : 'addClass']('highcharts-data-label-hidden');
  44926. complete = function () {
  44927. if (!chart.styledMode) {
  44928. label.css({
  44929. pointerEvents: newOpacity ? 'auto' : 'none'
  44930. });
  44931. }
  44932. };
  44933. isLabelAffected = true;
  44934. // Animate or set the opacity
  44935. label.alignAttr.opacity = newOpacity;
  44936. label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
  44937. fireEvent(chart, 'afterHideOverlappingLabel');
  44938. }
  44939. else { // other labels, tick labels
  44940. label.attr({
  44941. opacity: newOpacity
  44942. });
  44943. }
  44944. }
  44945. label.isOld = true;
  44946. }
  44947. return isLabelAffected;
  44948. }
  44949. });
  44950. _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) {
  44951. /* *
  44952. *
  44953. * Highcharts Border Radius module
  44954. *
  44955. * Author: Torstein Honsi
  44956. *
  44957. * License: www.highcharts.com/license
  44958. *
  44959. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  44960. *
  44961. * */
  44962. const { defaultOptions } = D;
  44963. const { seriesTypes } = SeriesRegistry;
  44964. const { addEvent, extend, isObject, merge, relativeLength } = U;
  44965. /* *
  44966. *
  44967. * Constants
  44968. *
  44969. * */
  44970. const defaultBorderRadiusOptions = {
  44971. radius: 0,
  44972. scope: 'stack',
  44973. where: void 0
  44974. };
  44975. const optionsToObject = (options, seriesBROptions) => {
  44976. if (!isObject(options)) {
  44977. options = { radius: options || 0 };
  44978. }
  44979. return merge(defaultBorderRadiusOptions, seriesBROptions, options);
  44980. };
  44981. const applyBorderRadius = (path, i, r) => {
  44982. const a = path[i];
  44983. let b = path[i + 1];
  44984. if (b[0] === 'Z') {
  44985. b = path[0];
  44986. }
  44987. let line, arc, fromLineToArc;
  44988. // From straight line to arc
  44989. if ((a[0] === 'M' || a[0] === 'L') && b[0] === 'A') {
  44990. line = a;
  44991. arc = b;
  44992. fromLineToArc = true;
  44993. // From arc to straight line
  44994. }
  44995. else if (a[0] === 'A' && (b[0] === 'M' || b[0] === 'L')) {
  44996. line = b;
  44997. arc = a;
  44998. }
  44999. if (line && arc && arc.params) {
  45000. const bigR = arc[1],
  45001. // In our use cases, outer pie slice arcs are clockwise and inner
  45002. // arcs (donut/sunburst etc) are anti-clockwise
  45003. clockwise = arc[5], params = arc.params, { start, end, cx, cy } = params;
  45004. // Some geometric constants
  45005. const relativeR = clockwise ? (bigR - r) : (bigR + r),
  45006. // The angle, on the big arc, that the border radius arc takes up
  45007. angleOfBorderRadius = relativeR ? Math.asin(r / relativeR) : 0, angleOffset = clockwise ?
  45008. angleOfBorderRadius :
  45009. -angleOfBorderRadius,
  45010. // The distance along the radius of the big arc to the starting
  45011. // point of the small border radius arc
  45012. distanceBigCenterToStartArc = (Math.cos(angleOfBorderRadius) *
  45013. relativeR);
  45014. // From line to arc
  45015. if (fromLineToArc) {
  45016. // Update the cache
  45017. params.start = start + angleOffset;
  45018. // First move to the start position at the radial line. We want to
  45019. // start one borderRadius closer to the center.
  45020. line[1] = cx + distanceBigCenterToStartArc * Math.cos(start);
  45021. line[2] = cy + distanceBigCenterToStartArc * Math.sin(start);
  45022. // Now draw an arc towards the point where the small circle touches
  45023. // the great circle.
  45024. path.splice(i + 1, 0, [
  45025. 'A',
  45026. r,
  45027. r,
  45028. 0,
  45029. 0,
  45030. 1,
  45031. cx + bigR * Math.cos(params.start),
  45032. cy + bigR * Math.sin(params.start)
  45033. ]);
  45034. // From arc to line
  45035. }
  45036. else {
  45037. // Update the cache
  45038. params.end = end - angleOffset;
  45039. // End the big arc a bit earlier
  45040. arc[6] = cx + bigR * Math.cos(params.end);
  45041. arc[7] = cy + bigR * Math.sin(params.end);
  45042. // Draw a small arc towards a point on the end angle, but one
  45043. // borderRadius closer to the center relative to the perimeter.
  45044. path.splice(i + 1, 0, [
  45045. 'A',
  45046. r,
  45047. r,
  45048. 0,
  45049. 0,
  45050. 1,
  45051. cx + distanceBigCenterToStartArc * Math.cos(end),
  45052. cy + distanceBigCenterToStartArc * Math.sin(end)
  45053. ]);
  45054. }
  45055. // Long or short arc must be reconsidered because we have modified the
  45056. // start and end points
  45057. arc[4] = Math.abs(params.end - params.start) < Math.PI ? 0 : 1;
  45058. }
  45059. };
  45060. /* *
  45061. *
  45062. * Modifications
  45063. *
  45064. * */
  45065. // Check if the module has already been imported
  45066. // @todo implement as composition
  45067. if (SVGElement.symbolCustomAttribs.indexOf('borderRadius') === -1) {
  45068. SVGElement.symbolCustomAttribs.push('borderRadius', 'brBoxHeight', 'brBoxY');
  45069. // Extend arc with borderRadius
  45070. const arc = SVGRenderer.prototype.symbols.arc;
  45071. SVGRenderer.prototype.symbols.arc = function (x, y, w, h, options = {}) {
  45072. const path = arc(x, y, w, h, options), { innerR = 0, r = w, start = 0, end = 0 } = options;
  45073. if (options.open || !options.borderRadius) {
  45074. return path;
  45075. }
  45076. const alpha = end - start, sinHalfAlpha = Math.sin(alpha / 2), borderRadius = Math.max(Math.min(relativeLength(options.borderRadius || 0, r - innerR),
  45077. // Cap to half the sector radius
  45078. (r - innerR) / 2,
  45079. // For smaller pie slices, cap to the largest small circle that
  45080. // can be fitted within the sector
  45081. (r * sinHalfAlpha) / (1 + sinHalfAlpha)), 0),
  45082. // For the inner radius, we need an extra cap because the inner arc
  45083. // is shorter than the outer arc
  45084. innerBorderRadius = Math.min(borderRadius, 2 * (alpha / Math.PI) * innerR);
  45085. // Apply turn-by-turn border radius. Start at the end since we're
  45086. // splicing in arc segments.
  45087. let i = path.length - 1;
  45088. while (i--) {
  45089. applyBorderRadius(path, i, i > 1 ? innerBorderRadius : borderRadius);
  45090. }
  45091. return path;
  45092. };
  45093. // Extend roundedRect with individual cutting through rOffset
  45094. const roundedRect = SVGRenderer.prototype.symbols.roundedRect;
  45095. SVGRenderer.prototype.symbols.roundedRect = function (x, y, width, height, options = {}) {
  45096. const path = roundedRect(x, y, width, height, options), { r = 0, brBoxHeight = height, brBoxY = y } = options, brOffsetTop = y - brBoxY, brOffsetBtm = (brBoxY + brBoxHeight) - (y + height),
  45097. // When the distance to the border-radius box is greater than the r
  45098. // itself, it means no border radius. The -0.1 accounts for float
  45099. // rounding errors.
  45100. 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);
  45101. /*
  45102. The naming of control points:
  45103. / a -------- b \
  45104. / \
  45105. h c
  45106. | |
  45107. | |
  45108. | |
  45109. g d
  45110. \ /
  45111. \ f -------- e /
  45112. */
  45113. const a = [x + rTop, y], b = [x + width - rTop, y], c = [x + width, y + rTop], d = [
  45114. x + width, y + height - rBtm
  45115. ], e = [
  45116. x + width - rBtm,
  45117. y + height
  45118. ], f = [x + rBtm, y + height], g = [x, y + height - rBtm], h = [x, y + rTop];
  45119. const applyPythagoras = (r, altitude) => Math.sqrt(Math.pow(r, 2) - Math.pow(altitude, 2));
  45120. // Inside stacks, cut off part of the top
  45121. if (cutTop) {
  45122. const base = applyPythagoras(rTop, rTop - cutTop);
  45123. a[0] -= base;
  45124. b[0] += base;
  45125. c[1] = h[1] = y + rTop - cutTop;
  45126. }
  45127. // Column is lower than the radius. Cut off bottom inside the top
  45128. // radius.
  45129. if (height < rTop - cutTop) {
  45130. const base = applyPythagoras(rTop, rTop - cutTop - height);
  45131. c[0] = d[0] = x + width - rTop + base;
  45132. e[0] = Math.min(c[0], e[0]);
  45133. f[0] = Math.max(d[0], f[0]);
  45134. g[0] = h[0] = x + rTop - base;
  45135. c[1] = h[1] = y + height;
  45136. }
  45137. // Inside stacks, cut off part of the bottom
  45138. if (cutBtm) {
  45139. const base = applyPythagoras(rBtm, rBtm - cutBtm);
  45140. e[0] += base;
  45141. f[0] -= base;
  45142. d[1] = g[1] = y + height - rBtm + cutBtm;
  45143. }
  45144. // Cut off top inside the bottom radius
  45145. if (height < rBtm - cutBtm) {
  45146. const base = applyPythagoras(rBtm, rBtm - cutBtm - height);
  45147. c[0] = d[0] = x + width - rBtm + base;
  45148. b[0] = Math.min(c[0], b[0]);
  45149. a[0] = Math.max(d[0], a[0]);
  45150. g[0] = h[0] = x + rBtm - base;
  45151. d[1] = g[1] = y;
  45152. }
  45153. // Preserve the box for data labels
  45154. path.length = 0;
  45155. path.push(['M', ...a],
  45156. // top side
  45157. ['L', ...b],
  45158. // top right corner
  45159. ['A', rTop, rTop, 0, 0, 1, ...c],
  45160. // right side
  45161. ['L', ...d],
  45162. // bottom right corner
  45163. ['A', rBtm, rBtm, 0, 0, 1, ...e],
  45164. // bottom side
  45165. ['L', ...f],
  45166. // bottom left corner
  45167. ['A', rBtm, rBtm, 0, 0, 1, ...g],
  45168. // left side
  45169. ['L', ...h],
  45170. // top left corner
  45171. ['A', rTop, rTop, 0, 0, 1, ...a], ['Z']);
  45172. return path;
  45173. };
  45174. addEvent(seriesTypes.pie, 'afterTranslate', function () {
  45175. const borderRadius = optionsToObject(this.options.borderRadius);
  45176. for (const point of this.points) {
  45177. const shapeArgs = point.shapeArgs;
  45178. if (shapeArgs) {
  45179. shapeArgs.borderRadius = relativeLength(borderRadius.radius, (shapeArgs.r || 0) - ((shapeArgs.innerR) || 0));
  45180. }
  45181. }
  45182. });
  45183. addEvent(Series, 'afterColumnTranslate', function () {
  45184. var _a,
  45185. _b;
  45186. if (this.options.borderRadius &&
  45187. !(this.chart.is3d && this.chart.is3d())) {
  45188. 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;
  45189. for (const point of this.points) {
  45190. const { shapeArgs } = point;
  45191. if (point.shapeType === 'roundedRect' && shapeArgs) {
  45192. const { width = 0, height = 0, y = 0 } = shapeArgs;
  45193. let brBoxY = y, brBoxHeight = height;
  45194. // It would be nice to refactor StackItem.getStackBox/
  45195. // setOffset so that we could get a reliable box out of
  45196. // it. Currently it is close if we remove the label
  45197. // offset, but we still need to run crispCol and also
  45198. // flip it if inverted, so atm it is simpler to do it
  45199. // like the below.
  45200. if (borderRadius.scope === 'stack' &&
  45201. point.stackTotal) {
  45202. 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));
  45203. brBoxY = box.y;
  45204. brBoxHeight = box.height;
  45205. }
  45206. const flip = (point.negative ? -1 : 1) *
  45207. (reversed ? -1 : 1) === -1;
  45208. // Handle the where option
  45209. let where = borderRadius.where;
  45210. // Waterfall, hanging columns should have rounding on
  45211. // all sides
  45212. if (!where &&
  45213. this.is('waterfall') &&
  45214. Math.abs((point.yBottom || 0) -
  45215. (this.translatedThreshold || 0)) > this.borderWidth) {
  45216. where = 'all';
  45217. }
  45218. if (!where) {
  45219. where = 'end';
  45220. }
  45221. // Get the radius
  45222. const r = Math.min(relativeLength(borderRadius.radius, width), width / 2,
  45223. // Cap to the height, but not if where is `end`
  45224. where === 'all' ? height / 2 : Infinity) || 0;
  45225. // If the `where` option is 'end', cut off the
  45226. // rectangles by making the border-radius box one r
  45227. // greater, so that the imaginary radius falls outside
  45228. // the rectangle.
  45229. if (where === 'end') {
  45230. if (flip) {
  45231. brBoxY -= r;
  45232. brBoxHeight += r;
  45233. }
  45234. else {
  45235. brBoxHeight += r;
  45236. }
  45237. }
  45238. extend(shapeArgs, { brBoxHeight, brBoxY, r });
  45239. }
  45240. }
  45241. }
  45242. }, {
  45243. // After columnrange and polar column modifications
  45244. order: 9
  45245. });
  45246. }
  45247. /* *
  45248. *
  45249. * Default Export
  45250. *
  45251. * */
  45252. const BorderRadius = {
  45253. optionsToObject
  45254. };
  45255. /* *
  45256. *
  45257. * API Declarations
  45258. *
  45259. * */
  45260. /**
  45261. * Detailed options for border radius.
  45262. *
  45263. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  45264. * Rounded columns
  45265. * @sample highcharts/plotoptions/series-border-radius
  45266. * Column and pie with rounded border
  45267. *
  45268. * @interface Highcharts.BorderRadiusOptionsObject
  45269. */ /**
  45270. * The border radius. A number signifies pixels. A percentage string, like for
  45271. * example `50%`, signifies a relative size. For columns this is relative to the
  45272. * column width, for pies it is relative to the radius and the inner radius.
  45273. *
  45274. * @name Highcharts.BorderRadiusOptionsObject#radius
  45275. * @type {string|number}
  45276. */ /**
  45277. * The scope of the rounding for column charts. In a stacked column chart, the
  45278. * value `point` means each single point will get rounded corners. The value
  45279. * `stack` means the rounding will apply to the full stack, so that only points
  45280. * close to the top or bottom will receive rounding.
  45281. *
  45282. * @name Highcharts.BorderRadiusOptionsObject#scope
  45283. * @validvalue ["point", "stack"]
  45284. * @type {string}
  45285. */ /**
  45286. * For column charts, where in the point or stack to apply rounding. The `end`
  45287. * value means only those corners at the point value will be rounded, leaving
  45288. * the corners at the base or threshold unrounded. This is the most intuitive
  45289. * behaviour. The `all` value means also the base will be rounded.
  45290. *
  45291. * @name Highcharts.BorderRadiusOptionsObject#where
  45292. * @validvalue ["all", "end"]
  45293. * @type {string}
  45294. * @default end
  45295. */
  45296. (''); // keeps doclets above in JS file
  45297. return BorderRadius;
  45298. });
  45299. _registerModule(_modules, 'Core/Responsive.js', [_modules['Core/Utilities.js']], function (U) {
  45300. /* *
  45301. *
  45302. * (c) 2010-2021 Torstein Honsi
  45303. *
  45304. * License: www.highcharts.com/license
  45305. *
  45306. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45307. *
  45308. * */
  45309. const { diffObjects, extend, find, isArray, isObject, merge, objectEach, pick, splat, uniqueKey } = U;
  45310. /* *
  45311. *
  45312. * Composition
  45313. *
  45314. * */
  45315. var Responsive;
  45316. (function (Responsive) {
  45317. /* *
  45318. *
  45319. * Declarations
  45320. *
  45321. * */
  45322. /* *
  45323. *
  45324. * Constants
  45325. *
  45326. * */
  45327. const composedMembers = [];
  45328. /* *
  45329. *
  45330. * Functions
  45331. *
  45332. * */
  45333. /**
  45334. * @private
  45335. */
  45336. function compose(ChartClass) {
  45337. if (U.pushUnique(composedMembers, ChartClass)) {
  45338. extend(ChartClass.prototype, {
  45339. matchResponsiveRule,
  45340. setResponsive
  45341. });
  45342. }
  45343. return ChartClass;
  45344. }
  45345. Responsive.compose = compose;
  45346. /**
  45347. * Handle a single responsiveness rule.
  45348. *
  45349. * @private
  45350. * @function Highcharts.Chart#matchResponsiveRule
  45351. * @param {Highcharts.ResponsiveRulesOptions} rule
  45352. * @param {Array<string>} matches
  45353. */
  45354. function matchResponsiveRule(rule, matches) {
  45355. const condition = rule.condition, fn = condition.callback || function () {
  45356. return (this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
  45357. this.chartHeight <= pick(condition.maxHeight, Number.MAX_VALUE) &&
  45358. this.chartWidth >= pick(condition.minWidth, 0) &&
  45359. this.chartHeight >= pick(condition.minHeight, 0));
  45360. };
  45361. if (fn.call(this)) {
  45362. matches.push(rule._id);
  45363. }
  45364. }
  45365. /**
  45366. * Update the chart based on the current chart/document size and options
  45367. * for responsiveness.
  45368. *
  45369. * @private
  45370. * @function Highcharts.Chart#setResponsive
  45371. * @param {boolean} [redraw=true]
  45372. * @param {boolean} [reset=false]
  45373. * Reset by un-applying all rules. Chart.update resets all rules before
  45374. * applying updated options.
  45375. */
  45376. function setResponsive(redraw, reset) {
  45377. const options = this.options.responsive, currentResponsive = this.currentResponsive;
  45378. let ruleIds = [], undoOptions;
  45379. if (!reset && options && options.rules) {
  45380. options.rules.forEach((rule) => {
  45381. if (typeof rule._id === 'undefined') {
  45382. rule._id = uniqueKey();
  45383. }
  45384. this.matchResponsiveRule(rule, ruleIds /* , redraw */);
  45385. }, this);
  45386. }
  45387. // Merge matching rules
  45388. const mergedOptions = merge(...ruleIds
  45389. .map((ruleId) => find((options || {}).rules || [], (rule) => (rule._id === ruleId)))
  45390. .map((rule) => (rule && rule.chartOptions)));
  45391. mergedOptions.isResponsiveOptions = true;
  45392. // Stringified key for the rules that currently apply.
  45393. ruleIds = (ruleIds.toString() || void 0);
  45394. const currentRuleIds = (currentResponsive && currentResponsive.ruleIds);
  45395. // Changes in what rules apply
  45396. if (ruleIds !== currentRuleIds) {
  45397. // Undo previous rules. Before we apply a new set of rules, we
  45398. // need to roll back completely to base options (#6291).
  45399. if (currentResponsive) {
  45400. this.update(currentResponsive.undoOptions, redraw, true);
  45401. }
  45402. if (ruleIds) {
  45403. // Get undo-options for matching rules. The `undoOptions``
  45404. // hold the current values before they are changed by the
  45405. // `mergedOptions`.
  45406. undoOptions = diffObjects(mergedOptions, this.options, true, this.collectionsWithUpdate);
  45407. undoOptions.isResponsiveOptions = true;
  45408. this.currentResponsive = {
  45409. ruleIds: ruleIds,
  45410. mergedOptions: mergedOptions,
  45411. undoOptions: undoOptions
  45412. };
  45413. this.update(mergedOptions, redraw, true);
  45414. }
  45415. else {
  45416. this.currentResponsive = void 0;
  45417. }
  45418. }
  45419. }
  45420. })(Responsive || (Responsive = {}));
  45421. /* *
  45422. *
  45423. * Default Export
  45424. *
  45425. * */
  45426. /* *
  45427. *
  45428. * API Declarations
  45429. *
  45430. * */
  45431. /**
  45432. * A callback function to gain complete control on when the responsive rule
  45433. * applies.
  45434. *
  45435. * @callback Highcharts.ResponsiveCallbackFunction
  45436. *
  45437. * @param {Highcharts.Chart} this
  45438. * Chart context.
  45439. *
  45440. * @return {boolean}
  45441. * Return `true` if it applies.
  45442. */
  45443. (''); // keeps doclets above in JS file
  45444. /* *
  45445. *
  45446. * API Options
  45447. *
  45448. * */
  45449. /**
  45450. * Allows setting a set of rules to apply for different screen or chart
  45451. * sizes. Each rule specifies additional chart options.
  45452. *
  45453. * @sample {highstock} stock/demo/responsive/
  45454. * Stock chart
  45455. * @sample highcharts/responsive/axis/
  45456. * Axis
  45457. * @sample highcharts/responsive/legend/
  45458. * Legend
  45459. * @sample highcharts/responsive/classname/
  45460. * Class name
  45461. *
  45462. * @since 5.0.0
  45463. * @apioption responsive
  45464. */
  45465. /**
  45466. * A set of rules for responsive settings. The rules are executed from
  45467. * the top down.
  45468. *
  45469. * @sample {highcharts} highcharts/responsive/axis/
  45470. * Axis changes
  45471. * @sample {highstock} highcharts/responsive/axis/
  45472. * Axis changes
  45473. * @sample {highmaps} highcharts/responsive/axis/
  45474. * Axis changes
  45475. *
  45476. * @type {Array<*>}
  45477. * @since 5.0.0
  45478. * @apioption responsive.rules
  45479. */
  45480. /**
  45481. * A full set of chart options to apply as overrides to the general
  45482. * chart options. The chart options are applied when the given rule
  45483. * is active.
  45484. *
  45485. * A special case is configuration objects that take arrays, for example
  45486. * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
  45487. * collections, an `id` option is used to map the new option set to
  45488. * an existing object. If an existing object of the same id is not found,
  45489. * the item of the same indexupdated. So for example, setting `chartOptions`
  45490. * with two series items without an `id`, will cause the existing chart's
  45491. * two series to be updated with respective options.
  45492. *
  45493. * @sample {highstock} stock/demo/responsive/
  45494. * Stock chart
  45495. * @sample highcharts/responsive/axis/
  45496. * Axis
  45497. * @sample highcharts/responsive/legend/
  45498. * Legend
  45499. * @sample highcharts/responsive/classname/
  45500. * Class name
  45501. *
  45502. * @type {Highcharts.Options}
  45503. * @since 5.0.0
  45504. * @apioption responsive.rules.chartOptions
  45505. */
  45506. /**
  45507. * Under which conditions the rule applies.
  45508. *
  45509. * @since 5.0.0
  45510. * @apioption responsive.rules.condition
  45511. */
  45512. /**
  45513. * A callback function to gain complete control on when the responsive
  45514. * rule applies. Return `true` if it applies. This opens for checking
  45515. * against other metrics than the chart size, for example the document
  45516. * size or other elements.
  45517. *
  45518. * @type {Highcharts.ResponsiveCallbackFunction}
  45519. * @since 5.0.0
  45520. * @context Highcharts.Chart
  45521. * @apioption responsive.rules.condition.callback
  45522. */
  45523. /**
  45524. * The responsive rule applies if the chart height is less than this.
  45525. *
  45526. * @type {number}
  45527. * @since 5.0.0
  45528. * @apioption responsive.rules.condition.maxHeight
  45529. */
  45530. /**
  45531. * The responsive rule applies if the chart width is less than this.
  45532. *
  45533. * @sample highcharts/responsive/axis/
  45534. * Max width is 500
  45535. *
  45536. * @type {number}
  45537. * @since 5.0.0
  45538. * @apioption responsive.rules.condition.maxWidth
  45539. */
  45540. /**
  45541. * The responsive rule applies if the chart height is greater than this.
  45542. *
  45543. * @type {number}
  45544. * @default 0
  45545. * @since 5.0.0
  45546. * @apioption responsive.rules.condition.minHeight
  45547. */
  45548. /**
  45549. * The responsive rule applies if the chart width is greater than this.
  45550. *
  45551. * @type {number}
  45552. * @default 0
  45553. * @since 5.0.0
  45554. * @apioption responsive.rules.condition.minWidth
  45555. */
  45556. (''); // keeps doclets above in JS file
  45557. return Responsive;
  45558. });
  45559. _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) {
  45560. const G = Highcharts;
  45561. // Animation
  45562. G.animate = Animation.animate;
  45563. G.animObject = Animation.animObject;
  45564. G.getDeferredAnimation = Animation.getDeferredAnimation;
  45565. G.setAnimation = Animation.setAnimation;
  45566. G.stop = Animation.stop;
  45567. G.timers = Fx.timers;
  45568. // Classes
  45569. G.AST = AST;
  45570. G.Axis = Axis;
  45571. G.Chart = Chart;
  45572. G.chart = Chart.chart;
  45573. G.Fx = Fx;
  45574. G.Legend = Legend;
  45575. G.PlotLineOrBand = PlotLineOrBand;
  45576. G.Point = Point;
  45577. G.Pointer = Pointer;
  45578. G.Series = Series;
  45579. G.StackItem = StackItem;
  45580. G.SVGElement = SVGElement;
  45581. G.SVGRenderer = SVGRenderer;
  45582. G.Templating = Templating;
  45583. G.Tick = Tick;
  45584. G.Time = Time;
  45585. G.Tooltip = Tooltip;
  45586. // Color
  45587. G.Color = Color;
  45588. G.color = Color.parse;
  45589. // Compositions
  45590. HTMLRenderer.compose(SVGRenderer);
  45591. HTMLElement.compose(SVGElement);
  45592. Pointer.compose(Chart);
  45593. Legend.compose(Chart);
  45594. // DefaultOptions
  45595. G.defaultOptions = Defaults.defaultOptions;
  45596. G.getOptions = Defaults.getOptions;
  45597. G.time = Defaults.defaultTime;
  45598. G.setOptions = Defaults.setOptions;
  45599. // Format Utilities
  45600. G.dateFormat = Templating.dateFormat;
  45601. G.format = Templating.format;
  45602. G.numberFormat = Templating.numberFormat;
  45603. // Utilities
  45604. G.addEvent = Utilities.addEvent;
  45605. G.arrayMax = Utilities.arrayMax;
  45606. G.arrayMin = Utilities.arrayMin;
  45607. G.attr = Utilities.attr;
  45608. G.clearTimeout = Utilities.clearTimeout;
  45609. G.correctFloat = Utilities.correctFloat;
  45610. G.createElement = Utilities.createElement;
  45611. G.css = Utilities.css;
  45612. G.defined = Utilities.defined;
  45613. G.destroyObjectProperties = Utilities.destroyObjectProperties;
  45614. G.discardElement = Utilities.discardElement;
  45615. G.distribute = RendererUtilities.distribute;
  45616. G.erase = Utilities.erase;
  45617. G.error = Utilities.error;
  45618. G.extend = Utilities.extend;
  45619. G.extendClass = Utilities.extendClass;
  45620. G.find = Utilities.find;
  45621. G.fireEvent = Utilities.fireEvent;
  45622. G.getMagnitude = Utilities.getMagnitude;
  45623. G.getStyle = Utilities.getStyle;
  45624. G.inArray = Utilities.inArray;
  45625. G.isArray = Utilities.isArray;
  45626. G.isClass = Utilities.isClass;
  45627. G.isDOMElement = Utilities.isDOMElement;
  45628. G.isFunction = Utilities.isFunction;
  45629. G.isNumber = Utilities.isNumber;
  45630. G.isObject = Utilities.isObject;
  45631. G.isString = Utilities.isString;
  45632. G.keys = Utilities.keys;
  45633. G.merge = Utilities.merge;
  45634. G.normalizeTickInterval = Utilities.normalizeTickInterval;
  45635. G.objectEach = Utilities.objectEach;
  45636. G.offset = Utilities.offset;
  45637. G.pad = Utilities.pad;
  45638. G.pick = Utilities.pick;
  45639. G.pInt = Utilities.pInt;
  45640. G.relativeLength = Utilities.relativeLength;
  45641. G.removeEvent = Utilities.removeEvent;
  45642. G.seriesType = SeriesRegistry.seriesType;
  45643. G.splat = Utilities.splat;
  45644. G.stableSort = Utilities.stableSort;
  45645. G.syncTimeout = Utilities.syncTimeout;
  45646. G.timeUnits = Utilities.timeUnits;
  45647. G.uniqueKey = Utilities.uniqueKey;
  45648. G.useSerialIds = Utilities.useSerialIds;
  45649. G.wrap = Utilities.wrap;
  45650. // Compositions
  45651. ColumnDataLabel.compose(ColumnSeries);
  45652. DataLabel.compose(Series);
  45653. DateTimeAxis.compose(Axis);
  45654. LogarithmicAxis.compose(Axis);
  45655. PieDataLabel.compose(PieSeries);
  45656. PlotLineOrBand.compose(Axis);
  45657. Responsive.compose(Chart);
  45658. StackingAxis.compose(Axis, Chart, Series);
  45659. Tooltip.compose(Pointer);
  45660. // Default Export
  45661. return G;
  45662. });
  45663. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Stacking/StackItem.js'], _modules['Core/Utilities.js']], function (StackItem, U) {
  45664. /* *
  45665. *
  45666. * (c) 2009-2021 Torstein Honsi
  45667. *
  45668. * License: www.highcharts.com/license
  45669. *
  45670. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45671. *
  45672. * */
  45673. const { addEvent, find, fireEvent, isArray, isNumber, pick } = U;
  45674. /* *
  45675. *
  45676. * Composition
  45677. *
  45678. * */
  45679. /**
  45680. * Axis with support of broken data rows.
  45681. * @private
  45682. */
  45683. var BrokenAxis;
  45684. (function (BrokenAxis) {
  45685. /* *
  45686. *
  45687. * Declarations
  45688. *
  45689. * */
  45690. /* *
  45691. *
  45692. * Constants
  45693. *
  45694. * */
  45695. const composedMembers = [];
  45696. /* *
  45697. *
  45698. * Functions
  45699. *
  45700. * */
  45701. /* eslint-disable valid-jsdoc */
  45702. /**
  45703. * Adds support for broken axes.
  45704. * @private
  45705. */
  45706. function compose(AxisClass, SeriesClass) {
  45707. if (U.pushUnique(composedMembers, AxisClass)) {
  45708. AxisClass.keepProps.push('brokenAxis');
  45709. addEvent(AxisClass, 'init', onAxisInit);
  45710. addEvent(AxisClass, 'afterInit', onAxisAfterInit);
  45711. addEvent(AxisClass, 'afterSetTickPositions', onAxisAfterSetTickPositions);
  45712. addEvent(AxisClass, 'afterSetOptions', onAxisAfterSetOptions);
  45713. }
  45714. if (U.pushUnique(composedMembers, SeriesClass)) {
  45715. const seriesProto = SeriesClass.prototype;
  45716. seriesProto.drawBreaks = seriesDrawBreaks;
  45717. seriesProto.gappedPath = seriesGappedPath;
  45718. addEvent(SeriesClass, 'afterGeneratePoints', onSeriesAfterGeneratePoints);
  45719. addEvent(SeriesClass, 'afterRender', onSeriesAfterRender);
  45720. }
  45721. return AxisClass;
  45722. }
  45723. BrokenAxis.compose = compose;
  45724. /**
  45725. * @private
  45726. */
  45727. function onAxisAfterInit() {
  45728. if (typeof this.brokenAxis !== 'undefined') {
  45729. this.brokenAxis.setBreaks(this.options.breaks, false);
  45730. }
  45731. }
  45732. /**
  45733. * Force Axis to be not-ordinal when breaks are defined.
  45734. * @private
  45735. */
  45736. function onAxisAfterSetOptions() {
  45737. const axis = this;
  45738. if (axis.brokenAxis && axis.brokenAxis.hasBreaks) {
  45739. axis.options.ordinal = false;
  45740. }
  45741. }
  45742. /**
  45743. * @private
  45744. */
  45745. function onAxisAfterSetTickPositions() {
  45746. const axis = this, brokenAxis = axis.brokenAxis;
  45747. if (brokenAxis &&
  45748. brokenAxis.hasBreaks) {
  45749. const tickPositions = axis.tickPositions, info = axis.tickPositions.info, newPositions = [];
  45750. for (let i = 0; i < tickPositions.length; i++) {
  45751. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  45752. newPositions.push(tickPositions[i]);
  45753. }
  45754. }
  45755. axis.tickPositions = newPositions;
  45756. axis.tickPositions.info = info;
  45757. }
  45758. }
  45759. /**
  45760. * @private
  45761. */
  45762. function onAxisInit() {
  45763. const axis = this;
  45764. if (!axis.brokenAxis) {
  45765. axis.brokenAxis = new Additions(axis);
  45766. }
  45767. }
  45768. /**
  45769. * @private
  45770. */
  45771. function onSeriesAfterGeneratePoints() {
  45772. const { isDirty, options: { connectNulls }, points, xAxis, yAxis } = this;
  45773. // Set, or reset visibility of the points. Axis.setBreaks marks
  45774. // the series as isDirty
  45775. if (isDirty) {
  45776. let i = points.length;
  45777. while (i--) {
  45778. const point = points[i];
  45779. // Respect nulls inside the break (#4275)
  45780. const nullGap = point.y === null && connectNulls === false;
  45781. const isPointInBreak = (!nullGap && ((xAxis &&
  45782. xAxis.brokenAxis &&
  45783. xAxis.brokenAxis.isInAnyBreak(point.x, true)) || (yAxis &&
  45784. yAxis.brokenAxis &&
  45785. yAxis.brokenAxis.isInAnyBreak(point.y, true))));
  45786. // Set point.visible if in any break.
  45787. // If not in break, reset visible to original value.
  45788. point.visible = isPointInBreak ?
  45789. false :
  45790. point.options.visible !== false;
  45791. }
  45792. }
  45793. }
  45794. /**
  45795. * @private
  45796. */
  45797. function onSeriesAfterRender() {
  45798. this.drawBreaks(this.xAxis, ['x']);
  45799. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  45800. }
  45801. /**
  45802. * @private
  45803. */
  45804. function seriesDrawBreaks(axis, keys) {
  45805. const series = this, points = series.points;
  45806. let breaks, threshold, eventName, y;
  45807. if (axis && // #5950
  45808. axis.brokenAxis &&
  45809. axis.brokenAxis.hasBreaks) {
  45810. const brokenAxis = axis.brokenAxis;
  45811. keys.forEach(function (key) {
  45812. breaks = brokenAxis && brokenAxis.breakArray || [];
  45813. threshold = axis.isXAxis ?
  45814. axis.min :
  45815. pick(series.options.threshold, axis.min);
  45816. points.forEach(function (point) {
  45817. y = pick(point['stack' + key.toUpperCase()], point[key]);
  45818. breaks.forEach(function (brk) {
  45819. if (isNumber(threshold) && isNumber(y)) {
  45820. eventName = false;
  45821. if ((threshold < brk.from && y > brk.to) ||
  45822. (threshold > brk.from && y < brk.from)) {
  45823. eventName = 'pointBreak';
  45824. }
  45825. else if ((threshold < brk.from &&
  45826. y > brk.from &&
  45827. y < brk.to) || (threshold > brk.from &&
  45828. y > brk.to &&
  45829. y < brk.from)) {
  45830. eventName = 'pointInBreak';
  45831. }
  45832. if (eventName) {
  45833. fireEvent(axis, eventName, { point, brk });
  45834. }
  45835. }
  45836. });
  45837. });
  45838. });
  45839. }
  45840. }
  45841. /**
  45842. * Extend getGraphPath by identifying gaps in the data so that we
  45843. * can draw a gap in the line or area. This was moved from ordinal
  45844. * axis module to broken axis module as of #5045.
  45845. *
  45846. * @private
  45847. * @function Highcharts.Series#gappedPath
  45848. *
  45849. * @return {Highcharts.SVGPathArray}
  45850. * Gapped path
  45851. */
  45852. function seriesGappedPath() {
  45853. const currentDataGrouping = this.currentDataGrouping, groupingSize = currentDataGrouping && currentDataGrouping.gapSize, points = this.points.slice(), yAxis = this.yAxis;
  45854. let gapSize = this.options.gapSize, i = points.length - 1, stack;
  45855. /**
  45856. * Defines when to display a gap in the graph, together with the
  45857. * [gapUnit](plotOptions.series.gapUnit) option.
  45858. *
  45859. * In case when `dataGrouping` is enabled, points can be grouped
  45860. * into a larger time span. This can make the grouped points to
  45861. * have a greater distance than the absolute value of `gapSize`
  45862. * property, which will result in disappearing graph completely.
  45863. * To prevent this situation the mentioned distance between
  45864. * grouped points is used instead of previously defined
  45865. * `gapSize`.
  45866. *
  45867. * In practice, this option is most often used to visualize gaps
  45868. * in time series. In a stock chart, intraday data is available
  45869. * for daytime hours, while gaps will appear in nights and
  45870. * weekends.
  45871. *
  45872. * @see [gapUnit](plotOptions.series.gapUnit)
  45873. * @see [xAxis.breaks](#xAxis.breaks)
  45874. *
  45875. * @sample {highstock} stock/plotoptions/series-gapsize/
  45876. * Setting the gap size to 2 introduces gaps for weekends in
  45877. * daily datasets.
  45878. *
  45879. * @type {number}
  45880. * @default 0
  45881. * @product highstock
  45882. * @requires modules/broken-axis
  45883. * @apioption plotOptions.series.gapSize
  45884. */
  45885. /**
  45886. * Together with [gapSize](plotOptions.series.gapSize), this
  45887. * option defines where to draw gaps in the graph.
  45888. *
  45889. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  45890. * means that if the distance between two points is greater than
  45891. * 5 times that of the two closest points, the graph will be
  45892. * broken.
  45893. *
  45894. * When the `gapUnit` is `"value"`, the gap is based on absolute
  45895. * axis values, which on a datetime axis is milliseconds. This
  45896. * also applies to the navigator series that inherits gap
  45897. * options from the base series.
  45898. *
  45899. * @see [gapSize](plotOptions.series.gapSize)
  45900. *
  45901. * @type {string}
  45902. * @default relative
  45903. * @since 5.0.13
  45904. * @product highstock
  45905. * @validvalue ["relative", "value"]
  45906. * @requires modules/broken-axis
  45907. * @apioption plotOptions.series.gapUnit
  45908. */
  45909. if (gapSize && i > 0) { // #5008
  45910. // Gap unit is relative
  45911. if (this.options.gapUnit !== 'value') {
  45912. gapSize *= this.basePointRange;
  45913. }
  45914. // Setting a new gapSize in case dataGrouping is enabled
  45915. // (#7686)
  45916. if (groupingSize &&
  45917. groupingSize > gapSize &&
  45918. // Except when DG is forced (e.g. from other series)
  45919. // and has lower granularity than actual points (#11351)
  45920. groupingSize >= this.basePointRange) {
  45921. gapSize = groupingSize;
  45922. }
  45923. // extension for ordinal breaks
  45924. let current, next;
  45925. while (i--) {
  45926. // Reassign next if it is not visible
  45927. if (!(next && next.visible !== false)) {
  45928. next = points[i + 1];
  45929. }
  45930. current = points[i];
  45931. // Skip iteration if one of the points is not visible
  45932. if (next.visible === false || current.visible === false) {
  45933. continue;
  45934. }
  45935. if (next.x - current.x > gapSize) {
  45936. const xRange = (current.x + next.x) / 2;
  45937. points.splice(// insert after this one
  45938. i + 1, 0, {
  45939. isNull: true,
  45940. x: xRange
  45941. });
  45942. // For stacked chart generate empty stack items, #6546
  45943. if (yAxis.stacking && this.options.stacking) {
  45944. stack = yAxis.stacking.stacks[this.stackKey][xRange] = new StackItem(yAxis, yAxis.options.stackLabels, false, xRange, this.stack);
  45945. stack.total = 0;
  45946. }
  45947. }
  45948. // Assign current to next for the upcoming iteration
  45949. next = current;
  45950. }
  45951. }
  45952. // Call base method
  45953. return this.getGraphPath(points);
  45954. }
  45955. /* *
  45956. *
  45957. * Class
  45958. *
  45959. * */
  45960. /**
  45961. * Provides support for broken axes.
  45962. * @private
  45963. * @class
  45964. */
  45965. class Additions {
  45966. /* *
  45967. *
  45968. * Static Functions
  45969. *
  45970. * */
  45971. /**
  45972. * @private
  45973. */
  45974. static isInBreak(brk, val) {
  45975. const repeat = brk.repeat || Infinity, from = brk.from, length = brk.to - brk.from, test = (val >= from ?
  45976. (val - from) % repeat :
  45977. repeat - ((from - val) % repeat));
  45978. let ret;
  45979. if (!brk.inclusive) {
  45980. ret = test < length && test !== 0;
  45981. }
  45982. else {
  45983. ret = test <= length;
  45984. }
  45985. return ret;
  45986. }
  45987. /**
  45988. * @private
  45989. */
  45990. static lin2Val(val) {
  45991. const axis = this;
  45992. const brokenAxis = axis.brokenAxis;
  45993. const breakArray = brokenAxis && brokenAxis.breakArray;
  45994. if (!breakArray || !isNumber(val)) {
  45995. return val;
  45996. }
  45997. let nval = val, brk, i;
  45998. for (i = 0; i < breakArray.length; i++) {
  45999. brk = breakArray[i];
  46000. if (brk.from >= nval) {
  46001. break;
  46002. }
  46003. else if (brk.to < nval) {
  46004. nval += brk.len;
  46005. }
  46006. else if (Additions.isInBreak(brk, nval)) {
  46007. nval += brk.len;
  46008. }
  46009. }
  46010. return nval;
  46011. }
  46012. /**
  46013. * @private
  46014. */
  46015. static val2Lin(val) {
  46016. const axis = this;
  46017. const brokenAxis = axis.brokenAxis;
  46018. const breakArray = brokenAxis && brokenAxis.breakArray;
  46019. if (!breakArray || !isNumber(val)) {
  46020. return val;
  46021. }
  46022. let nval = val, brk, i;
  46023. for (i = 0; i < breakArray.length; i++) {
  46024. brk = breakArray[i];
  46025. if (brk.to <= val) {
  46026. nval -= brk.len;
  46027. }
  46028. else if (brk.from >= val) {
  46029. break;
  46030. }
  46031. else if (Additions.isInBreak(brk, val)) {
  46032. nval -= (val - brk.from);
  46033. break;
  46034. }
  46035. }
  46036. return nval;
  46037. }
  46038. /* *
  46039. *
  46040. * Constructors
  46041. *
  46042. * */
  46043. constructor(axis) {
  46044. this.hasBreaks = false;
  46045. this.axis = axis;
  46046. }
  46047. /* *
  46048. *
  46049. * Functions
  46050. *
  46051. * */
  46052. /**
  46053. * Returns the first break found where the x is larger then break.from
  46054. * and smaller then break.to.
  46055. *
  46056. * @param {number} x
  46057. * The number which should be within a break.
  46058. *
  46059. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  46060. * The array of breaks to search within.
  46061. *
  46062. * @return {Highcharts.XAxisBreaksOptions|undefined}
  46063. * Returns the first break found that matches, returns false if no break
  46064. * is found.
  46065. */
  46066. findBreakAt(x, breaks) {
  46067. return find(breaks, function (b) {
  46068. return b.from < x && x < b.to;
  46069. });
  46070. }
  46071. /**
  46072. * @private
  46073. */
  46074. isInAnyBreak(val, testKeep) {
  46075. const brokenAxis = this, axis = brokenAxis.axis, breaks = axis.options.breaks || [];
  46076. let i = breaks.length, inbrk, keep, ret;
  46077. if (i && isNumber(val)) {
  46078. while (i--) {
  46079. if (Additions.isInBreak(breaks[i], val)) {
  46080. inbrk = true;
  46081. if (!keep) {
  46082. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  46083. }
  46084. }
  46085. }
  46086. if (inbrk && testKeep) {
  46087. ret = inbrk && !keep;
  46088. }
  46089. else {
  46090. ret = inbrk;
  46091. }
  46092. }
  46093. return ret;
  46094. }
  46095. /**
  46096. * Dynamically set or unset breaks in an axis. This function in lighter
  46097. * than usin Axis.update, and it also preserves animation.
  46098. *
  46099. * @private
  46100. * @function Highcharts.Axis#setBreaks
  46101. *
  46102. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  46103. * The breaks to add. When `undefined` it removes existing breaks.
  46104. *
  46105. * @param {boolean} [redraw=true]
  46106. * Whether to redraw the chart immediately.
  46107. */
  46108. setBreaks(breaks, redraw) {
  46109. const brokenAxis = this;
  46110. const axis = brokenAxis.axis;
  46111. const hasBreaks = (isArray(breaks) && !!breaks.length);
  46112. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  46113. brokenAxis.hasBreaks = hasBreaks;
  46114. if (breaks !== axis.options.breaks) {
  46115. axis.options.breaks = axis.userOptions.breaks = breaks;
  46116. }
  46117. axis.forceRedraw = true; // Force recalculation in setScale
  46118. // Recalculate series related to the axis.
  46119. axis.series.forEach(function (series) {
  46120. series.isDirty = true;
  46121. });
  46122. if (!hasBreaks && axis.val2lin === Additions.val2Lin) {
  46123. // Revert to prototype functions
  46124. delete axis.val2lin;
  46125. delete axis.lin2val;
  46126. }
  46127. if (hasBreaks) {
  46128. axis.userOptions.ordinal = false;
  46129. axis.lin2val = Additions.lin2Val;
  46130. axis.val2lin = Additions.val2Lin;
  46131. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  46132. // If trying to set extremes inside a break, extend min to
  46133. // after, and max to before the break ( #3857 )
  46134. if (brokenAxis.hasBreaks) {
  46135. const breaks = (this.options.breaks || []);
  46136. let axisBreak;
  46137. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  46138. newMin = axisBreak.to;
  46139. }
  46140. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  46141. newMax = axisBreak.from;
  46142. }
  46143. // If both min and max is within the same break.
  46144. if (newMax < newMin) {
  46145. newMax = newMin;
  46146. }
  46147. }
  46148. axis.constructor.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  46149. };
  46150. axis.setAxisTranslation = function () {
  46151. axis.constructor.prototype.setAxisTranslation.call(this);
  46152. brokenAxis.unitLength = void 0;
  46153. if (brokenAxis.hasBreaks) {
  46154. const breaks = axis.options.breaks || [],
  46155. // Temporary one:
  46156. breakArrayT = [], breakArray = [], pointRangePadding = pick(axis.pointRangePadding, 0);
  46157. let length = 0, inBrk, repeat, min = axis.userMin || axis.min, max = axis.userMax || axis.max, start, i;
  46158. // Min & max check (#4247)
  46159. breaks.forEach(function (brk) {
  46160. repeat = brk.repeat || Infinity;
  46161. if (isNumber(min) && isNumber(max)) {
  46162. if (Additions.isInBreak(brk, min)) {
  46163. min += ((brk.to % repeat) -
  46164. (min % repeat));
  46165. }
  46166. if (Additions.isInBreak(brk, max)) {
  46167. max -= ((max % repeat) -
  46168. (brk.from % repeat));
  46169. }
  46170. }
  46171. });
  46172. // Construct an array holding all breaks in the axis
  46173. breaks.forEach(function (brk) {
  46174. start = brk.from;
  46175. repeat = brk.repeat || Infinity;
  46176. if (isNumber(min) && isNumber(max)) {
  46177. while (start - repeat > min) {
  46178. start -= repeat;
  46179. }
  46180. while (start < min) {
  46181. start += repeat;
  46182. }
  46183. for (i = start; i < max; i += repeat) {
  46184. breakArrayT.push({
  46185. value: i,
  46186. move: 'in'
  46187. });
  46188. breakArrayT.push({
  46189. value: i + brk.to - brk.from,
  46190. move: 'out',
  46191. size: brk.breakSize
  46192. });
  46193. }
  46194. }
  46195. });
  46196. breakArrayT.sort(function (a, b) {
  46197. return ((a.value === b.value) ?
  46198. ((a.move === 'in' ? 0 : 1) -
  46199. (b.move === 'in' ? 0 : 1)) :
  46200. a.value - b.value);
  46201. });
  46202. // Simplify the breaks
  46203. inBrk = 0;
  46204. start = min;
  46205. breakArrayT.forEach(function (brk) {
  46206. inBrk += (brk.move === 'in' ? 1 : -1);
  46207. if (inBrk === 1 && brk.move === 'in') {
  46208. start = brk.value;
  46209. }
  46210. if (inBrk === 0 && isNumber(start)) {
  46211. breakArray.push({
  46212. from: start,
  46213. to: brk.value,
  46214. len: brk.value - start - (brk.size || 0)
  46215. });
  46216. length += (brk.value -
  46217. start -
  46218. (brk.size || 0));
  46219. }
  46220. });
  46221. brokenAxis.breakArray = breakArray;
  46222. // Used with staticScale, and below the actual axis
  46223. // length, when breaks are substracted.
  46224. if (isNumber(min) &&
  46225. isNumber(max) &&
  46226. isNumber(axis.min)) {
  46227. brokenAxis.unitLength = max - min - length +
  46228. pointRangePadding;
  46229. fireEvent(axis, 'afterBreaks');
  46230. if (axis.staticScale) {
  46231. axis.transA = axis.staticScale;
  46232. }
  46233. else if (brokenAxis.unitLength) {
  46234. axis.transA *=
  46235. (max - axis.min + pointRangePadding) /
  46236. brokenAxis.unitLength;
  46237. }
  46238. if (pointRangePadding) {
  46239. axis.minPixelPadding =
  46240. axis.transA * (axis.minPointOffset || 0);
  46241. }
  46242. axis.min = min;
  46243. axis.max = max;
  46244. }
  46245. }
  46246. };
  46247. }
  46248. if (pick(redraw, true)) {
  46249. axis.chart.redraw();
  46250. }
  46251. }
  46252. }
  46253. BrokenAxis.Additions = Additions;
  46254. })(BrokenAxis || (BrokenAxis = {}));
  46255. /* *
  46256. *
  46257. * Default Export
  46258. *
  46259. * */
  46260. return BrokenAxis;
  46261. });
  46262. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [_modules['Core/Globals.js'], _modules['Core/Axis/BrokenAxis.js']], function (Highcharts, BrokenAxis) {
  46263. const G = Highcharts;
  46264. // Compositions
  46265. BrokenAxis.compose(G.Axis, G.Series);
  46266. });
  46267. _registerModule(_modules, 'Extensions/DataGrouping/ApproximationRegistry.js', [], function () {
  46268. /* *
  46269. *
  46270. * (c) 2010-2021 Torstein Honsi
  46271. *
  46272. * License: www.highcharts.com/license
  46273. *
  46274. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46275. *
  46276. * */
  46277. /* *
  46278. *
  46279. * Constants
  46280. *
  46281. * */
  46282. /**
  46283. * Define the available approximation types. The data grouping
  46284. * approximations takes an array or numbers as the first parameter. In case
  46285. * of ohlc, four arrays are sent in as four parameters. Each array consists
  46286. * only of numbers. In case null values belong to the group, the property
  46287. * .hasNulls will be set to true on the array.
  46288. *
  46289. * @product highstock
  46290. *
  46291. * @private
  46292. */
  46293. const ApproximationRegistry = {
  46294. // approximations added programmatically
  46295. };
  46296. /* *
  46297. *
  46298. * Default Export
  46299. *
  46300. * */
  46301. return ApproximationRegistry;
  46302. });
  46303. _registerModule(_modules, 'Extensions/DataGrouping/ApproximationDefaults.js', [_modules['Extensions/DataGrouping/ApproximationRegistry.js'], _modules['Core/Utilities.js']], function (ApproximationRegistry, U) {
  46304. /* *
  46305. *
  46306. * (c) 2010-2021 Torstein Honsi
  46307. *
  46308. * License: www.highcharts.com/license
  46309. *
  46310. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46311. *
  46312. * */
  46313. const { arrayMax, arrayMin, correctFloat, extend, isNumber } = U;
  46314. /* *
  46315. *
  46316. * Functions
  46317. *
  46318. * */
  46319. /**
  46320. * @private
  46321. */
  46322. function average(arr) {
  46323. const len = arr.length;
  46324. let ret = sum(arr);
  46325. // If we have a number, return it divided by the length. If not,
  46326. // return null or undefined based on what the sum method finds.
  46327. if (isNumber(ret) && len) {
  46328. ret = correctFloat(ret / len);
  46329. }
  46330. return ret;
  46331. }
  46332. /**
  46333. * The same as average, but for series with multiple values, like area ranges.
  46334. * @private
  46335. */
  46336. function averages() {
  46337. const ret = [];
  46338. [].forEach.call(arguments, function (arr) {
  46339. ret.push(average(arr));
  46340. });
  46341. // Return undefined when first elem. is undefined and let
  46342. // sum method handle null (#7377)
  46343. return typeof ret[0] === 'undefined' ? void 0 : ret;
  46344. }
  46345. /**
  46346. * @private
  46347. */
  46348. function close(arr) {
  46349. return arr.length ?
  46350. arr[arr.length - 1] :
  46351. (arr.hasNulls ? null : void 0);
  46352. }
  46353. /**
  46354. * @private
  46355. */
  46356. function high(arr) {
  46357. return arr.length ?
  46358. arrayMax(arr) :
  46359. (arr.hasNulls ? null : void 0);
  46360. }
  46361. /**
  46362. * HLC, OHLC and range are special cases where a multidimensional array is input
  46363. * and an array is output.
  46364. * @private
  46365. */
  46366. function hlc(high, low, close) {
  46367. high = ApproximationRegistry.high(high);
  46368. low = ApproximationRegistry.low(low);
  46369. close = ApproximationRegistry.close(close);
  46370. if (isNumber(high) ||
  46371. isNumber(low) ||
  46372. isNumber(close)) {
  46373. return [high, low, close];
  46374. }
  46375. }
  46376. /**
  46377. * @private
  46378. */
  46379. function low(arr) {
  46380. return arr.length ?
  46381. arrayMin(arr) :
  46382. (arr.hasNulls ? null : void 0);
  46383. }
  46384. /**
  46385. * @private
  46386. */
  46387. function ohlc(open, high, low, close) {
  46388. open = ApproximationRegistry.open(open);
  46389. high = ApproximationRegistry.high(high);
  46390. low = ApproximationRegistry.low(low);
  46391. close = ApproximationRegistry.close(close);
  46392. if (isNumber(open) ||
  46393. isNumber(high) ||
  46394. isNumber(low) ||
  46395. isNumber(close)) {
  46396. return [open, high, low, close];
  46397. }
  46398. }
  46399. /**
  46400. * @private
  46401. */
  46402. function open(arr) {
  46403. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  46404. }
  46405. /**
  46406. * @private
  46407. */
  46408. function range(low, high) {
  46409. low = ApproximationRegistry.low(low);
  46410. high = ApproximationRegistry.high(high);
  46411. if (isNumber(low) || isNumber(high)) {
  46412. return [low, high];
  46413. }
  46414. if (low === null && high === null) {
  46415. return null;
  46416. }
  46417. // else, return is undefined
  46418. }
  46419. /**
  46420. * @private
  46421. */
  46422. function sum(arr) {
  46423. let len = arr.length, ret;
  46424. // 1. it consists of nulls exclusive
  46425. if (!len && arr.hasNulls) {
  46426. ret = null;
  46427. // 2. it has a length and real values
  46428. }
  46429. else if (len) {
  46430. ret = 0;
  46431. while (len--) {
  46432. ret += arr[len];
  46433. }
  46434. }
  46435. // 3. it has zero length, so just return undefined
  46436. // => doNothing()
  46437. return ret;
  46438. }
  46439. /* *
  46440. *
  46441. * Default Export
  46442. *
  46443. * */
  46444. const ApproximationDefaults = {
  46445. average,
  46446. averages,
  46447. close,
  46448. high,
  46449. hlc,
  46450. low,
  46451. ohlc,
  46452. open,
  46453. range,
  46454. sum
  46455. };
  46456. extend(ApproximationRegistry, ApproximationDefaults);
  46457. return ApproximationDefaults;
  46458. });
  46459. _registerModule(_modules, 'Extensions/DataGrouping/DataGroupingDefaults.js', [], function () {
  46460. /* *
  46461. *
  46462. * (c) 2010-2021 Torstein Honsi
  46463. *
  46464. * License: www.highcharts.com/license
  46465. *
  46466. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46467. *
  46468. * */
  46469. /* *
  46470. *
  46471. * Constants
  46472. *
  46473. * */
  46474. /**
  46475. * Common options
  46476. * @private
  46477. */
  46478. const common = {
  46479. // enabled: null, // (true for stock charts, false for basic),
  46480. // forced: undefined,
  46481. groupPixelWidth: 2,
  46482. // the first one is the point or start value, the second is the start
  46483. // value if we're dealing with range, the third one is the end value if
  46484. // dealing with a range
  46485. dateTimeLabelFormats: {
  46486. millisecond: [
  46487. '%A, %e %b, %H:%M:%S.%L',
  46488. '%A, %e %b, %H:%M:%S.%L',
  46489. '-%H:%M:%S.%L'
  46490. ],
  46491. second: [
  46492. '%A, %e %b, %H:%M:%S',
  46493. '%A, %e %b, %H:%M:%S',
  46494. '-%H:%M:%S'
  46495. ],
  46496. minute: [
  46497. '%A, %e %b, %H:%M',
  46498. '%A, %e %b, %H:%M',
  46499. '-%H:%M'
  46500. ],
  46501. hour: [
  46502. '%A, %e %b, %H:%M',
  46503. '%A, %e %b, %H:%M',
  46504. '-%H:%M'
  46505. ],
  46506. day: [
  46507. '%A, %e %b %Y',
  46508. '%A, %e %b',
  46509. '-%A, %e %b %Y'
  46510. ],
  46511. week: [
  46512. 'Week from %A, %e %b %Y',
  46513. '%A, %e %b',
  46514. '-%A, %e %b %Y'
  46515. ],
  46516. month: [
  46517. '%B %Y',
  46518. '%B',
  46519. '-%B %Y'
  46520. ],
  46521. year: [
  46522. '%Y',
  46523. '%Y',
  46524. '-%Y'
  46525. ]
  46526. }
  46527. // smoothed = false, // enable this for navigator series only
  46528. };
  46529. /**
  46530. * Extends common options
  46531. * @private
  46532. */
  46533. const seriesSpecific = {
  46534. line: {},
  46535. spline: {},
  46536. area: {},
  46537. areaspline: {},
  46538. arearange: {},
  46539. column: {
  46540. groupPixelWidth: 10
  46541. },
  46542. columnrange: {
  46543. groupPixelWidth: 10
  46544. },
  46545. candlestick: {
  46546. groupPixelWidth: 10
  46547. },
  46548. ohlc: {
  46549. groupPixelWidth: 5
  46550. },
  46551. hlc: {
  46552. groupPixelWidth: 5
  46553. // Move to HeikinAshiSeries.ts aftre refactoring data grouping.
  46554. },
  46555. heikinashi: {
  46556. groupPixelWidth: 10
  46557. }
  46558. };
  46559. /**
  46560. * Units are defined in a separate array to allow complete overriding in
  46561. * case of a user option.
  46562. * @private
  46563. */
  46564. const units = [
  46565. [
  46566. 'millisecond',
  46567. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  46568. ], [
  46569. 'second',
  46570. [1, 2, 5, 10, 15, 30]
  46571. ], [
  46572. 'minute',
  46573. [1, 2, 5, 10, 15, 30]
  46574. ], [
  46575. 'hour',
  46576. [1, 2, 3, 4, 6, 8, 12]
  46577. ], [
  46578. 'day',
  46579. [1]
  46580. ], [
  46581. 'week',
  46582. [1]
  46583. ], [
  46584. 'month',
  46585. [1, 3, 6]
  46586. ], [
  46587. 'year',
  46588. null
  46589. ]
  46590. ];
  46591. /* *
  46592. *
  46593. * Default Export
  46594. *
  46595. * */
  46596. const DataGroupingDefaults = {
  46597. common,
  46598. seriesSpecific,
  46599. units
  46600. };
  46601. return DataGroupingDefaults;
  46602. });
  46603. _registerModule(_modules, 'Extensions/DataGrouping/DataGroupingAxisComposition.js', [_modules['Extensions/DataGrouping/DataGroupingDefaults.js'], _modules['Core/Utilities.js']], function (DataGroupingDefaults, U) {
  46604. /* *
  46605. *
  46606. * (c) 2010-2021 Torstein Honsi
  46607. *
  46608. * License: www.highcharts.com/license
  46609. *
  46610. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46611. *
  46612. * */
  46613. const { addEvent, extend, merge, pick } = U;
  46614. /* *
  46615. *
  46616. * Constants
  46617. *
  46618. * */
  46619. const composedMembers = [];
  46620. /* *
  46621. *
  46622. * Variables
  46623. *
  46624. * */
  46625. let AxisConstructor;
  46626. /* *
  46627. *
  46628. * Functions
  46629. *
  46630. * */
  46631. /**
  46632. * Check the groupPixelWidth and apply the grouping if needed.
  46633. * Fired only after processing the data.
  46634. *
  46635. * @product highstock
  46636. *
  46637. * @function Highcharts.Axis#applyGrouping
  46638. */
  46639. function applyGrouping(e) {
  46640. const axis = this, series = axis.series;
  46641. // Reset the groupPixelWidth for all series, #17141.
  46642. series.forEach(function (series) {
  46643. series.groupPixelWidth = void 0; // #2110
  46644. });
  46645. series.forEach(function (series) {
  46646. series.groupPixelWidth = (axis.getGroupPixelWidth &&
  46647. axis.getGroupPixelWidth());
  46648. if (series.groupPixelWidth) {
  46649. series.hasProcessed = true; // #2692
  46650. }
  46651. // Fire independing on series.groupPixelWidth to always set a proper
  46652. // dataGrouping state, (#16238)
  46653. series.applyGrouping(!!e.hasExtremesChanged);
  46654. });
  46655. }
  46656. /**
  46657. * @private
  46658. */
  46659. function compose(AxisClass) {
  46660. AxisConstructor = AxisClass;
  46661. if (U.pushUnique(composedMembers, AxisClass)) {
  46662. addEvent(AxisClass, 'afterSetScale', onAfterSetScale);
  46663. // When all series are processed, calculate the group pixel width and
  46664. // then if this value is different than zero apply groupings.
  46665. addEvent(AxisClass, 'postProcessData', applyGrouping);
  46666. extend(AxisClass.prototype, {
  46667. applyGrouping,
  46668. getGroupPixelWidth,
  46669. setDataGrouping
  46670. });
  46671. }
  46672. }
  46673. /**
  46674. * Get the data grouping pixel width based on the greatest defined individual
  46675. * width of the axis' series, and if whether one of the axes need grouping.
  46676. * @private
  46677. */
  46678. function getGroupPixelWidth() {
  46679. const series = this.series;
  46680. let i = series.length, groupPixelWidth = 0, doGrouping = false, dataLength, dgOptions;
  46681. // If one of the series needs grouping, apply it to all (#1634)
  46682. while (i--) {
  46683. dgOptions = series[i].options.dataGrouping;
  46684. if (dgOptions) { // #2692
  46685. // If multiple series are compared on the same x axis, give them the
  46686. // same group pixel width (#334)
  46687. groupPixelWidth = Math.max(groupPixelWidth,
  46688. // Fallback to commonOptions (#9693)
  46689. pick(dgOptions.groupPixelWidth, DataGroupingDefaults.common.groupPixelWidth));
  46690. dataLength = (series[i].processedXData || series[i].data).length;
  46691. // Execute grouping if the amount of points is greater than the
  46692. // limit defined in groupPixelWidth
  46693. if (series[i].groupPixelWidth ||
  46694. (dataLength >
  46695. (this.chart.plotSizeX / groupPixelWidth)) ||
  46696. (dataLength && dgOptions.forced)) {
  46697. doGrouping = true;
  46698. }
  46699. }
  46700. }
  46701. return doGrouping ? groupPixelWidth : 0;
  46702. }
  46703. /**
  46704. * When resetting the scale reset the hasProccessed flag to avoid taking
  46705. * previous data grouping of neighbour series into accound when determining
  46706. * group pixel width (#2692).
  46707. * @private
  46708. */
  46709. function onAfterSetScale() {
  46710. this.series.forEach(function (series) {
  46711. series.hasProcessed = false;
  46712. });
  46713. }
  46714. /**
  46715. * Highcharts Stock only. Force data grouping on all the axis' series.
  46716. *
  46717. * @product highstock
  46718. *
  46719. * @function Highcharts.Axis#setDataGrouping
  46720. *
  46721. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  46722. * A `dataGrouping` configuration. Use `false` to disable data grouping
  46723. * dynamically.
  46724. *
  46725. * @param {boolean} [redraw=true]
  46726. * Whether to redraw the chart or wait for a later call to
  46727. * {@link Chart#redraw}.
  46728. */
  46729. function setDataGrouping(dataGrouping, redraw) {
  46730. const axis = this;
  46731. let i;
  46732. redraw = pick(redraw, true);
  46733. if (!dataGrouping) {
  46734. dataGrouping = {
  46735. forced: false,
  46736. units: null
  46737. };
  46738. }
  46739. // Axis is instantiated, update all series
  46740. if (this instanceof AxisConstructor) {
  46741. i = this.series.length;
  46742. while (i--) {
  46743. this.series[i].update({
  46744. dataGrouping: dataGrouping
  46745. }, false);
  46746. }
  46747. // Axis not yet instanciated, alter series options
  46748. }
  46749. else {
  46750. this.chart.options.series.forEach(function (seriesOptions) {
  46751. // Merging dataGrouping options with already defined options #16759
  46752. seriesOptions.dataGrouping = typeof dataGrouping === 'boolean' ?
  46753. dataGrouping :
  46754. merge(dataGrouping, seriesOptions.dataGrouping);
  46755. });
  46756. }
  46757. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  46758. if (axis.ordinal) {
  46759. axis.ordinal.slope = void 0;
  46760. }
  46761. if (redraw) {
  46762. this.chart.redraw();
  46763. }
  46764. }
  46765. /* *
  46766. *
  46767. * Default Export
  46768. *
  46769. * */
  46770. const DataGroupingAxisComposition = {
  46771. compose
  46772. };
  46773. return DataGroupingAxisComposition;
  46774. });
  46775. _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) {
  46776. /* *
  46777. *
  46778. * (c) 2010-2021 Torstein Honsi
  46779. *
  46780. * License: www.highcharts.com/license
  46781. *
  46782. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46783. *
  46784. * */
  46785. const { series: { prototype: seriesProto } } = SeriesRegistry;
  46786. const { addEvent, defined, error, extend, isNumber, merge, pick } = U;
  46787. /* *
  46788. *
  46789. * Constants
  46790. *
  46791. * */
  46792. const baseGeneratePoints = seriesProto.generatePoints;
  46793. const composedMembers = [];
  46794. /* *
  46795. *
  46796. * Functions
  46797. *
  46798. * */
  46799. /**
  46800. * @private
  46801. */
  46802. function adjustExtremes(xAxis, groupedXData) {
  46803. // Make sure the X axis extends to show the first group (#2533)
  46804. // But only for visible series (#5493, #6393)
  46805. if (defined(groupedXData[0]) &&
  46806. isNumber(xAxis.min) &&
  46807. isNumber(xAxis.dataMin) &&
  46808. groupedXData[0] < xAxis.min) {
  46809. if ((!defined(xAxis.options.min) &&
  46810. xAxis.min <= xAxis.dataMin) ||
  46811. xAxis.min === xAxis.dataMin) {
  46812. xAxis.min = Math.min(groupedXData[0], xAxis.min);
  46813. }
  46814. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  46815. }
  46816. // When the last anchor set, change the extremes that
  46817. // the last point is visible (#12455).
  46818. if (defined(groupedXData[groupedXData.length - 1]) &&
  46819. isNumber(xAxis.max) &&
  46820. isNumber(xAxis.dataMax) &&
  46821. groupedXData[groupedXData.length - 1] > xAxis.max) {
  46822. if ((!defined(xAxis.options.max) &&
  46823. isNumber(xAxis.dataMax) &&
  46824. xAxis.max >= xAxis.dataMax) || xAxis.max === xAxis.dataMax) {
  46825. xAxis.max = Math.max(groupedXData[groupedXData.length - 1], xAxis.max);
  46826. }
  46827. xAxis.dataMax = Math.max(groupedXData[groupedXData.length - 1], xAxis.dataMax);
  46828. }
  46829. }
  46830. /**
  46831. * @private
  46832. */
  46833. function anchorPoints(series, groupedXData, xMax) {
  46834. const options = series.options, dataGroupingOptions = options.dataGrouping, totalRange = (series.currentDataGrouping && series.currentDataGrouping.gapSize);
  46835. let i;
  46836. // DataGrouping x-coordinates.
  46837. if (dataGroupingOptions && series.xData && totalRange && series.groupMap) {
  46838. const groupedDataLength = groupedXData.length - 1, anchor = dataGroupingOptions.anchor, firstAnchor = pick(dataGroupingOptions.firstAnchor, anchor), lastAnchor = pick(dataGroupingOptions.lastAnchor, anchor);
  46839. // Anchor points that are not extremes.
  46840. if (anchor && anchor !== 'start') {
  46841. const shiftInterval = (totalRange *
  46842. { middle: 0.5, end: 1 }[anchor]);
  46843. i = groupedXData.length - 1;
  46844. while (i-- && i > 0) {
  46845. groupedXData[i] += shiftInterval;
  46846. }
  46847. }
  46848. // Change the first point position, but only when it is
  46849. // the first point in the data set not in the current zoom.
  46850. if (firstAnchor &&
  46851. firstAnchor !== 'start' &&
  46852. series.xData[0] >= groupedXData[0]) {
  46853. const groupStart = series.groupMap[0].start, groupLength = series.groupMap[0].length;
  46854. let firstGroupstEnd;
  46855. if (isNumber(groupStart) && isNumber(groupLength)) {
  46856. firstGroupstEnd = groupStart + (groupLength - 1);
  46857. }
  46858. groupedXData[0] = {
  46859. middle: groupedXData[0] + 0.5 * totalRange,
  46860. end: groupedXData[0] + totalRange,
  46861. firstPoint: series.xData[0],
  46862. lastPoint: firstGroupstEnd && series.xData[firstGroupstEnd]
  46863. }[firstAnchor];
  46864. }
  46865. // Change the last point position but only when it is
  46866. // the last point in the data set not in the current zoom.
  46867. if (lastAnchor &&
  46868. lastAnchor !== 'start' &&
  46869. totalRange &&
  46870. groupedXData[groupedDataLength] >= xMax - totalRange) {
  46871. const lastGroupStart = series.groupMap[series.groupMap.length - 1].start;
  46872. groupedXData[groupedDataLength] = {
  46873. middle: groupedXData[groupedDataLength] + 0.5 * totalRange,
  46874. end: groupedXData[groupedDataLength] + totalRange,
  46875. firstPoint: lastGroupStart && series.xData[lastGroupStart],
  46876. lastPoint: series.xData[series.xData.length - 1]
  46877. }[lastAnchor];
  46878. }
  46879. }
  46880. }
  46881. /**
  46882. * For the processed data, calculate the grouped data if needed.
  46883. *
  46884. * @private
  46885. * @function Highcharts.Series#applyGrouping
  46886. */
  46887. function applyGrouping(hasExtremesChanged) {
  46888. const series = this, chart = series.chart, options = series.options, dataGroupingOptions = options.dataGrouping, groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  46889. pick(dataGroupingOptions.enabled, chart.options.isStock), visible = (series.visible || !chart.options.chart.ignoreHiddenSeries), lastDataGrouping = this.currentDataGrouping;
  46890. let currentDataGrouping, croppedData, revertRequireSorting = false;
  46891. // Data needs to be sorted for dataGrouping
  46892. if (groupingEnabled && !series.requireSorting) {
  46893. series.requireSorting = revertRequireSorting = true;
  46894. }
  46895. // Skip if skipDataGrouping method returns false or if grouping is disabled
  46896. // (in that order).
  46897. const skip = skipDataGrouping(series, hasExtremesChanged) === false || !groupingEnabled;
  46898. // Revert original requireSorting value if changed
  46899. if (revertRequireSorting) {
  46900. series.requireSorting = false;
  46901. }
  46902. if (!skip) {
  46903. series.destroyGroupedData();
  46904. const processedXData = dataGroupingOptions.groupAll ?
  46905. series.xData :
  46906. series.processedXData, processedYData = dataGroupingOptions.groupAll ?
  46907. series.yData :
  46908. series.processedYData, plotSizeX = chart.plotSizeX, xAxis = series.xAxis, ordinal = xAxis.options.ordinal, groupPixelWidth = series.groupPixelWidth;
  46909. let i, hasGroupedData;
  46910. // Execute grouping if the amount of points is greater than the limit
  46911. // defined in groupPixelWidth
  46912. if (groupPixelWidth &&
  46913. processedXData &&
  46914. processedXData.length &&
  46915. plotSizeX) {
  46916. hasGroupedData = true;
  46917. // Force recreation of point instances in series.translate, #5699
  46918. series.isDirty = true;
  46919. series.points = null; // #6709
  46920. const extremes = xAxis.getExtremes(), xMin = extremes.min, xMax = extremes.max, groupIntervalFactor = (ordinal &&
  46921. xAxis.ordinal &&
  46922. xAxis.ordinal.getGroupIntervalFactor(xMin, xMax, series)) || 1, interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  46923. groupIntervalFactor, groupPositions = xAxis.getTimeTicks(DateTimeAxis.Additions.prototype.normalizeTimeTickInterval(interval, dataGroupingOptions.units ||
  46924. DataGroupingDefaults.units),
  46925. // Processed data may extend beyond axis (#4907)
  46926. Math.min(xMin, processedXData[0]), Math.max(xMax, processedXData[processedXData.length - 1]), xAxis.options.startOfWeek, processedXData, series.closestPointRange), groupedData = seriesProto.groupData.apply(series, [
  46927. processedXData,
  46928. processedYData,
  46929. groupPositions,
  46930. dataGroupingOptions.approximation
  46931. ]);
  46932. let groupedXData = groupedData.groupedXData, groupedYData = groupedData.groupedYData, gapSize = 0;
  46933. // The smoothed option is deprecated, instead, there is a fallback
  46934. // to the new anchoring mechanism. #12455.
  46935. if (dataGroupingOptions &&
  46936. dataGroupingOptions.smoothed &&
  46937. groupedXData.length) {
  46938. dataGroupingOptions.firstAnchor = 'firstPoint';
  46939. dataGroupingOptions.anchor = 'middle';
  46940. dataGroupingOptions.lastAnchor = 'lastPoint';
  46941. error(32, false, chart, {
  46942. 'dataGrouping.smoothed': 'use dataGrouping.anchor'
  46943. });
  46944. }
  46945. anchorPoints(series, groupedXData, xMax);
  46946. // Record what data grouping values were used
  46947. for (i = 1; i < groupPositions.length; i++) {
  46948. // The grouped gapSize needs to be the largest distance between
  46949. // the group to capture varying group sizes like months or DST
  46950. // crossing (#10000). Also check that the gap is not at the
  46951. // start of a segment.
  46952. if (!groupPositions.info.segmentStarts ||
  46953. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  46954. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  46955. }
  46956. }
  46957. currentDataGrouping = groupPositions.info;
  46958. currentDataGrouping.gapSize = gapSize;
  46959. series.closestPointRange = groupPositions.info.totalRange;
  46960. series.groupMap = groupedData.groupMap;
  46961. if (visible) {
  46962. adjustExtremes(xAxis, groupedXData);
  46963. }
  46964. // We calculated all group positions but we should render
  46965. // only the ones within the visible range
  46966. if (dataGroupingOptions.groupAll) {
  46967. // Keep the reference to all grouped points
  46968. // for further calculation (eg. heikinashi).
  46969. series.allGroupedData = groupedYData;
  46970. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  46971. );
  46972. groupedXData = croppedData.xData;
  46973. groupedYData = croppedData.yData;
  46974. series.cropStart = croppedData.start; // #15005
  46975. }
  46976. // Set series props
  46977. series.processedXData = groupedXData;
  46978. series.processedYData = groupedYData;
  46979. }
  46980. else {
  46981. series.groupMap = null;
  46982. }
  46983. series.hasGroupedData = hasGroupedData;
  46984. series.currentDataGrouping = currentDataGrouping;
  46985. series.preventGraphAnimation =
  46986. (lastDataGrouping && lastDataGrouping.totalRange) !==
  46987. (currentDataGrouping && currentDataGrouping.totalRange);
  46988. }
  46989. }
  46990. /**
  46991. * @private
  46992. */
  46993. function compose(SeriesClass) {
  46994. const PointClass = SeriesClass.prototype.pointClass;
  46995. if (U.pushUnique(composedMembers, PointClass)) {
  46996. // Override point prototype to throw a warning when trying to update
  46997. // grouped points.
  46998. addEvent(PointClass, 'update', function () {
  46999. if (this.dataGroup) {
  47000. error(24, false, this.series.chart);
  47001. return false;
  47002. }
  47003. });
  47004. }
  47005. if (U.pushUnique(composedMembers, SeriesClass)) {
  47006. addEvent(SeriesClass, 'afterSetOptions', onAfterSetOptions);
  47007. addEvent(SeriesClass, 'destroy', destroyGroupedData);
  47008. extend(SeriesClass.prototype, {
  47009. applyGrouping,
  47010. destroyGroupedData,
  47011. generatePoints,
  47012. getDGApproximation,
  47013. groupData
  47014. });
  47015. }
  47016. }
  47017. /**
  47018. * Destroy the grouped data points. #622, #740
  47019. * @private
  47020. */
  47021. function destroyGroupedData() {
  47022. // Clear previous groups
  47023. if (this.groupedData) {
  47024. this.groupedData.forEach(function (point, i) {
  47025. if (point) {
  47026. this.groupedData[i] = point.destroy ?
  47027. point.destroy() : null;
  47028. }
  47029. }, this);
  47030. // Clears all:
  47031. // - `this.groupedData`
  47032. // - `this.points`
  47033. // - `preserve` object in series.update()
  47034. this.groupedData.length = 0;
  47035. }
  47036. }
  47037. /**
  47038. * Override the generatePoints method by adding a reference to grouped data
  47039. * @private
  47040. */
  47041. function generatePoints() {
  47042. baseGeneratePoints.apply(this);
  47043. // Record grouped data in order to let it be destroyed the next time
  47044. // processData runs
  47045. this.destroyGroupedData(); // #622
  47046. this.groupedData = this.hasGroupedData ? this.points : null;
  47047. }
  47048. /**
  47049. * Set default approximations to the prototypes if present. Properties are
  47050. * inherited down. Can be overridden for individual series types.
  47051. * @private
  47052. */
  47053. function getDGApproximation() {
  47054. if (this.is('arearange')) {
  47055. return 'range';
  47056. }
  47057. if (this.is('ohlc')) {
  47058. return 'ohlc';
  47059. }
  47060. if (this.is('hlc')) {
  47061. return 'hlc';
  47062. }
  47063. if (
  47064. // #18974, default approximation for cumulative
  47065. // should be `sum` when `dataGrouping` is enabled
  47066. this.is('column') ||
  47067. this.options.cumulative) {
  47068. return 'sum';
  47069. }
  47070. return 'average';
  47071. }
  47072. /**
  47073. * Highcharts Stock only. Takes parallel arrays of x and y data and groups the
  47074. * data into intervals defined by groupPositions, a collection of starting x
  47075. * values for each group.
  47076. *
  47077. * @product highstock
  47078. *
  47079. * @function Highcharts.Series#groupData
  47080. * @param {Array<number>} xData
  47081. * Parallel array of x data.
  47082. * @param {Array<(number|null|undefined)>|Array<Array<(number|null|undefined)>>} yData
  47083. * Parallel array of y data.
  47084. * @param {Array<number>} groupPositions
  47085. * Group positions.
  47086. * @param {string|Function} [approximation]
  47087. * Approximation to use.
  47088. * @return {Highcharts.DataGroupingResultObject}
  47089. * Mapped groups.
  47090. */
  47091. function groupData(xData, yData, groupPositions, approximation) {
  47092. const series = this, data = series.data, dataOptions = series.options && series.options.data, groupedXData = [], groupedYData = [], groupMap = [], dataLength = xData.length,
  47093. // when grouping the fake extended axis for panning,
  47094. // we don't need to consider y
  47095. handleYData = !!yData, values = [], pointArrayMap = series.pointArrayMap, pointArrayMapLength = pointArrayMap && pointArrayMap.length, extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']), groupAll = (this.options.dataGrouping &&
  47096. this.options.dataGrouping.groupAll);
  47097. let pointX, pointY, groupedY, pos = 0, start = 0;
  47098. const approximationFn = (typeof approximation === 'function' ?
  47099. approximation :
  47100. approximation && ApproximationRegistry[approximation] ?
  47101. ApproximationRegistry[approximation] :
  47102. ApproximationRegistry[(series.getDGApproximation && series.getDGApproximation() ||
  47103. 'average')]);
  47104. // Calculate values array size from pointArrayMap length
  47105. if (pointArrayMapLength) {
  47106. let len = pointArrayMap.length;
  47107. while (len--) {
  47108. values.push([]);
  47109. }
  47110. }
  47111. else {
  47112. values.push([]);
  47113. }
  47114. const valuesLen = pointArrayMapLength || 1;
  47115. for (let i = 0; i <= dataLength; i++) {
  47116. // Start with the first point within the X axis range (#2696)
  47117. if (xData[i] < groupPositions[0]) {
  47118. continue; // with next point
  47119. }
  47120. // when a new group is entered, summarize and initialize
  47121. // the previous group
  47122. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  47123. xData[i] >= groupPositions[pos + 1]) ||
  47124. i === dataLength) { // get the last group
  47125. // get group x and y
  47126. pointX = groupPositions[pos];
  47127. series.dataGroupInfo = {
  47128. start: groupAll ? start : (series.cropStart + start),
  47129. length: values[0].length
  47130. };
  47131. groupedY = approximationFn.apply(series, values);
  47132. // By default, let options of the first grouped point be passed over
  47133. // to the grouped point. This allows preserving properties like
  47134. // `name` and `color` or custom properties. Implementers can
  47135. // override this from the approximation function, where they can
  47136. // write custom options to `this.dataGroupInfo.options`.
  47137. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  47138. // Convert numbers and arrays into objects
  47139. series.dataGroupInfo.options = merge(series.pointClass.prototype
  47140. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  47141. // Make sure the raw data (x, y, open, high etc) is not copied
  47142. // over and overwriting approximated data.
  47143. extendedPointArrayMap.forEach(function (key) {
  47144. delete series.dataGroupInfo.options[key];
  47145. });
  47146. }
  47147. // push the grouped data
  47148. if (typeof groupedY !== 'undefined') {
  47149. groupedXData.push(pointX);
  47150. groupedYData.push(groupedY);
  47151. groupMap.push(series.dataGroupInfo);
  47152. }
  47153. // reset the aggregate arrays
  47154. start = i;
  47155. for (let j = 0; j < valuesLen; j++) {
  47156. values[j].length = 0; // faster than values[j] = []
  47157. values[j].hasNulls = false;
  47158. }
  47159. // Advance on the group positions
  47160. pos += 1;
  47161. // don't loop beyond the last group
  47162. if (i === dataLength) {
  47163. break;
  47164. }
  47165. }
  47166. // break out
  47167. if (i === dataLength) {
  47168. break;
  47169. }
  47170. // for each raw data point, push it to an array that contains all values
  47171. // for this specific group
  47172. if (pointArrayMap) {
  47173. const index = (series.options.dataGrouping &&
  47174. series.options.dataGrouping.groupAll ?
  47175. i : series.cropStart + i), point = (data && data[index]) ||
  47176. series.pointClass.prototype.applyOptions.apply({
  47177. series: series
  47178. }, [dataOptions[index]]);
  47179. let val;
  47180. for (let j = 0; j < pointArrayMapLength; j++) {
  47181. val = point[pointArrayMap[j]];
  47182. if (isNumber(val)) {
  47183. values[j].push(val);
  47184. }
  47185. else if (val === null) {
  47186. values[j].hasNulls = true;
  47187. }
  47188. }
  47189. }
  47190. else {
  47191. pointY = handleYData ? yData[i] : null;
  47192. if (isNumber(pointY)) {
  47193. values[0].push(pointY);
  47194. }
  47195. else if (pointY === null) {
  47196. values[0].hasNulls = true;
  47197. }
  47198. }
  47199. }
  47200. return {
  47201. groupedXData,
  47202. groupedYData,
  47203. groupMap
  47204. };
  47205. }
  47206. /**
  47207. * Handle default options for data grouping. This must be set at runtime because
  47208. * some series types are defined after this.
  47209. * @private
  47210. */
  47211. function onAfterSetOptions(e) {
  47212. const options = e.options, type = this.type, plotOptions = this.chart.options.plotOptions,
  47213. // External series, for example technical indicators should also inherit
  47214. // commonOptions which are not available outside this module
  47215. baseOptions = (this.useCommonDataGrouping &&
  47216. DataGroupingDefaults.common), seriesSpecific = DataGroupingDefaults.seriesSpecific;
  47217. let defaultOptions = D.defaultOptions.plotOptions[type].dataGrouping;
  47218. if (plotOptions && (seriesSpecific[type] || baseOptions)) { // #1284
  47219. const rangeSelector = this.chart.rangeSelector;
  47220. if (!defaultOptions) {
  47221. defaultOptions = merge(DataGroupingDefaults.common, seriesSpecific[type]);
  47222. }
  47223. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  47224. // Set by the StockChart constructor:
  47225. plotOptions[type].dataGrouping, this.userOptions.dataGrouping, !options.isInternal &&
  47226. rangeSelector &&
  47227. isNumber(rangeSelector.selected) &&
  47228. rangeSelector.buttonOptions[rangeSelector.selected].dataGrouping);
  47229. }
  47230. }
  47231. /**
  47232. * @private
  47233. */
  47234. function skipDataGrouping(series, force) {
  47235. return !(series.isCartesian &&
  47236. !series.isDirty &&
  47237. !series.xAxis.isDirty &&
  47238. !series.yAxis.isDirty &&
  47239. !force);
  47240. }
  47241. /* *
  47242. *
  47243. * Default Export
  47244. *
  47245. * */
  47246. const DataGroupingSeriesComposition = {
  47247. compose,
  47248. groupData
  47249. };
  47250. return DataGroupingSeriesComposition;
  47251. });
  47252. _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) {
  47253. /* *
  47254. *
  47255. * (c) 2010-2021 Torstein Honsi
  47256. *
  47257. * License: www.highcharts.com/license
  47258. *
  47259. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47260. *
  47261. * */
  47262. const { format } = F;
  47263. const { addEvent, extend, isNumber } = U;
  47264. /* *
  47265. *
  47266. * Constants
  47267. *
  47268. * */
  47269. const composedMembers = [];
  47270. /* *
  47271. *
  47272. * Functions
  47273. *
  47274. * */
  47275. /**
  47276. * @private
  47277. */
  47278. function compose(AxisClass, SeriesClass, TooltipClass) {
  47279. DataGroupingAxisComposition.compose(AxisClass);
  47280. DataGroupingSeriesComposition.compose(SeriesClass);
  47281. if (TooltipClass &&
  47282. U.pushUnique(composedMembers, TooltipClass)) {
  47283. addEvent(TooltipClass, 'headerFormatter', onTooltipHeaderFormatter);
  47284. }
  47285. }
  47286. /**
  47287. * Extend the original method, make the tooltip's header reflect the grouped
  47288. * range.
  47289. * @private
  47290. */
  47291. function onTooltipHeaderFormatter(e) {
  47292. 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;
  47293. let xDateFormat = tooltipOptions.xDateFormat, xDateFormatEnd, currentDataGrouping, dateTimeLabelFormats, labelFormats, formattedKey, formatString = tooltipOptions[e.isFooter ? 'footerFormat' : 'headerFormat'];
  47294. // apply only to grouped series
  47295. if (xAxis &&
  47296. xAxis.options.type === 'datetime' &&
  47297. dataGroupingOptions &&
  47298. isNumber(labelConfig.key)) {
  47299. // set variables
  47300. currentDataGrouping = series.currentDataGrouping;
  47301. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  47302. // Fallback to commonOptions (#9693)
  47303. DataGroupingDefaults.common.dateTimeLabelFormats;
  47304. // if we have grouped data, use the grouping information to get the
  47305. // right format
  47306. if (currentDataGrouping) {
  47307. labelFormats =
  47308. dateTimeLabelFormats[currentDataGrouping.unitName];
  47309. if (currentDataGrouping.count === 1) {
  47310. xDateFormat = labelFormats[0];
  47311. }
  47312. else {
  47313. xDateFormat = labelFormats[1];
  47314. xDateFormatEnd = labelFormats[2];
  47315. }
  47316. // if not grouped, and we don't have set the xDateFormat option, get the
  47317. // best fit, so if the least distance between points is one minute, show
  47318. // it, but if the least distance is one day, skip hours and minutes etc.
  47319. }
  47320. else if (!xDateFormat && dateTimeLabelFormats && xAxis.dateTime) {
  47321. xDateFormat = xAxis.dateTime.getXDateFormat(labelConfig.x, tooltipOptions.dateTimeLabelFormats);
  47322. }
  47323. // now format the key
  47324. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  47325. if (xDateFormatEnd) {
  47326. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  47327. }
  47328. // Replace default header style with class name
  47329. if (series.chart.styledMode) {
  47330. formatString = this.styledModeFormat(formatString);
  47331. }
  47332. // return the replaced format
  47333. e.text = format(formatString, {
  47334. point: extend(labelConfig.point, { key: formattedKey }),
  47335. series: series
  47336. }, chart);
  47337. e.preventDefault();
  47338. }
  47339. }
  47340. /* *
  47341. *
  47342. * Default Export
  47343. *
  47344. * */
  47345. const DataGroupingComposition = {
  47346. compose,
  47347. groupData: DataGroupingSeriesComposition.groupData
  47348. };
  47349. /* *
  47350. *
  47351. * API Declarations
  47352. *
  47353. * */
  47354. /**
  47355. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  47356. */
  47357. /**
  47358. * The position of the point inside the group.
  47359. *
  47360. * @typedef {"start"|"middle"|"end"} Highcharts.DataGroupingAnchor
  47361. */
  47362. /**
  47363. * The position of the first or last point in the series inside the group.
  47364. *
  47365. * @typedef {"start"|"middle"|"end"|"firstPoint"|"lastPoint"} Highcharts.DataGroupingAnchorExtremes
  47366. */
  47367. /**
  47368. * Highcharts Stock only.
  47369. *
  47370. * @product highstock
  47371. * @interface Highcharts.DataGroupingInfoObject
  47372. */ /**
  47373. * @name Highcharts.DataGroupingInfoObject#length
  47374. * @type {number}
  47375. */ /**
  47376. * @name Highcharts.DataGroupingInfoObject#options
  47377. * @type {Highcharts.SeriesOptionsType|undefined}
  47378. */ /**
  47379. * @name Highcharts.DataGroupingInfoObject#start
  47380. * @type {number}
  47381. */
  47382. /**
  47383. * Highcharts Stock only.
  47384. *
  47385. * @product highstock
  47386. * @interface Highcharts.DataGroupingResultObject
  47387. */ /**
  47388. * @name Highcharts.DataGroupingResultObject#groupedXData
  47389. * @type {Array<number>}
  47390. */ /**
  47391. * @name Highcharts.DataGroupingResultObject#groupedYData
  47392. * @type {Array<(number|null|undefined)>|Array<Array<(number|null|undefined)>>}
  47393. */ /**
  47394. * @name Highcharts.DataGroupingResultObject#groupMap
  47395. * @type {Array<DataGroupingInfoObject>}
  47396. */
  47397. /**
  47398. * Highcharts Stock only. If a point object is created by data
  47399. * grouping, it doesn't reflect actual points in the raw
  47400. * data. In this case, the `dataGroup` property holds
  47401. * information that points back to the raw data.
  47402. *
  47403. * - `dataGroup.start` is the index of the first raw data
  47404. * point in the group.
  47405. *
  47406. * - `dataGroup.length` is the amount of points in the
  47407. * group.
  47408. *
  47409. * @sample stock/members/point-datagroup
  47410. * Click to inspect raw data points
  47411. *
  47412. * @product highstock
  47413. *
  47414. * @name Highcharts.Point#dataGroup
  47415. * @type {Highcharts.DataGroupingInfoObject|undefined}
  47416. */
  47417. (''); // detach doclets above
  47418. /* *
  47419. *
  47420. * API Options
  47421. *
  47422. * */
  47423. /**
  47424. * Data grouping is the concept of sampling the data values into larger
  47425. * blocks in order to ease readability and increase performance of the
  47426. * JavaScript charts. Highcharts Stock by default applies data grouping when
  47427. * the points become closer than a certain pixel value, determined by
  47428. * the `groupPixelWidth` option.
  47429. *
  47430. * If data grouping is applied, the grouping information of grouped
  47431. * points can be read from the [Point.dataGroup](
  47432. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  47433. * the data itself are set, for example `name` or `color` or custom properties,
  47434. * the grouping logic doesn't know how to group it. In this case the options of
  47435. * the first point instance are copied over to the group point. This can be
  47436. * altered through a custom `approximation` callback function.
  47437. *
  47438. * @declare Highcharts.DataGroupingOptionsObject
  47439. * @product highstock
  47440. * @requires product:highstock
  47441. * @requires module:modules/datagrouping
  47442. * @apioption plotOptions.series.dataGrouping
  47443. */
  47444. /**
  47445. * Specifies how the points should be located on the X axis inside the group.
  47446. * Points that are extremes can be set separately. Available options:
  47447. *
  47448. * - `start` places the point at the beginning of the group
  47449. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  47450. *
  47451. * - `middle` places the point in the middle of the group
  47452. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  47453. *
  47454. * - `end` places the point at the end of the group
  47455. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  47456. *
  47457. * @sample {highstock} stock/plotoptions/series-datagrouping-anchor
  47458. * Changing the point x-coordinate inside the group.
  47459. *
  47460. * @see [dataGrouping.firstAnchor](#plotOptions.series.dataGrouping.firstAnchor)
  47461. * @see [dataGrouping.lastAnchor](#plotOptions.series.dataGrouping.lastAnchor)
  47462. *
  47463. * @type {Highcharts.DataGroupingAnchor}
  47464. * @since 9.1.0
  47465. * @default start
  47466. * @apioption plotOptions.series.dataGrouping.anchor
  47467. */
  47468. /**
  47469. * The method of approximation inside a group. When for example 30 days
  47470. * are grouped into one month, this determines what value should represent
  47471. * the group. Possible values are "average", "averages", "open", "high",
  47472. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  47473. * is "ohlc" by default, which finds the open, high, low and close values
  47474. * within all the grouped data. For ranges, the approximation is "range",
  47475. * which finds the low and high values. For multi-dimensional data,
  47476. * like ranges and OHLC, "averages" will compute the average for each
  47477. * dimension.
  47478. *
  47479. * Custom aggregate methods can be added by assigning a callback function
  47480. * as the approximation. This function takes a numeric array as the
  47481. * argument and should return a single numeric value or `null`. Note
  47482. * that the numeric array will never contain null values, only true
  47483. * numbers. Instead, if null values are present in the raw data, the
  47484. * numeric array will have an `.hasNulls` property set to `true`. For
  47485. * single-value data sets the data is available in the first argument
  47486. * of the callback function. For OHLC data sets, all the open values
  47487. * are in the first argument, all high values in the second etc.
  47488. *
  47489. * Since v4.2.7, grouping meta data is available in the approximation
  47490. * callback from `this.dataGroupInfo`. It can be used to extract information
  47491. * from the raw data.
  47492. *
  47493. * Defaults to `average` for line-type series, `sum` for columns, `range`
  47494. * for range series, `hlc` for HLC, and `ohlc` for OHLC and candlestick.
  47495. *
  47496. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  47497. * Approximation callback with custom data
  47498. * @sample {highstock} stock/plotoptions/series-datagrouping-simple-approximation
  47499. * Simple approximation demo
  47500. *
  47501. * @type {Highcharts.DataGroupingApproximationValue|Function}
  47502. * @apioption plotOptions.series.dataGrouping.approximation
  47503. */
  47504. /**
  47505. * Datetime formats for the header of the tooltip in a stock chart.
  47506. * The format can vary within a chart depending on the currently selected
  47507. * time range and the current data grouping.
  47508. *
  47509. * The default formats are:
  47510. * ```js
  47511. * {
  47512. * millisecond: [
  47513. * '%A, %e %b, %H:%M:%S.%L', '%A, %e %b, %H:%M:%S.%L', '-%H:%M:%S.%L'
  47514. * ],
  47515. * second: ['%A, %e %b, %H:%M:%S', '%A, %e %b, %H:%M:%S', '-%H:%M:%S'],
  47516. * minute: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
  47517. * hour: ['%A, %e %b, %H:%M', '%A, %e %b, %H:%M', '-%H:%M'],
  47518. * day: ['%A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
  47519. * week: ['Week from %A, %e %b %Y', '%A, %e %b', '-%A, %e %b %Y'],
  47520. * month: ['%B %Y', '%B', '-%B %Y'],
  47521. * year: ['%Y', '%Y', '-%Y']
  47522. * }
  47523. * ```
  47524. *
  47525. * For each of these array definitions, the first item is the format
  47526. * used when the active time span is one unit. For instance, if the
  47527. * current data applies to one week, the first item of the week array
  47528. * is used. The second and third items are used when the active time
  47529. * span is more than two units. For instance, if the current data applies
  47530. * to two weeks, the second and third item of the week array are used,
  47531. * and applied to the start and end date of the time span.
  47532. *
  47533. * @type {Object}
  47534. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  47535. */
  47536. /**
  47537. * Enable or disable data grouping.
  47538. *
  47539. * @type {boolean}
  47540. * @default true
  47541. * @apioption plotOptions.series.dataGrouping.enabled
  47542. */
  47543. /**
  47544. * Specifies how the first grouped point is positioned on the xAxis.
  47545. * If firstAnchor and/or lastAnchor are defined, then those options take
  47546. * precedence over anchor for the first and/or last grouped points.
  47547. * Available options:
  47548. *
  47549. * -`start` places the point at the beginning of the group
  47550. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  47551. *
  47552. * -`middle` places the point in the middle of the group
  47553. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  47554. *
  47555. * -`end` places the point at the end of the group
  47556. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  47557. *
  47558. * -`firstPoint` the first point in the group
  47559. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  47560. *
  47561. * -`lastPoint` the last point in the group
  47562. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  47563. *
  47564. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  47565. * Applying first and last anchor.
  47566. *
  47567. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  47568. *
  47569. * @type {Highcharts.DataGroupingAnchorExtremes}
  47570. * @since 9.1.0
  47571. * @default start
  47572. * @apioption plotOptions.series.dataGrouping.firstAnchor
  47573. */
  47574. /**
  47575. * When data grouping is forced, it runs no matter how small the intervals
  47576. * are. This can be handy for example when the sum should be calculated
  47577. * for values appearing at random times within each hour.
  47578. *
  47579. * @type {boolean}
  47580. * @default false
  47581. * @apioption plotOptions.series.dataGrouping.forced
  47582. */
  47583. /**
  47584. * The approximate pixel width of each group. If for example a series
  47585. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  47586. * is performed. If however the series contains so many points that
  47587. * the spacing is less than the groupPixelWidth, Highcharts will try
  47588. * to group it into appropriate groups so that each is more or less
  47589. * two pixels wide. If multiple series with different group pixel widths
  47590. * are drawn on the same x axis, all series will take the greatest width.
  47591. * For example, line series have 2px default group width, while column
  47592. * series have 10px. If combined, both the line and the column will
  47593. * have 10px by default.
  47594. *
  47595. * @type {number}
  47596. * @default 2
  47597. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  47598. */
  47599. /**
  47600. * By default only points within the visible range are grouped. Enabling this
  47601. * option will force data grouping to calculate all grouped points for a given
  47602. * dataset. That option prevents for example a column series from calculating
  47603. * a grouped point partially. The effect is similar to
  47604. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  47605. * not affect yAxis extremes.
  47606. *
  47607. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  47608. * Two series with the same data but different groupAll setting
  47609. *
  47610. * @type {boolean}
  47611. * @default false
  47612. * @since 6.1.0
  47613. * @apioption plotOptions.series.dataGrouping.groupAll
  47614. */
  47615. /**
  47616. * Specifies how the last grouped point is positioned on the xAxis.
  47617. * If firstAnchor and/or lastAnchor are defined, then those options take
  47618. * precedence over anchor for the first and/or last grouped points.
  47619. * Available options:
  47620. *
  47621. * -`start` places the point at the beginning of the group
  47622. * (e.g. range 00:00:00 - 23:59:59 -> 00:00:00)
  47623. *
  47624. * -`middle` places the point in the middle of the group
  47625. * (e.g. range 00:00:00 - 23:59:59 -> 12:00:00)
  47626. *
  47627. * -`end` places the point at the end of the group
  47628. * (e.g. range 00:00:00 - 23:59:59 -> 23:59:59)
  47629. *
  47630. * -`firstPoint` the first point in the group
  47631. * (e.g. points at 00:13, 00:35, 00:59 -> 00:13)
  47632. *
  47633. * -`lastPoint` the last point in the group
  47634. * (e.g. points at 00:13, 00:35, 00:59 -> 00:59)
  47635. *
  47636. * @sample {highstock} stock/plotoptions/series-datagrouping-first-anchor
  47637. * Applying first and last anchor.
  47638. *
  47639. * @sample {highstock} stock/plotoptions/series-datagrouping-last-anchor
  47640. * Applying the last anchor in the chart with live data.
  47641. *
  47642. * @see [dataGrouping.anchor](#plotOptions.series.dataGrouping.anchor)
  47643. *
  47644. * @type {Highcharts.DataGroupingAnchorExtremes}
  47645. * @since 9.1.0
  47646. * @default start
  47647. * @apioption plotOptions.series.dataGrouping.lastAnchor
  47648. */
  47649. /**
  47650. * Normally, a group is indexed by the start of that group, so for example
  47651. * when 30 daily values are grouped into one month, that month's x value
  47652. * will be the 1st of the month. This apparently shifts the data to
  47653. * the left. When the smoothed option is true, this is compensated for.
  47654. * The data is shifted to the middle of the group, and min and max
  47655. * values are preserved. Internally, this is used in the Navigator series.
  47656. *
  47657. * @type {boolean}
  47658. * @default false
  47659. * @deprecated
  47660. * @apioption plotOptions.series.dataGrouping.smoothed
  47661. */
  47662. /**
  47663. * An array determining what time intervals the data is allowed to be
  47664. * grouped to. Each array item is an array where the first value is
  47665. * the time unit and the second value another array of allowed multiples.
  47666. *
  47667. * Defaults to:
  47668. * ```js
  47669. * units: [[
  47670. * 'millisecond', // unit name
  47671. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  47672. * ], [
  47673. * 'second',
  47674. * [1, 2, 5, 10, 15, 30]
  47675. * ], [
  47676. * 'minute',
  47677. * [1, 2, 5, 10, 15, 30]
  47678. * ], [
  47679. * 'hour',
  47680. * [1, 2, 3, 4, 6, 8, 12]
  47681. * ], [
  47682. * 'day',
  47683. * [1]
  47684. * ], [
  47685. * 'week',
  47686. * [1]
  47687. * ], [
  47688. * 'month',
  47689. * [1, 3, 6]
  47690. * ], [
  47691. * 'year',
  47692. * null
  47693. * ]]
  47694. * ```
  47695. *
  47696. * @type {Array<Array<string,(Array<number>|null)>>}
  47697. * @apioption plotOptions.series.dataGrouping.units
  47698. */
  47699. /**
  47700. * The approximate pixel width of each group. If for example a series
  47701. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  47702. * is performed. If however the series contains so many points that
  47703. * the spacing is less than the groupPixelWidth, Highcharts will try
  47704. * to group it into appropriate groups so that each is more or less
  47705. * two pixels wide. Defaults to `10`.
  47706. *
  47707. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  47708. * Two series with the same data density but different groupPixelWidth
  47709. *
  47710. * @type {number}
  47711. * @default 10
  47712. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  47713. */
  47714. ''; // required by JSDoc parsing
  47715. return DataGroupingComposition;
  47716. });
  47717. _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) {
  47718. const G = Highcharts;
  47719. G.dataGrouping = {
  47720. approximationDefaults: ApproximationDefaults,
  47721. approximations: ApproximationRegistry
  47722. };
  47723. DataGrouping.compose(G.Axis, G.Series, G.Tooltip);
  47724. });
  47725. _registerModule(_modules, 'Extensions/MouseWheelZoom/MouseWheelZoom.js', [_modules['Core/Utilities.js']], function (U) {
  47726. /* *
  47727. *
  47728. * (c) 2023 Torstein Honsi, Askel Eirik Johansson
  47729. *
  47730. * License: www.highcharts.com/license
  47731. *
  47732. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47733. *
  47734. * */
  47735. const { addEvent, isObject, pick, defined, merge } = U;
  47736. /* *
  47737. *
  47738. * Constants
  47739. *
  47740. * */
  47741. const composedClasses = [], defaultOptions = {
  47742. enabled: true,
  47743. sensitivity: 1.1
  47744. };
  47745. /* *
  47746. *
  47747. * Functions
  47748. *
  47749. * */
  47750. /**
  47751. * @private
  47752. */
  47753. const optionsToObject = (options) => {
  47754. if (!isObject(options)) {
  47755. return merge(defaultOptions, { enabled: defined(options) ? options : true });
  47756. }
  47757. return merge(defaultOptions, options);
  47758. };
  47759. /**
  47760. * @private
  47761. */
  47762. const fitToBox = function (inner, outer) {
  47763. if (inner.x + inner.width > outer.x + outer.width) {
  47764. if (inner.width > outer.width) {
  47765. inner.width = outer.width;
  47766. inner.x = outer.x;
  47767. }
  47768. else {
  47769. inner.x = outer.x + outer.width - inner.width;
  47770. }
  47771. }
  47772. if (inner.width > outer.width) {
  47773. inner.width = outer.width;
  47774. }
  47775. if (inner.x < outer.x) {
  47776. inner.x = outer.x;
  47777. }
  47778. // y and height
  47779. if (inner.y + inner.height > outer.y + outer.height) {
  47780. if (inner.height > outer.height) {
  47781. inner.height = outer.height;
  47782. inner.y = outer.y;
  47783. }
  47784. else {
  47785. inner.y = outer.y + outer.height - inner.height;
  47786. }
  47787. }
  47788. if (inner.height > outer.height) {
  47789. inner.height = outer.height;
  47790. }
  47791. if (inner.y < outer.y) {
  47792. inner.y = outer.y;
  47793. }
  47794. return inner;
  47795. };
  47796. let wheelTimer, originalOptions;
  47797. /**
  47798. * @private
  47799. */
  47800. const zoomBy = function (chart, howMuch, centerXArg, centerYArg, mouseX, mouseY, options) {
  47801. 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);
  47802. if (defined(xAxis.max) && defined(xAxis.min) &&
  47803. defined(yAxis.max) && defined(yAxis.min) &&
  47804. defined(xAxis.dataMax) && defined(xAxis.dataMin) &&
  47805. defined(yAxis.dataMax) && defined(yAxis.dataMin)) {
  47806. if (zoomY) {
  47807. // Options interfering with yAxis zoom by setExtremes() returning
  47808. // integers by default.
  47809. if (defined(wheelTimer)) {
  47810. clearTimeout(wheelTimer);
  47811. }
  47812. const { startOnTick, endOnTick } = yAxis.options;
  47813. if (!originalOptions) {
  47814. originalOptions = { startOnTick, endOnTick };
  47815. }
  47816. if (startOnTick || endOnTick) {
  47817. yAxis.setOptions({ startOnTick: false, endOnTick: false });
  47818. }
  47819. wheelTimer = setTimeout(() => {
  47820. if (originalOptions) {
  47821. yAxis.setOptions(originalOptions);
  47822. // Set the extremes to the same as they already are, but now
  47823. // with the original startOnTick and endOnTick. We need
  47824. // `forceRedraw` otherwise it will detect that the values
  47825. // haven't changed. We do not use a simple yAxis.update()
  47826. // because it will destroy the ticks and prevent animation.
  47827. const { min, max } = yAxis.getExtremes();
  47828. yAxis.forceRedraw = true;
  47829. yAxis.setExtremes(min, max);
  47830. originalOptions = void 0;
  47831. }
  47832. }, 400);
  47833. }
  47834. if (chart.inverted) {
  47835. const emulateRoof = yAxis.pos + yAxis.len;
  47836. // Get the correct values
  47837. centerXArg = xAxis.toValue(mouseY);
  47838. centerYArg = yAxis.toValue(mouseX);
  47839. // Swapping x and y for simplicity when chart is inverted.
  47840. const tmp = mouseX;
  47841. mouseX = mouseY;
  47842. mouseY = emulateRoof - tmp + yAxis.pos;
  47843. }
  47844. let fixToX = mouseX ? ((mouseX - xAxis.pos) / xAxis.len) : 0.5;
  47845. if (xAxis.reversed && !chart.inverted ||
  47846. chart.inverted && !xAxis.reversed) {
  47847. // We are taking into account that xAxis automatically gets
  47848. // reversed when chart.inverted
  47849. fixToX = 1 - fixToX;
  47850. }
  47851. let fixToY = 1 - (mouseY ? ((mouseY - yAxis.pos) / yAxis.len) : 0.5);
  47852. if (yAxis.reversed) {
  47853. fixToY = 1 - fixToY;
  47854. }
  47855. 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 +
  47856. dataRangeX * xAxis.options.maxPadding, outerY = yAxis.dataMin - dataRangeY * yAxis.options.minPadding, outerHeight = dataRangeY + dataRangeY * yAxis.options.minPadding +
  47857. dataRangeY * yAxis.options.maxPadding, newExt = fitToBox({
  47858. x: newXMin,
  47859. y: newYMin,
  47860. width: newXRange,
  47861. height: newYRange
  47862. }, {
  47863. x: outerX,
  47864. y: outerY,
  47865. width: outerWidth,
  47866. height: outerHeight
  47867. }), zoomOut = (newExt.x <= outerX &&
  47868. newExt.width >=
  47869. outerWidth &&
  47870. newExt.y <= outerY &&
  47871. newExt.height >= outerHeight);
  47872. // Zoom
  47873. if (defined(howMuch) && !zoomOut) {
  47874. if (zoomX) {
  47875. xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false);
  47876. }
  47877. if (zoomY) {
  47878. yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false);
  47879. }
  47880. // Reset zoom
  47881. }
  47882. else {
  47883. if (zoomX) {
  47884. xAxis.setExtremes(void 0, void 0, false);
  47885. }
  47886. if (zoomY) {
  47887. yAxis.setExtremes(void 0, void 0, false);
  47888. }
  47889. }
  47890. chart.redraw(false);
  47891. }
  47892. };
  47893. /**
  47894. * @private
  47895. */
  47896. function onAfterGetContainer() {
  47897. const chart = this, wheelZoomOptions = optionsToObject(chart.options.chart.zooming.mouseWheel);
  47898. if (wheelZoomOptions.enabled) {
  47899. addEvent(this.container, 'wheel', (e) => {
  47900. e = this.pointer.normalize(e);
  47901. // Firefox uses e.detail, WebKit and IE uses deltaX, deltaY, deltaZ.
  47902. if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) {
  47903. const wheelSensitivity = pick(wheelZoomOptions.sensitivity, 1.1), delta = e.detail || ((e.deltaY || 0) / 120);
  47904. zoomBy(chart, Math.pow(wheelSensitivity, delta), chart.xAxis[0].toValue(e.chartX), chart.yAxis[0].toValue(e.chartY), e.chartX, e.chartY, wheelZoomOptions);
  47905. }
  47906. // prevent page scroll
  47907. if (e.preventDefault) {
  47908. e.preventDefault();
  47909. }
  47910. });
  47911. }
  47912. }
  47913. /**
  47914. * @private
  47915. */
  47916. function compose(ChartClass) {
  47917. if (composedClasses.indexOf(ChartClass) === -1) {
  47918. composedClasses.push(ChartClass);
  47919. addEvent(ChartClass, 'afterGetContainer', onAfterGetContainer);
  47920. }
  47921. }
  47922. /* *
  47923. *
  47924. * Default Export
  47925. *
  47926. * */
  47927. const MouseWheelZoomComposition = {
  47928. compose
  47929. };
  47930. /* *
  47931. *
  47932. * API Options
  47933. *
  47934. * */
  47935. /**
  47936. * The mouse wheel zoom is a feature included in Highcharts Stock, but is
  47937. * also available for Highcharts Core as a module. Zooming with the mouse wheel
  47938. * is enabled by default. It can be disabled by setting this option to
  47939. * `false`.
  47940. *
  47941. * @type {boolean|object}
  47942. * @since 11.1.0
  47943. * @requires modules/mouse-wheel-zoom
  47944. * @sample {highcharts} highcharts/mouse-wheel-zoom/enabled
  47945. * Enable or disable
  47946. * @sample {highstock} stock/mouse-wheel-zoom/enabled
  47947. * Enable or disable
  47948. * @apioption chart.zooming.mouseWheel
  47949. */
  47950. /**
  47951. * Zooming with the mouse wheel can be disabled by setting this option to
  47952. * `false`.
  47953. *
  47954. * @type {boolean}
  47955. * @default true
  47956. * @since 11.1.0
  47957. * @requires modules/mouse-wheel-zoom
  47958. * @apioption chart.zooming.mouseWheel.enabled
  47959. */
  47960. /**
  47961. * Adjust the sensitivity of the zoom. Sensitivity of mouse wheel or trackpad
  47962. * scrolling. `1` is no sensitivity, while with `2`, one mouse wheel delta will
  47963. * zoom in `50%`.
  47964. *
  47965. * @type {number}
  47966. * @default 1.1
  47967. * @since 11.1.0
  47968. * @requires modules/mouse-wheel-zoom
  47969. * @sample {highcharts} highcharts/mouse-wheel-zoom/sensitivity
  47970. * Change mouse wheel zoom sensitivity
  47971. * @sample {highstock} stock/mouse-wheel-zoom/sensitivity
  47972. * Change mouse wheel zoom sensitivity
  47973. * @apioption chart.zooming.mouseWheel.sensitivity
  47974. */
  47975. /**
  47976. * Decides in what dimensions the user can zoom scrolling the wheel.
  47977. * Can be one of `x`, `y` or `xy`. If not specified here, it will inherit the
  47978. * type from [chart.zooming.type](chart.zooming.type).
  47979. *
  47980. * Note that particularly with mouse wheel in the y direction, the zoom is
  47981. * affected by the default [yAxis.startOnTick](#yAxis.startOnTick) and
  47982. * [endOnTick]((#yAxis.endOnTick)) settings. In order to respect these settings,
  47983. * the zoom level will adjust after the user has stopped zooming. To prevent
  47984. * this, consider setting `startOnTick` and `endOnTick` to `false`.
  47985. *
  47986. * @type {string}
  47987. * @default x
  47988. * @validvalue ["x", "y", "xy"]
  47989. * @since 11.1.0
  47990. * @requires modules/mouse-wheel-zoom
  47991. * @apioption chart.zooming.mouseWheel.type
  47992. */
  47993. (''); // Keeps doclets above in JS file
  47994. return MouseWheelZoomComposition;
  47995. });
  47996. _registerModule(_modules, 'masters/modules/mouse-wheel-zoom.src.js', [_modules['Core/Globals.js'], _modules['Extensions/MouseWheelZoom/MouseWheelZoom.js']], function (Highcharts, MouseWheelZoom) {
  47997. const G = Highcharts;
  47998. MouseWheelZoom.compose(G.Chart);
  47999. });
  48000. _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) {
  48001. /* *
  48002. *
  48003. * (c) 2010-2021 Torstein Honsi
  48004. *
  48005. * License: www.highcharts.com/license
  48006. *
  48007. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48008. *
  48009. * */
  48010. const { prototype: { tooltipFormatter: pointTooltipFormatter } } = Point;
  48011. const { addEvent, arrayMax, arrayMin, correctFloat, defined, isArray, isNumber, isString, pick } = U;
  48012. /* *
  48013. *
  48014. * Composition
  48015. *
  48016. * */
  48017. var DataModifyComposition;
  48018. (function (DataModifyComposition) {
  48019. /* *
  48020. *
  48021. * Declarations
  48022. *
  48023. * */
  48024. /* *
  48025. *
  48026. * Constants
  48027. *
  48028. * */
  48029. const composedMembers = [];
  48030. /* *
  48031. *
  48032. * Functions
  48033. *
  48034. * */
  48035. /* eslint-disable valid-jsdoc */
  48036. /**
  48037. * Extends the series, axis and point classes with
  48038. * compare and cumulative support.
  48039. *
  48040. * @private
  48041. *
  48042. * @param SeriesClass
  48043. * Series class to use.
  48044. *
  48045. * @param AxisClass
  48046. * Axis class to extend.
  48047. *
  48048. * @param PointClass
  48049. * Point class to use.
  48050. */
  48051. function compose(SeriesClass, AxisClass, PointClass) {
  48052. if (U.pushUnique(composedMembers, SeriesClass)) {
  48053. const seriesProto = SeriesClass.prototype;
  48054. seriesProto.setCompare = seriesSetCompare;
  48055. seriesProto.setCumulative = seriesSetCumulative;
  48056. addEvent(SeriesClass, 'afterInit', afterInit);
  48057. addEvent(SeriesClass, 'afterGetExtremes', afterGetExtremes);
  48058. addEvent(SeriesClass, 'afterProcessData', afterProcessData);
  48059. }
  48060. if (U.pushUnique(composedMembers, AxisClass)) {
  48061. const axisProto = AxisClass.prototype;
  48062. axisProto.setCompare = axisSetCompare;
  48063. axisProto.setModifier = setModifier;
  48064. axisProto.setCumulative = axisSetCumulative;
  48065. }
  48066. if (U.pushUnique(composedMembers, PointClass)) {
  48067. const pointProto = PointClass.prototype;
  48068. pointProto.tooltipFormatter = tooltipFormatter;
  48069. }
  48070. return SeriesClass;
  48071. }
  48072. DataModifyComposition.compose = compose;
  48073. /* ********************************************************************** *
  48074. * Start shared compare and cumulative logic *
  48075. * ********************************************************************** */
  48076. /**
  48077. * Shared code for the axis.setCompare() and the axis.setCumulative()
  48078. * methods. Inits the 'compare' or the 'cumulative' mode.
  48079. * @private
  48080. */
  48081. function setModifier(mode, modeState, redraw) {
  48082. if (!this.isXAxis) {
  48083. this.series.forEach(function (series) {
  48084. if (mode === 'compare' &&
  48085. typeof modeState !== 'boolean') {
  48086. series.setCompare(modeState, false);
  48087. }
  48088. else if (mode === 'cumulative' &&
  48089. !isString(modeState)) {
  48090. series.setCumulative(modeState, false);
  48091. }
  48092. });
  48093. if (pick(redraw, true)) {
  48094. this.chart.redraw();
  48095. }
  48096. }
  48097. }
  48098. /**
  48099. * Extend the tooltip formatter by adding support for the point.change
  48100. * variable as well as the changeDecimals option.
  48101. *
  48102. * @ignore
  48103. * @function Highcharts.Point#tooltipFormatter
  48104. *
  48105. * @param {string} pointFormat
  48106. */
  48107. function tooltipFormatter(pointFormat) {
  48108. const point = this, { numberFormatter } = point.series.chart, replace = function (value) {
  48109. pointFormat = pointFormat.replace('{point.' + value + '}', (point[value] > 0 && value === 'change' ? '+' : '') +
  48110. numberFormatter(point[value], pick(point.series.tooltipOptions.changeDecimals, 2)));
  48111. };
  48112. if (defined(point.change)) {
  48113. replace('change');
  48114. }
  48115. if (defined(point.cumulativeSum)) {
  48116. replace('cumulativeSum');
  48117. }
  48118. return pointTooltipFormatter.apply(this, [pointFormat]);
  48119. }
  48120. /**
  48121. * Extend series.init by adding a methods to modify the y values used
  48122. * for plotting on the y axis. For compare mode, this method is called both
  48123. * from the axis when finding dataMin and dataMax,
  48124. * and from the series.translate method.
  48125. *
  48126. * @ignore
  48127. * @function Highcharts.Series#init
  48128. */
  48129. function afterInit() {
  48130. const compare = this.options.compare;
  48131. let dataModify;
  48132. if (compare === 'percent' ||
  48133. compare === 'value' ||
  48134. this.options.cumulative) {
  48135. dataModify = new Additions(this);
  48136. if (compare === 'percent' || compare === 'value') {
  48137. // Set comparison mode
  48138. dataModify.initCompare(compare);
  48139. }
  48140. else {
  48141. // Set Cumulative Sum mode
  48142. dataModify.initCumulative();
  48143. }
  48144. }
  48145. this.dataModify = dataModify;
  48146. }
  48147. /**
  48148. * Adjust the extremes (compare and cumulative modify the data).
  48149. * @private
  48150. */
  48151. function afterGetExtremes(e) {
  48152. const dataExtremes = e.dataExtremes, activeYData = dataExtremes.activeYData;
  48153. if (this.dataModify && dataExtremes) {
  48154. let extremes;
  48155. if (this.options.compare) {
  48156. extremes = [
  48157. this.dataModify.modifyValue(dataExtremes.dataMin),
  48158. this.dataModify.modifyValue(dataExtremes.dataMax)
  48159. ];
  48160. }
  48161. else if (this.options.cumulative &&
  48162. isArray(activeYData) &&
  48163. // If only one y visible, sum doesn't change
  48164. // so no need to change extremes
  48165. activeYData.length >= 2) {
  48166. extremes = Additions.getCumulativeExtremes(activeYData);
  48167. }
  48168. if (extremes) {
  48169. dataExtremes.dataMin = arrayMin(extremes);
  48170. dataExtremes.dataMax = arrayMax(extremes);
  48171. }
  48172. }
  48173. }
  48174. /* ********************************************************************** *
  48175. * End shared compare and cumulative logic *
  48176. * ********************************************************************** */
  48177. /* ********************************************************************** *
  48178. * Start value compare logic *
  48179. * ********************************************************************** */
  48180. /**
  48181. * Highcharts Stock only. Set the
  48182. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  48183. * mode of the series after render time.
  48184. * In most cases it is more useful running
  48185. * {@link Axis#setCompare} on the X axis to update all its series.
  48186. *
  48187. * @function Highcharts.Series#setCompare
  48188. *
  48189. * @param {string|null} [compare]
  48190. * Can be one of `undefined` (default), `null`, `"percent"`
  48191. * or `"value"`.
  48192. *
  48193. * @param {boolean} [redraw=true]
  48194. * Whether to redraw the chart or to wait for a later call to
  48195. * {@link Chart#redraw}.
  48196. */
  48197. function seriesSetCompare(compare, redraw) {
  48198. // Survive to export, #5485 (and for options generally)
  48199. this.options.compare = this.userOptions.compare = compare;
  48200. // Fire series.init() that will set or delete series.dataModify
  48201. this.update({}, pick(redraw, true));
  48202. if (this.dataModify && (compare === 'value' || compare === 'percent')) {
  48203. this.dataModify.initCompare(compare);
  48204. }
  48205. else {
  48206. // When disabling, clear the points
  48207. this.points.forEach((point) => {
  48208. delete point.change;
  48209. });
  48210. }
  48211. }
  48212. /**
  48213. * Extend series.processData by finding the first y value in the plot area,
  48214. * used for comparing the following values
  48215. *
  48216. * @ignore
  48217. * @function Highcharts.Series#processData
  48218. */
  48219. function afterProcessData() {
  48220. const series = this;
  48221. if (series.xAxis && // not pies
  48222. series.processedYData &&
  48223. series.dataModify) {
  48224. const processedXData = series.processedXData, processedYData = series.processedYData, length = processedYData.length, compareStart = series.options.compareStart === true ? 0 : 1;
  48225. let keyIndex = -1, i;
  48226. // For series with more than one value (range, OHLC etc), compare
  48227. // against close or the pointValKey (#4922, #3112, #9854)
  48228. if (series.pointArrayMap) {
  48229. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  48230. }
  48231. // find the first value for comparison
  48232. for (i = 0; i < length - compareStart; i++) {
  48233. const compareValue = processedYData[i] && keyIndex > -1 ?
  48234. processedYData[i][keyIndex] : processedYData[i];
  48235. if (isNumber(compareValue) &&
  48236. compareValue !== 0 &&
  48237. processedXData[i + compareStart] >= (series.xAxis.min || 0)) {
  48238. series.dataModify.compareValue = compareValue;
  48239. break;
  48240. }
  48241. }
  48242. }
  48243. }
  48244. /**
  48245. * Highcharts Stock only. Set the compare mode on all series
  48246. * belonging to a Y axis.
  48247. *
  48248. * @see [plotOptions.series.compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  48249. *
  48250. * @sample stock/members/axis-setcompare/
  48251. * Set compare
  48252. *
  48253. * @function Highcharts.Axis#setCompare
  48254. *
  48255. * @param {string|null} [compare]
  48256. * The compare mode. Can be one of `undefined` (default), `null`,
  48257. * `"value"` or `"percent"`.
  48258. *
  48259. * @param {boolean} [redraw=true]
  48260. * Whether to redraw the chart or to wait for a later call to
  48261. * {@link Chart#redraw}.
  48262. */
  48263. function axisSetCompare(compare, redraw) {
  48264. this.setModifier('compare', compare, redraw);
  48265. }
  48266. /* ********************************************************************** *
  48267. * End value compare logic *
  48268. * ********************************************************************** */
  48269. /* ********************************************************************** *
  48270. * Start Cumulative Sum logic, author: Rafal Sebestjanski *
  48271. * ********************************************************************** */
  48272. /**
  48273. * Highcharts Stock only. Set the
  48274. * [cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative)
  48275. * mode of the series after render time.
  48276. * In most cases it is more useful running
  48277. * {@link Axis#setCumulative} on the Y axis to update all its series.
  48278. *
  48279. * @function Highcharts.Series#setCumulative
  48280. *
  48281. * @param {boolean} [cumulative=false]
  48282. * Either enable or disable Cumulative Sum mode.
  48283. * Can be one of `false` (default) or `true`.
  48284. *
  48285. * @param {boolean} [redraw=true]
  48286. * Whether to redraw the chart or to wait for a later call to
  48287. * {@link Chart#redraw}.
  48288. */
  48289. function seriesSetCumulative(cumulative, redraw) {
  48290. // Set default value to false
  48291. cumulative = pick(cumulative, false);
  48292. // Survive to export, #5485 (and for options generally)
  48293. this.options.cumulative = this.userOptions.cumulative = cumulative;
  48294. // Fire series.init() that will set or delete series.dataModify
  48295. this.update({}, pick(redraw, true));
  48296. // If should, turn on the Cumulative Sum mode
  48297. if (this.dataModify) {
  48298. this.dataModify.initCumulative();
  48299. }
  48300. else {
  48301. // When disabling, clear the points
  48302. this.points.forEach((point) => {
  48303. delete point.cumulativeSum;
  48304. });
  48305. }
  48306. }
  48307. /**
  48308. * Highcharts Stock only. Set the cumulative mode on all series
  48309. * belonging to a Y axis.
  48310. *
  48311. * @see [plotOptions.series.cumulative](https://api.highcharts.com/highstock/plotOptions.series.cumulative)
  48312. *
  48313. * @sample stock/members/axis-setcumulative/
  48314. * Set cumulative
  48315. *
  48316. * @function Highcharts.Axis#setCumulative
  48317. *
  48318. * @param {boolean} [cumulative]
  48319. * Whether to disable or enable the cumulative mode.
  48320. * Can be one of `undefined` (default, treated as `false`),
  48321. * `false` or `true`.
  48322. *
  48323. * @param {boolean} [redraw=true]
  48324. * Whether to redraw the chart or to wait for a later call to
  48325. * {@link Chart#redraw}.
  48326. */
  48327. function axisSetCumulative(cumulative, redraw) {
  48328. this.setModifier('cumulative', cumulative, redraw);
  48329. }
  48330. /* *
  48331. *
  48332. * Classes
  48333. *
  48334. * */
  48335. /**
  48336. * @private
  48337. */
  48338. class Additions {
  48339. /* *
  48340. *
  48341. * Constructors
  48342. *
  48343. * */
  48344. /**
  48345. * @private
  48346. */
  48347. constructor(series) {
  48348. this.series = series;
  48349. }
  48350. /* *
  48351. *
  48352. * Functions
  48353. *
  48354. * */
  48355. /**
  48356. * @private
  48357. */
  48358. modifyValue() {
  48359. return 0;
  48360. }
  48361. /**
  48362. * @ignore
  48363. * @function Highcharts.Series#getCumulativeExtremes
  48364. *
  48365. * @param {Array} [activeYData]
  48366. * An array cointaining all the points' y values
  48367. * in a visible range.
  48368. */
  48369. static getCumulativeExtremes(activeYData) {
  48370. let cumulativeDataMin = Infinity, cumulativeDataMax = -Infinity;
  48371. activeYData.reduce((prev, cur) => {
  48372. const sum = prev + cur;
  48373. cumulativeDataMin = Math.min(cumulativeDataMin, sum, prev);
  48374. cumulativeDataMax = Math.max(cumulativeDataMax, sum, prev);
  48375. return sum;
  48376. });
  48377. return [cumulativeDataMin, cumulativeDataMax];
  48378. }
  48379. /**
  48380. * @ignore
  48381. * @function Highcharts.Series#initCompare
  48382. *
  48383. * @param {string} [compare]
  48384. * Can be one of `"percent"` or `"value"`.
  48385. */
  48386. initCompare(compare) {
  48387. // Set the modifyValue method
  48388. this.modifyValue = function (value, index) {
  48389. if (value === null) {
  48390. value = 0;
  48391. }
  48392. const compareValue = this.compareValue;
  48393. if (typeof value !== 'undefined' &&
  48394. typeof compareValue !== 'undefined') { // #2601, #5814
  48395. // Get the modified value
  48396. if (compare === 'value') {
  48397. value -= compareValue;
  48398. // Compare percent
  48399. }
  48400. else {
  48401. const compareBase = this.series.options.compareBase;
  48402. value = 100 * (value / compareValue) -
  48403. (compareBase === 100 ? 0 : 100);
  48404. }
  48405. // record for tooltip etc.
  48406. if (typeof index !== 'undefined') {
  48407. const point = this.series.points[index];
  48408. if (point) {
  48409. point.change = value;
  48410. }
  48411. }
  48412. return value;
  48413. }
  48414. return 0;
  48415. };
  48416. }
  48417. /**
  48418. * @ignore
  48419. * @function Highcharts.Series#initCumulative
  48420. */
  48421. initCumulative() {
  48422. // Set the modifyValue method
  48423. this.modifyValue = function (value, index) {
  48424. if (value === null) {
  48425. value = 0;
  48426. }
  48427. if (value !== void 0 && index !== void 0) {
  48428. const prevPoint = index > 0 ?
  48429. this.series.points[index - 1] : null;
  48430. // Get the modified value
  48431. if (prevPoint && prevPoint.cumulativeSum) {
  48432. value = correctFloat(prevPoint.cumulativeSum + value);
  48433. }
  48434. // Record for tooltip etc.
  48435. const point = this.series.points[index];
  48436. if (point) {
  48437. point.cumulativeSum = value;
  48438. }
  48439. return value;
  48440. }
  48441. return 0;
  48442. };
  48443. }
  48444. }
  48445. DataModifyComposition.Additions = Additions;
  48446. })(DataModifyComposition || (DataModifyComposition = {}));
  48447. /* *
  48448. *
  48449. * Default Export
  48450. *
  48451. * */
  48452. /* *
  48453. *
  48454. * API Options
  48455. *
  48456. * */
  48457. /**
  48458. * Compare the values of the series against the first non-null, non-
  48459. * zero value in the visible range. The y axis will show percentage
  48460. * or absolute change depending on whether `compare` is set to `"percent"`
  48461. * or `"value"`. When this is applied to multiple series, it allows
  48462. * comparing the development of the series against each other. Adds
  48463. * a `change` field to every point object.
  48464. *
  48465. * @see [compareBase](#plotOptions.series.compareBase)
  48466. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  48467. * @see [Series.setCompare()](/class-reference/Highcharts.Series#setCompare)
  48468. *
  48469. * @sample {highstock} stock/plotoptions/series-compare-percent/
  48470. * Percent
  48471. * @sample {highstock} stock/plotoptions/series-compare-value/
  48472. * Value
  48473. *
  48474. * @type {string}
  48475. * @since 1.0.1
  48476. * @product highstock
  48477. * @validvalue ["percent", "value"]
  48478. * @apioption plotOptions.series.compare
  48479. */
  48480. /**
  48481. * Defines if comparison should start from the first point within the visible
  48482. * range or should start from the first point **before** the range.
  48483. *
  48484. * In other words, this flag determines if first point within the visible range
  48485. * will have 0% (`compareStart=true`) or should have been already calculated
  48486. * according to the previous point (`compareStart=false`).
  48487. *
  48488. * @sample {highstock} stock/plotoptions/series-comparestart/
  48489. * Calculate compare within visible range
  48490. *
  48491. * @type {boolean}
  48492. * @default false
  48493. * @since 6.0.0
  48494. * @product highstock
  48495. * @apioption plotOptions.series.compareStart
  48496. */
  48497. /**
  48498. * When [compare](#plotOptions.series.compare) is `percent`, this option
  48499. * dictates whether to use 0 or 100 as the base of comparison.
  48500. *
  48501. * @sample {highstock} stock/plotoptions/series-comparebase/
  48502. * Compare base is 100
  48503. *
  48504. * @type {number}
  48505. * @default 0
  48506. * @since 5.0.6
  48507. * @product highstock
  48508. * @validvalue [0, 100]
  48509. * @apioption plotOptions.series.compareBase
  48510. */
  48511. /**
  48512. * Cumulative Sum feature replaces points' values with the following formula:
  48513. * `sum of all previous points' values + current point's value`.
  48514. * Works only for points in a visible range.
  48515. * Adds the `cumulativeSum` field to each point object that can be accessed
  48516. * e.g. in the [tooltip.pointFormat](https://api.highcharts.com/highstock/tooltip.pointFormat).
  48517. *
  48518. * With `dataGrouping` enabled, default grouping approximation is set to `sum`.
  48519. *
  48520. * @see [Axis.setCumulative()](/class-reference/Highcharts.Axis#setCumulative)
  48521. * @see [Series.setCumulative()](/class-reference/Highcharts.Series#setCumulative)
  48522. *
  48523. * @sample {highstock} stock/plotoptions/series-cumulative-sum/
  48524. * Cumulative Sum
  48525. *
  48526. * @type {boolean}
  48527. * @default false
  48528. * @since 9.3.0
  48529. * @product highstock
  48530. * @apioption plotOptions.series.cumulative
  48531. */
  48532. ''; // keeps doclets above in transpiled file
  48533. return DataModifyComposition;
  48534. });
  48535. _registerModule(_modules, 'Core/Axis/NavigatorAxisComposition.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  48536. /* *
  48537. *
  48538. * (c) 2010-2021 Torstein Honsi
  48539. *
  48540. * License: www.highcharts.com/license
  48541. *
  48542. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48543. *
  48544. * */
  48545. const { isTouchDevice } = H;
  48546. const { addEvent, correctFloat, defined, isNumber, pick } = U;
  48547. /* *
  48548. *
  48549. * Constants
  48550. *
  48551. * */
  48552. const composedMembers = [];
  48553. /* *
  48554. *
  48555. * Functions
  48556. *
  48557. * */
  48558. /**
  48559. * @private
  48560. */
  48561. function onAxisInit() {
  48562. const axis = this;
  48563. if (!axis.navigatorAxis) {
  48564. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  48565. }
  48566. }
  48567. /**
  48568. * For Stock charts, override selection zooming with some special features
  48569. * because X axis zooming is already allowed by the Navigator and Range
  48570. * selector.
  48571. * @private
  48572. */
  48573. function onAxisZoom(e) {
  48574. console.log("onAxisZoom")
  48575. 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;
  48576. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  48577. (rangeSelector && rangeSelector.enabled))) {
  48578. // For y only zooming, ignore the X axis completely
  48579. if (zoomType === 'y') {
  48580. e.zoomed = false;
  48581. // For xy zooming, record the state of the zoom before zoom
  48582. // selection, then when the reset button is pressed, revert to
  48583. // this state. This should apply only if the chart is
  48584. // initialized with a range (#6612), otherwise zoom all the way
  48585. // out.
  48586. }
  48587. else if (((!isTouchDevice && zoomType === 'xy') ||
  48588. (isTouchDevice && pinchType === 'xy')) &&
  48589. axis.options.range) {
  48590. const previousZoom = navigatorAxis.previousZoom;
  48591. if (defined(e.newMin)) {
  48592. navigatorAxis.previousZoom = [axis.min, axis.max];
  48593. }
  48594. else if (previousZoom) {
  48595. e.newMin = previousZoom[0];
  48596. e.newMax = previousZoom[1];
  48597. navigatorAxis.previousZoom = void 0;
  48598. }
  48599. }
  48600. }
  48601. if (typeof e.zoomed !== 'undefined') {
  48602. e.preventDefault();
  48603. }
  48604. }
  48605. /* *
  48606. *
  48607. * Class
  48608. *
  48609. * */
  48610. /**
  48611. * @private
  48612. * @class
  48613. */
  48614. class NavigatorAxisAdditions {
  48615. /* *
  48616. *
  48617. * Static Functions
  48618. *
  48619. * */
  48620. /**
  48621. * @private
  48622. */
  48623. static compose(AxisClass) {
  48624. if (U.pushUnique(composedMembers, AxisClass)) {
  48625. AxisClass.keepProps.push('navigatorAxis');
  48626. addEvent(AxisClass, 'init', onAxisInit);
  48627. addEvent(AxisClass, 'zoom', onAxisZoom);
  48628. }
  48629. }
  48630. /* *
  48631. *
  48632. * Constructors
  48633. *
  48634. * */
  48635. constructor(axis) {
  48636. this.axis = axis;
  48637. }
  48638. /* *
  48639. *
  48640. * Functions
  48641. *
  48642. * */
  48643. /**
  48644. * @private
  48645. */
  48646. destroy() {
  48647. this.axis = void 0;
  48648. }
  48649. /**
  48650. * Add logic to normalize the zoomed range in order to preserve the pressed
  48651. * state of range selector buttons
  48652. *
  48653. * @private
  48654. * @function Highcharts.Axis#toFixedRange
  48655. */
  48656. toFixedRange(pxMin, pxMax, fixedMin, fixedMax) {
  48657. const axis = this.axis, chart = axis.chart;
  48658. let newMin = pick(fixedMin, axis.translate(pxMin, true, !axis.horiz)), newMax = pick(fixedMax, axis.translate(pxMax, true, !axis.horiz));
  48659. const fixedRange = chart && chart.fixedRange, halfPointRange = (axis.pointRange || 0) / 2;
  48660. // Add/remove half point range to/from the extremes (#1172)
  48661. if (!defined(fixedMin)) {
  48662. newMin = correctFloat(newMin + halfPointRange);
  48663. }
  48664. if (!defined(fixedMax)) {
  48665. newMax = correctFloat(newMax - halfPointRange);
  48666. }
  48667. // Make sure panning to the edges does not decrease the zoomed range
  48668. if (fixedRange && axis.dataMin && axis.dataMax) {
  48669. if (newMax >= axis.dataMax) {
  48670. newMin = correctFloat(axis.dataMax - fixedRange);
  48671. }
  48672. if (newMin <= axis.dataMin) {
  48673. newMax = correctFloat(axis.dataMin + fixedRange);
  48674. }
  48675. }
  48676. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  48677. newMin = newMax = void 0;
  48678. }
  48679. return {
  48680. min: newMin,
  48681. max: newMax
  48682. };
  48683. }
  48684. }
  48685. /* *
  48686. *
  48687. * Default Export
  48688. *
  48689. * */
  48690. return NavigatorAxisAdditions;
  48691. });
  48692. _registerModule(_modules, 'Stock/Navigator/NavigatorDefaults.js', [_modules['Core/Color/Color.js'], _modules['Core/Series/SeriesRegistry.js']], function (Color, SeriesRegistry) {
  48693. /* *
  48694. *
  48695. * (c) 2010-2021 Torstein Honsi
  48696. *
  48697. * License: www.highcharts.com/license
  48698. *
  48699. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48700. *
  48701. * */
  48702. const { parse: color } = Color;
  48703. const { seriesTypes } = SeriesRegistry;
  48704. /* *
  48705. *
  48706. * Constants
  48707. *
  48708. * */
  48709. /**
  48710. * The navigator is a small series below the main series, displaying
  48711. * a view of the entire data set. It provides tools to zoom in and
  48712. * out on parts of the data as well as panning across the dataset.
  48713. *
  48714. * @product highstock gantt
  48715. * @optionparent navigator
  48716. */
  48717. const NavigatorDefaults = {
  48718. /**
  48719. * Whether the navigator and scrollbar should adapt to updated data
  48720. * in the base X axis. When loading data async, as in the demo below,
  48721. * this should be `false`. Otherwise new data will trigger navigator
  48722. * redraw, which will cause unwanted looping. In the demo below, the
  48723. * data in the navigator is set only once. On navigating, only the main
  48724. * chart content is updated.
  48725. *
  48726. * @sample {highstock} stock/demo/lazy-loading/
  48727. * Set to false with async data loading
  48728. *
  48729. * @type {boolean}
  48730. * @default true
  48731. * @apioption navigator.adaptToUpdatedData
  48732. */
  48733. /**
  48734. * An integer identifying the index to use for the base series, or a
  48735. * string representing the id of the series.
  48736. *
  48737. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  48738. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  48739. *
  48740. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  48741. *
  48742. * @deprecated
  48743. * @type {number|string}
  48744. * @default 0
  48745. * @apioption navigator.baseSeries
  48746. */
  48747. /**
  48748. * Enable or disable the navigator.
  48749. *
  48750. * @sample {highstock} stock/navigator/enabled/
  48751. * Disable the navigator
  48752. *
  48753. * @type {boolean}
  48754. * @default true
  48755. * @apioption navigator.enabled
  48756. */
  48757. /**
  48758. * When the chart is inverted, whether to draw the navigator on the
  48759. * opposite side.
  48760. *
  48761. * @type {boolean}
  48762. * @default false
  48763. * @since 5.0.8
  48764. * @apioption navigator.opposite
  48765. */
  48766. /**
  48767. * The height of the navigator.
  48768. *
  48769. * @sample {highstock} stock/navigator/height/
  48770. * A higher navigator
  48771. */
  48772. height: 40,
  48773. /**
  48774. * The distance from the nearest element, the X axis or X axis labels.
  48775. *
  48776. * @sample {highstock} stock/navigator/margin/
  48777. * A margin of 2 draws the navigator closer to the X axis labels
  48778. */
  48779. margin: 25,
  48780. /**
  48781. * Whether the mask should be inside the range marking the zoomed
  48782. * range, or outside. In Highcharts Stock 1.x it was always `false`.
  48783. *
  48784. * @sample {highstock} stock/navigator/maskinside-false/
  48785. * False, mask outside
  48786. *
  48787. * @since 2.0
  48788. */
  48789. maskInside: true,
  48790. /**
  48791. * Options for the handles for dragging the zoomed area.
  48792. *
  48793. * @sample {highstock} stock/navigator/handles/
  48794. * Colored handles
  48795. */
  48796. handles: {
  48797. /**
  48798. * Width for handles.
  48799. *
  48800. * @sample {highstock} stock/navigator/styled-handles/
  48801. * Styled handles
  48802. *
  48803. * @since 6.0.0
  48804. */
  48805. width: 7,
  48806. /**
  48807. * Height for handles.
  48808. *
  48809. * @sample {highstock} stock/navigator/styled-handles/
  48810. * Styled handles
  48811. *
  48812. * @since 6.0.0
  48813. */
  48814. height: 15,
  48815. /**
  48816. * Array to define shapes of handles. 0-index for left, 1-index for
  48817. * right.
  48818. *
  48819. * Additionally, the URL to a graphic can be given on this form:
  48820. * `url(graphic.png)`. Note that for the image to be applied to
  48821. * exported charts, its URL needs to be accessible by the export
  48822. * server.
  48823. *
  48824. * Custom callbacks for symbol path generation can also be added to
  48825. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  48826. * used by its method name, as shown in the demo.
  48827. *
  48828. * @sample {highstock} stock/navigator/styled-handles/
  48829. * Styled handles
  48830. *
  48831. * @type {Array<string>}
  48832. * @default ["navigator-handle", "navigator-handle"]
  48833. * @since 6.0.0
  48834. */
  48835. symbols: ['navigator-handle', 'navigator-handle'],
  48836. /**
  48837. * Allows to enable/disable handles.
  48838. *
  48839. * @since 6.0.0
  48840. */
  48841. enabled: true,
  48842. /**
  48843. * The width for the handle border and the stripes inside.
  48844. *
  48845. * @sample {highstock} stock/navigator/styled-handles/
  48846. * Styled handles
  48847. *
  48848. * @since 6.0.0
  48849. * @apioption navigator.handles.lineWidth
  48850. */
  48851. lineWidth: 1,
  48852. /**
  48853. * The fill for the handle.
  48854. *
  48855. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48856. */
  48857. backgroundColor: "#f2f2f2" /* Palette.neutralColor5 */,
  48858. /**
  48859. * The stroke for the handle border and the stripes inside.
  48860. *
  48861. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48862. */
  48863. borderColor: "#999999" /* Palette.neutralColor40 */
  48864. },
  48865. /**
  48866. * The color of the mask covering the areas of the navigator series
  48867. * that are currently not visible in the main series. The default
  48868. * color is bluish with an opacity of 0.3 to see the series below.
  48869. *
  48870. * @see In styled mode, the mask is styled with the
  48871. * `.highcharts-navigator-mask` and
  48872. * `.highcharts-navigator-mask-inside` classes.
  48873. *
  48874. * @sample {highstock} stock/navigator/maskfill/
  48875. * Blue, semi transparent mask
  48876. *
  48877. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48878. * @default rgba(102,133,194,0.3)
  48879. */
  48880. maskFill: color("#667aff" /* Palette.highlightColor60 */).setOpacity(0.3).get(),
  48881. /**
  48882. * The color of the line marking the currently zoomed area in the
  48883. * navigator.
  48884. *
  48885. * @sample {highstock} stock/navigator/outline/
  48886. * 2px blue outline
  48887. *
  48888. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48889. * @default #cccccc
  48890. */
  48891. outlineColor: "#999999" /* Palette.neutralColor40 */,
  48892. /**
  48893. * The width of the line marking the currently zoomed area in the
  48894. * navigator.
  48895. *
  48896. * @see In styled mode, the outline stroke width is set with the
  48897. * `.highcharts-navigator-outline` class.
  48898. *
  48899. * @sample {highstock} stock/navigator/outline/
  48900. * 2px blue outline
  48901. *
  48902. * @type {number}
  48903. */
  48904. outlineWidth: 1,
  48905. /**
  48906. * Options for the navigator series. Available options are the same
  48907. * as any series, documented at [plotOptions](#plotOptions.series)
  48908. * and [series](#series).
  48909. *
  48910. * Unless data is explicitly defined on navigator.series, the data
  48911. * is borrowed from the first series in the chart.
  48912. *
  48913. * Default series options for the navigator series are:
  48914. * ```js
  48915. * series: {
  48916. * type: 'areaspline',
  48917. * fillOpacity: 0.05,
  48918. * dataGrouping: {
  48919. * smoothed: true
  48920. * },
  48921. * lineWidth: 1,
  48922. * marker: {
  48923. * enabled: false
  48924. * }
  48925. * }
  48926. * ```
  48927. *
  48928. * @see In styled mode, the navigator series is styled with the
  48929. * `.highcharts-navigator-series` class.
  48930. *
  48931. * @sample {highstock} stock/navigator/series-data/
  48932. * Using a separate data set for the navigator
  48933. * @sample {highstock} stock/navigator/series/
  48934. * A green navigator series
  48935. *
  48936. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  48937. */
  48938. series: {
  48939. /**
  48940. * The type of the navigator series.
  48941. *
  48942. * Heads up:
  48943. * In column-type navigator, zooming is limited to at least one
  48944. * point with its `pointRange`.
  48945. *
  48946. * @sample {highstock} stock/navigator/column/
  48947. * Column type navigator
  48948. *
  48949. * @type {string}
  48950. * @default {highstock} `areaspline` if defined, otherwise `line`
  48951. * @default {gantt} gantt
  48952. */
  48953. type: (typeof seriesTypes.areaspline === 'undefined' ?
  48954. 'line' :
  48955. 'areaspline'),
  48956. /**
  48957. * The fill opacity of the navigator series.
  48958. */
  48959. fillOpacity: 0.05,
  48960. /**
  48961. * The pixel line width of the navigator series.
  48962. */
  48963. lineWidth: 1,
  48964. /**
  48965. * @ignore-option
  48966. */
  48967. compare: null,
  48968. /**
  48969. * @ignore-option
  48970. */
  48971. sonification: {
  48972. enabled: false
  48973. },
  48974. /**
  48975. * Unless data is explicitly defined, the data is borrowed from the
  48976. * first series in the chart.
  48977. *
  48978. * @type {Array<number|Array<number|string|null>|object|null>}
  48979. * @product highstock
  48980. * @apioption navigator.series.data
  48981. */
  48982. /**
  48983. * Data grouping options for the navigator series.
  48984. *
  48985. * @extends plotOptions.series.dataGrouping
  48986. */
  48987. dataGrouping: {
  48988. approximation: 'average',
  48989. enabled: true,
  48990. groupPixelWidth: 2,
  48991. // Replace smoothed property by anchors, #12455.
  48992. firstAnchor: 'firstPoint',
  48993. anchor: 'middle',
  48994. lastAnchor: 'lastPoint',
  48995. // Day and week differs from plotOptions.series.dataGrouping
  48996. units: [
  48997. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  48998. ['second', [1, 2, 5, 10, 15, 30]],
  48999. ['minute', [1, 2, 5, 10, 15, 30]],
  49000. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  49001. ['day', [1, 2, 3, 4]],
  49002. ['week', [1, 2, 3]],
  49003. ['month', [1, 3, 6]],
  49004. ['year', null]
  49005. ]
  49006. },
  49007. /**
  49008. * Data label options for the navigator series. Data labels are
  49009. * disabled by default on the navigator series.
  49010. *
  49011. * @extends plotOptions.series.dataLabels
  49012. */
  49013. dataLabels: {
  49014. enabled: false,
  49015. zIndex: 2 // #1839
  49016. },
  49017. id: 'highcharts-navigator-series',
  49018. className: 'highcharts-navigator-series',
  49019. /**
  49020. * Sets the fill color of the navigator series.
  49021. *
  49022. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49023. * @apioption navigator.series.color
  49024. */
  49025. /**
  49026. * Line color for the navigator series. Allows setting the color
  49027. * while disallowing the default candlestick setting.
  49028. *
  49029. * @type {Highcharts.ColorString|null}
  49030. */
  49031. lineColor: null,
  49032. marker: {
  49033. enabled: false
  49034. },
  49035. /**
  49036. * Since Highcharts Stock v8, default value is the same as default
  49037. * `pointRange` defined for a specific type (e.g. `null` for
  49038. * column type).
  49039. *
  49040. * In Highcharts Stock version < 8, defaults to 0.
  49041. *
  49042. * @extends plotOptions.series.pointRange
  49043. * @type {number|null}
  49044. * @apioption navigator.series.pointRange
  49045. */
  49046. /**
  49047. * The threshold option. Setting it to 0 will make the default
  49048. * navigator area series draw its area from the 0 value and up.
  49049. *
  49050. * @type {number|null}
  49051. */
  49052. threshold: null
  49053. },
  49054. /**
  49055. * Enable or disable navigator sticking to right, while adding new
  49056. * points. If `undefined`, the navigator sticks to the axis maximum only
  49057. * if it was already at the maximum prior to adding points.
  49058. *
  49059. * @type {boolean}
  49060. * @default undefined
  49061. * @since 10.2.1
  49062. * @sample {highstock} stock/navigator/sticktomax-false/
  49063. * stickToMax set to false
  49064. * @apioption navigator.stickToMax
  49065. */
  49066. /**
  49067. * Options for the navigator X axis. Default series options for the
  49068. * navigator xAxis are:
  49069. * ```js
  49070. * xAxis: {
  49071. * tickWidth: 0,
  49072. * lineWidth: 0,
  49073. * gridLineWidth: 1,
  49074. * tickPixelInterval: 200,
  49075. * labels: {
  49076. * align: 'left',
  49077. * style: {
  49078. * color: '#888'
  49079. * },
  49080. * x: 3,
  49081. * y: -4
  49082. * }
  49083. * }
  49084. * ```
  49085. *
  49086. * @extends xAxis
  49087. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  49088. * showEmpty, maxRange
  49089. */
  49090. xAxis: {
  49091. /**
  49092. * Additional range on the right side of the xAxis. Works similar to
  49093. * xAxis.maxPadding, but value is set in milliseconds.
  49094. * Can be set for both, main xAxis and navigator's xAxis.
  49095. *
  49096. * @since 6.0.0
  49097. */
  49098. overscroll: 0,
  49099. className: 'highcharts-navigator-xaxis',
  49100. tickLength: 0,
  49101. lineWidth: 0,
  49102. gridLineColor: "#e6e6e6" /* Palette.neutralColor10 */,
  49103. gridLineWidth: 1,
  49104. tickPixelInterval: 200,
  49105. labels: {
  49106. align: 'left',
  49107. /**
  49108. * @type {Highcharts.CSSObject}
  49109. */
  49110. style: {
  49111. /** @ignore */
  49112. color: "#000000" /* Palette.neutralColor100 */,
  49113. /** @ignore */
  49114. fontSize: '0.7em',
  49115. /** @ignore */
  49116. opacity: 0.6,
  49117. /** @ignore */
  49118. textOutline: '2px contrast'
  49119. },
  49120. x: 3,
  49121. y: -4
  49122. },
  49123. crosshair: false
  49124. },
  49125. /**
  49126. * Options for the navigator Y axis. Default series options for the
  49127. * navigator yAxis are:
  49128. * ```js
  49129. * yAxis: {
  49130. * gridLineWidth: 0,
  49131. * startOnTick: false,
  49132. * endOnTick: false,
  49133. * minPadding: 0.1,
  49134. * maxPadding: 0.1,
  49135. * labels: {
  49136. * enabled: false
  49137. * },
  49138. * title: {
  49139. * text: null
  49140. * },
  49141. * tickWidth: 0
  49142. * }
  49143. * ```
  49144. *
  49145. * @extends yAxis
  49146. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  49147. * showEmpty, scrollbar, top, units, maxRange, minLength,
  49148. * maxLength, resize
  49149. */
  49150. yAxis: {
  49151. className: 'highcharts-navigator-yaxis',
  49152. gridLineWidth: 0,
  49153. startOnTick: false,
  49154. endOnTick: false,
  49155. minPadding: 0.1,
  49156. maxPadding: 0.1,
  49157. labels: {
  49158. enabled: false
  49159. },
  49160. crosshair: false,
  49161. title: {
  49162. text: null
  49163. },
  49164. tickLength: 0,
  49165. tickWidth: 0
  49166. }
  49167. };
  49168. /* *
  49169. *
  49170. * Default Export
  49171. *
  49172. * */
  49173. /* *
  49174. *
  49175. * API Options
  49176. *
  49177. * */
  49178. /**
  49179. * Maximum range which can be set using the navigator's handles.
  49180. * Opposite of [xAxis.minRange](#xAxis.minRange).
  49181. *
  49182. * @sample {highstock} stock/navigator/maxrange/
  49183. * Defined max and min range
  49184. *
  49185. * @type {number}
  49186. * @since 6.0.0
  49187. * @product highstock gantt
  49188. * @apioption xAxis.maxRange
  49189. */
  49190. (''); // keeps doclets above in JS file
  49191. return NavigatorDefaults;
  49192. });
  49193. _registerModule(_modules, 'Stock/Navigator/NavigatorSymbols.js', [], function () {
  49194. /* *
  49195. *
  49196. * (c) 2010-2021 Torstein Honsi
  49197. *
  49198. * License: www.highcharts.com/license
  49199. *
  49200. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49201. *
  49202. * */
  49203. /* *
  49204. *
  49205. * Constants
  49206. *
  49207. * */
  49208. /**
  49209. * Draw one of the handles on the side of the zoomed range in the navigator.
  49210. * @private
  49211. */
  49212. function navigatorHandle(_x, _y, width, height, options = {}) {
  49213. const halfWidth = options.width ? options.width / 2 : width, markerPosition = Math.round(halfWidth / 3) + 0.5;
  49214. height = options.height || height;
  49215. return [
  49216. ['M', -halfWidth - 1, 0.5],
  49217. ['L', halfWidth, 0.5],
  49218. ['L', halfWidth, height + 0.5],
  49219. ['L', -halfWidth - 1, height + 0.5],
  49220. ['L', -halfWidth - 1, 0.5],
  49221. ['M', -markerPosition, 4],
  49222. ['L', -markerPosition, height - 3],
  49223. ['M', markerPosition - 1, 4],
  49224. ['L', markerPosition - 1, height - 3]
  49225. ];
  49226. }
  49227. /* *
  49228. *
  49229. * Default Export
  49230. *
  49231. * */
  49232. const NavigatorSymbols = {
  49233. 'navigator-handle': navigatorHandle
  49234. };
  49235. return NavigatorSymbols;
  49236. });
  49237. _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) {
  49238. /* *
  49239. *
  49240. * (c) 2010-2021 Torstein Honsi
  49241. *
  49242. * License: www.highcharts.com/license
  49243. *
  49244. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49245. *
  49246. * */
  49247. const { defaultOptions, setOptions } = D;
  49248. const { isTouchDevice } = H;
  49249. const { getRendererType } = RendererRegistry;
  49250. const { addEvent, extend, merge, pick } = U;
  49251. /* *
  49252. *
  49253. * Constants
  49254. *
  49255. * */
  49256. const composedMembers = [];
  49257. /* *
  49258. *
  49259. * Variables
  49260. *
  49261. * */
  49262. let NavigatorConstructor;
  49263. /* *
  49264. *
  49265. * Functions
  49266. *
  49267. * */
  49268. /**
  49269. * @private
  49270. */
  49271. function compose(AxisClass, ChartClass, NavigatorClass, SeriesClass) {
  49272. NavigatorAxisAdditions.compose(AxisClass);
  49273. NavigatorConstructor = NavigatorClass;
  49274. if (U.pushUnique(composedMembers, ChartClass)) {
  49275. const chartProto = ChartClass.prototype;
  49276. chartProto.callbacks.push(onChartCallback);
  49277. addEvent(ChartClass, 'afterAddSeries', onChartAfterAddSeries);
  49278. addEvent(ChartClass, 'afterSetChartSize', onChartAfterSetChartSize);
  49279. addEvent(ChartClass, 'afterUpdate', onChartAfterUpdate);
  49280. addEvent(ChartClass, 'beforeRender', onChartBeforeRender);
  49281. addEvent(ChartClass, 'beforeShowResetZoom', onChartBeforeShowResetZoom);
  49282. addEvent(ChartClass, 'update', onChartUpdate);
  49283. }
  49284. if (U.pushUnique(composedMembers, SeriesClass)) {
  49285. addEvent(SeriesClass, 'afterUpdate', onSeriesAfterUpdate);
  49286. }
  49287. if (U.pushUnique(composedMembers, getRendererType)) {
  49288. extend(getRendererType().prototype.symbols, NavigatorSymbols);
  49289. }
  49290. if (U.pushUnique(composedMembers, setOptions)) {
  49291. extend(defaultOptions, { navigator: NavigatorDefaults });
  49292. }
  49293. }
  49294. /**
  49295. * Handle adding new series.
  49296. * @private
  49297. */
  49298. function onChartAfterAddSeries() {
  49299. if (this.navigator) {
  49300. // Recompute which series should be shown in navigator, and add them
  49301. this.navigator.setBaseSeries(null, false);
  49302. }
  49303. }
  49304. /**
  49305. * For stock charts, extend the Chart.setChartSize method so that we can set the
  49306. * final top position of the navigator once the height of the chart, including
  49307. * the legend, is determined. #367. We can't use Chart.getMargins, because
  49308. * labels offsets are not calculated yet.
  49309. * @private
  49310. */
  49311. function onChartAfterSetChartSize() {
  49312. var _a;
  49313. const legend = this.legend, navigator = this.navigator;
  49314. let legendOptions, xAxis, yAxis;
  49315. if (navigator) {
  49316. legendOptions = legend && legend.options;
  49317. xAxis = navigator.xAxis;
  49318. yAxis = navigator.yAxis;
  49319. const { scrollbarHeight, scrollButtonSize } = navigator;
  49320. // Compute the top position
  49321. if (this.inverted) {
  49322. navigator.left = navigator.opposite ?
  49323. this.chartWidth - scrollbarHeight -
  49324. navigator.height :
  49325. this.spacing[3] + scrollbarHeight;
  49326. navigator.top = this.plotTop + scrollButtonSize;
  49327. }
  49328. else {
  49329. navigator.left = pick(xAxis.left, this.plotLeft + scrollButtonSize);
  49330. navigator.top = navigator.navigatorOptions.top ||
  49331. this.chartHeight -
  49332. navigator.height -
  49333. scrollbarHeight -
  49334. (((_a = this.scrollbar) === null || _a === void 0 ? void 0 : _a.options.margin) || 0) -
  49335. this.spacing[2] -
  49336. (this.rangeSelector && this.extraBottomMargin ?
  49337. this.rangeSelector.getHeight() :
  49338. 0) -
  49339. ((legendOptions &&
  49340. legendOptions.verticalAlign === 'bottom' &&
  49341. legendOptions.layout !== 'proximate' && // #13392
  49342. legendOptions.enabled &&
  49343. !legendOptions.floating) ?
  49344. legend.legendHeight +
  49345. pick(legendOptions.margin, 10) :
  49346. 0) -
  49347. (this.titleOffset ? this.titleOffset[2] : 0);
  49348. }
  49349. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  49350. if (this.inverted) {
  49351. xAxis.options.left = yAxis.options.left = navigator.left;
  49352. }
  49353. else {
  49354. xAxis.options.top = yAxis.options.top = navigator.top;
  49355. }
  49356. xAxis.setAxisSize();
  49357. yAxis.setAxisSize();
  49358. }
  49359. }
  49360. }
  49361. /**
  49362. * Initialize navigator, if no scrolling exists yet.
  49363. * @private
  49364. */
  49365. function onChartAfterUpdate(event) {
  49366. if (!this.navigator && !this.scroller &&
  49367. (this.options.navigator.enabled ||
  49368. this.options.scrollbar.enabled)) {
  49369. this.scroller = this.navigator = new NavigatorConstructor(this);
  49370. if (pick(event.redraw, true)) {
  49371. this.redraw(event.animation); // #7067
  49372. }
  49373. }
  49374. }
  49375. /**
  49376. * Initialize navigator for stock charts
  49377. * @private
  49378. */
  49379. function onChartBeforeRender() {
  49380. const options = this.options;
  49381. if (options.navigator.enabled ||
  49382. options.scrollbar.enabled) {
  49383. this.scroller = this.navigator = new NavigatorConstructor(this);
  49384. }
  49385. }
  49386. /**
  49387. * For Stock charts. For x only zooming, do not to create the zoom button
  49388. * because X axis zooming is already allowed by the Navigator and Range
  49389. * selector. (#9285)
  49390. * @private
  49391. */
  49392. function onChartBeforeShowResetZoom() {
  49393. const chartOptions = this.options, navigator = chartOptions.navigator, rangeSelector = chartOptions.rangeSelector;
  49394. if (((navigator && navigator.enabled) ||
  49395. (rangeSelector && rangeSelector.enabled)) &&
  49396. ((!isTouchDevice &&
  49397. this.zooming.type === 'x') ||
  49398. (isTouchDevice && this.zooming.pinchType === 'x'))) {
  49399. return false;
  49400. }
  49401. }
  49402. /**
  49403. * @private
  49404. */
  49405. function onChartCallback(chart) {
  49406. const navigator = chart.navigator;
  49407. // Initialize the navigator
  49408. if (navigator && chart.xAxis[0]) {
  49409. const extremes = chart.xAxis[0].getExtremes();
  49410. navigator.render(extremes.min, extremes.max);
  49411. }
  49412. }
  49413. /**
  49414. * Merge options, if no scrolling exists yet
  49415. * @private
  49416. */
  49417. function onChartUpdate(e) {
  49418. const navigatorOptions = (e.options.navigator || {}), scrollbarOptions = (e.options.scrollbar || {});
  49419. if (!this.navigator && !this.scroller &&
  49420. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  49421. merge(true, this.options.navigator, navigatorOptions);
  49422. merge(true, this.options.scrollbar, scrollbarOptions);
  49423. delete e.options.navigator;
  49424. delete e.options.scrollbar;
  49425. }
  49426. }
  49427. /**
  49428. * Handle updating series
  49429. * @private
  49430. */
  49431. function onSeriesAfterUpdate() {
  49432. if (this.chart.navigator && !this.options.isInternal) {
  49433. this.chart.navigator.setBaseSeries(null, false);
  49434. }
  49435. }
  49436. /* *
  49437. *
  49438. * Default Export
  49439. *
  49440. * */
  49441. const NavigatorComposition = {
  49442. compose
  49443. };
  49444. return NavigatorComposition;
  49445. });
  49446. _registerModule(_modules, 'Core/Axis/ScrollbarAxis.js', [_modules['Core/Utilities.js']], function (U) {
  49447. /* *
  49448. *
  49449. * (c) 2010-2021 Torstein Honsi
  49450. *
  49451. * License: www.highcharts.com/license
  49452. *
  49453. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49454. *
  49455. * */
  49456. const { addEvent, defined, pick } = U;
  49457. /* *
  49458. *
  49459. * Constants
  49460. *
  49461. * */
  49462. const composedMembers = [];
  49463. /* *
  49464. *
  49465. * Composition
  49466. *
  49467. * */
  49468. /* eslint-disable no-invalid-this, valid-jsdoc */
  49469. /**
  49470. * Creates scrollbars if enabled.
  49471. * @private
  49472. */
  49473. class ScrollbarAxis {
  49474. /* *
  49475. *
  49476. * Static Properties
  49477. *
  49478. * */
  49479. /**
  49480. * Attaches to axis events to create scrollbars if enabled.
  49481. *
  49482. * @private
  49483. *
  49484. * @param AxisClass
  49485. * Axis class to extend.
  49486. *
  49487. * @param ScrollbarClass
  49488. * Scrollbar class to use.
  49489. */
  49490. static compose(AxisClass, ScrollbarClass) {
  49491. if (!U.pushUnique(composedMembers, AxisClass)) {
  49492. return AxisClass;
  49493. }
  49494. const getExtremes = (axis) => {
  49495. const axisMin = pick(axis.options && axis.options.min, axis.min);
  49496. const axisMax = pick(axis.options && axis.options.max, axis.max);
  49497. return {
  49498. axisMin,
  49499. axisMax,
  49500. scrollMin: defined(axis.dataMin) ?
  49501. Math.min(axisMin, axis.min, axis.dataMin, pick(axis.threshold, Infinity)) : axisMin,
  49502. scrollMax: defined(axis.dataMax) ?
  49503. Math.max(axisMax, axis.max, axis.dataMax, pick(axis.threshold, -Infinity)) : axisMax
  49504. };
  49505. };
  49506. // Wrap axis initialization and create scrollbar if enabled:
  49507. addEvent(AxisClass, 'afterInit', function () {
  49508. const axis = this;
  49509. if (axis.options &&
  49510. axis.options.scrollbar &&
  49511. axis.options.scrollbar.enabled) {
  49512. // Predefined options:
  49513. axis.options.scrollbar.vertical = !axis.horiz;
  49514. axis.options.startOnTick = axis.options.endOnTick = false;
  49515. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  49516. addEvent(axis.scrollbar, 'changed', function (e) {
  49517. let { axisMin, axisMax, scrollMin: unitedMin, scrollMax: unitedMax } = getExtremes(axis), range = unitedMax - unitedMin, to, from;
  49518. // #12834, scroll when show/hide series, wrong extremes
  49519. if (!defined(axisMin) || !defined(axisMax)) {
  49520. return;
  49521. }
  49522. if ((axis.horiz && !axis.reversed) ||
  49523. (!axis.horiz && axis.reversed)) {
  49524. to = unitedMin + range * this.to;
  49525. from = unitedMin + range * this.from;
  49526. }
  49527. else {
  49528. // y-values in browser are reversed, but this also
  49529. // applies for reversed horizontal axis:
  49530. to = unitedMin + range * (1 - this.from);
  49531. from = unitedMin + range * (1 - this.to);
  49532. }
  49533. if (this.shouldUpdateExtremes(e.DOMType)) {
  49534. // #17977, set animation to undefined instead of true
  49535. const animate = e.DOMType === 'mousemove' ||
  49536. e.DOMType === 'touchmove' ? false : void 0;
  49537. axis.setExtremes(from, to, true, animate, e);
  49538. }
  49539. else {
  49540. // When live redraw is disabled, don't change extremes
  49541. // Only change the position of the scollbar thumb
  49542. this.setRange(this.from, this.to);
  49543. }
  49544. });
  49545. }
  49546. });
  49547. // Wrap rendering axis, and update scrollbar if one is created:
  49548. addEvent(AxisClass, 'afterRender', function () {
  49549. 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;
  49550. if (scrollbar) {
  49551. if (axis.horiz) {
  49552. // Reserve space for labels/title
  49553. if (!axis.opposite) {
  49554. scrollbarsOffsets[1] += offset;
  49555. }
  49556. scrollbar.position(axis.left, (axis.top +
  49557. axis.height +
  49558. 2 +
  49559. scrollbarsOffsets[1] -
  49560. (axis.opposite ? axisMargin : 0)), axis.width, axis.height);
  49561. // Next scrollbar should reserve space for margin (if set)
  49562. if (!axis.opposite) {
  49563. scrollbarsOffsets[1] += axisMargin;
  49564. }
  49565. offsetsIndex = 1;
  49566. }
  49567. else {
  49568. // Reserve space for labels/title
  49569. if (axis.opposite) {
  49570. scrollbarsOffsets[0] += offset;
  49571. }
  49572. let xPosition;
  49573. if (!scrollbar.options.opposite) {
  49574. xPosition = axis.opposite ? 0 : axisMargin;
  49575. }
  49576. else {
  49577. xPosition = axis.left +
  49578. axis.width +
  49579. 2 +
  49580. scrollbarsOffsets[0] -
  49581. (axis.opposite ? 0 : axisMargin);
  49582. }
  49583. scrollbar.position(xPosition, axis.top, axis.width, axis.height);
  49584. // Next scrollbar should reserve space for margin (if set)
  49585. if (axis.opposite) {
  49586. scrollbarsOffsets[0] += axisMargin;
  49587. }
  49588. offsetsIndex = 0;
  49589. }
  49590. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  49591. (scrollbar.options.margin || 0);
  49592. if (isNaN(scrollMin) ||
  49593. isNaN(scrollMax) ||
  49594. !defined(axis.min) ||
  49595. !defined(axis.max) ||
  49596. axis.min === axis.max // #10733
  49597. ) {
  49598. // default action: when extremes are the same or there is
  49599. // not extremes on the axis, but scrollbar exists, make it
  49600. // full size
  49601. scrollbar.setRange(0, 1);
  49602. }
  49603. else {
  49604. from = ((axis.min - scrollMin) /
  49605. (scrollMax - scrollMin));
  49606. to = ((axis.max - scrollMin) /
  49607. (scrollMax - scrollMin));
  49608. if ((axis.horiz && !axis.reversed) ||
  49609. (!axis.horiz && axis.reversed)) {
  49610. scrollbar.setRange(from, to);
  49611. }
  49612. else {
  49613. // inverse vertical axis
  49614. scrollbar.setRange(1 - to, 1 - from);
  49615. }
  49616. }
  49617. }
  49618. });
  49619. // Make space for a scrollbar:
  49620. addEvent(AxisClass, 'afterGetOffset', function () {
  49621. const axis = this, scrollbar = axis.scrollbar, opposite = scrollbar && !scrollbar.options.opposite, index = axis.horiz ? 2 : opposite ? 3 : 1;
  49622. if (scrollbar) {
  49623. // reset scrollbars offsets
  49624. axis.chart.scrollbarsOffsets = [0, 0];
  49625. axis.chart.axisOffset[index] +=
  49626. scrollbar.size + (scrollbar.options.margin || 0);
  49627. }
  49628. });
  49629. return AxisClass;
  49630. }
  49631. }
  49632. return ScrollbarAxis;
  49633. });
  49634. _registerModule(_modules, 'Stock/Scrollbar/ScrollbarDefaults.js', [_modules['Core/Globals.js']], function (H) {
  49635. /* *
  49636. *
  49637. * (c) 2010-2021 Torstein Honsi
  49638. *
  49639. * License: www.highcharts.com/license
  49640. *
  49641. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49642. *
  49643. * */
  49644. const { isTouchDevice } = H;
  49645. /* *
  49646. *
  49647. * Constant
  49648. *
  49649. * */
  49650. /**
  49651. *
  49652. * The scrollbar is a means of panning over the X axis of a stock chart.
  49653. * Scrollbars can also be applied to other types of axes.
  49654. *
  49655. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  49656. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  49657. * is especially suitable for simpler cartesian charts on mobile.
  49658. *
  49659. * In styled mode, all the presentational options for the
  49660. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  49661. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  49662. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  49663. *
  49664. * @sample stock/yaxis/inverted-bar-scrollbar/
  49665. * A scrollbar on a simple bar chart
  49666. *
  49667. * @product highstock gantt
  49668. * @optionparent scrollbar
  49669. *
  49670. * @private
  49671. */
  49672. const ScrollbarDefaults = {
  49673. /**
  49674. * The height of the scrollbar. If `buttonsEnabled` is true , the height
  49675. * also applies to the width of the scroll arrows so that they are always
  49676. * squares.
  49677. *
  49678. * @sample stock/scrollbar/style/
  49679. * Non-default height
  49680. *
  49681. * @type {number}
  49682. */
  49683. height: 10,
  49684. /**
  49685. * The border rounding radius of the bar.
  49686. *
  49687. * @sample stock/scrollbar/style/
  49688. * Scrollbar styling
  49689. */
  49690. barBorderRadius: 5,
  49691. /**
  49692. * The corner radius of the scrollbar buttons.
  49693. *
  49694. * @sample stock/scrollbar/style/
  49695. * Scrollbar styling
  49696. */
  49697. buttonBorderRadius: 0,
  49698. /**
  49699. * Enable or disable the buttons at the end of the scrollbar.
  49700. *
  49701. * @since 11.0.0
  49702. */
  49703. buttonsEnabled: false,
  49704. /**
  49705. * Enable or disable the scrollbar.
  49706. *
  49707. * @sample stock/scrollbar/enabled/
  49708. * Disable the scrollbar, only use navigator
  49709. *
  49710. * @type {boolean}
  49711. * @default true
  49712. * @apioption scrollbar.enabled
  49713. */
  49714. /**
  49715. * Whether to redraw the main chart as the scrollbar or the navigator
  49716. * zoomed window is moved. Defaults to `true` for modern browsers and
  49717. * `false` for legacy IE browsers as well as mobile devices.
  49718. *
  49719. * @sample stock/scrollbar/liveredraw
  49720. * Setting live redraw to false
  49721. *
  49722. * @type {boolean}
  49723. * @since 1.3
  49724. */
  49725. liveRedraw: void 0,
  49726. /**
  49727. * The margin between the scrollbar and its axis when the scrollbar is
  49728. * applied directly to an axis, or the navigator in case that is enabled.
  49729. * Defaults to 10 for axis, 0 for navigator.
  49730. *
  49731. * @type {number|undefined}
  49732. */
  49733. margin: void 0,
  49734. /**
  49735. * The minimum width of the scrollbar.
  49736. *
  49737. * @since 1.2.5
  49738. */
  49739. minWidth: 6,
  49740. /** @ignore-option */
  49741. opposite: true,
  49742. /**
  49743. * Whether to show or hide the scrollbar when the scrolled content is
  49744. * zoomed out to it full extent.
  49745. *
  49746. * @type {boolean}
  49747. * @default true
  49748. * @apioption scrollbar.showFull
  49749. */
  49750. step: 0.2,
  49751. /**
  49752. * The z index of the scrollbar group.
  49753. */
  49754. zIndex: 3,
  49755. /**
  49756. * The background color of the scrollbar itself.
  49757. *
  49758. * @sample stock/scrollbar/style/
  49759. * Scrollbar styling
  49760. *
  49761. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49762. */
  49763. barBackgroundColor: "#cccccc" /* Palette.neutralColor20 */,
  49764. /**
  49765. * The width of the bar's border.
  49766. *
  49767. * @sample stock/scrollbar/style/
  49768. * Scrollbar styling
  49769. */
  49770. barBorderWidth: 0,
  49771. /**
  49772. * The color of the scrollbar's border.
  49773. *
  49774. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49775. */
  49776. barBorderColor: "#cccccc" /* Palette.neutralColor20 */,
  49777. /**
  49778. * The color of the small arrow inside the scrollbar buttons.
  49779. *
  49780. * @sample stock/scrollbar/style/
  49781. * Scrollbar styling
  49782. *
  49783. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49784. */
  49785. buttonArrowColor: "#333333" /* Palette.neutralColor80 */,
  49786. /**
  49787. * The color of scrollbar buttons.
  49788. *
  49789. * @sample stock/scrollbar/style/
  49790. * Scrollbar styling
  49791. *
  49792. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49793. */
  49794. buttonBackgroundColor: "#e6e6e6" /* Palette.neutralColor10 */,
  49795. /**
  49796. * The color of the border of the scrollbar buttons.
  49797. *
  49798. * @sample stock/scrollbar/style/
  49799. * Scrollbar styling
  49800. *
  49801. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49802. */
  49803. buttonBorderColor: "#cccccc" /* Palette.neutralColor20 */,
  49804. /**
  49805. * The border width of the scrollbar buttons.
  49806. *
  49807. * @sample stock/scrollbar/style/
  49808. * Scrollbar styling
  49809. */
  49810. buttonBorderWidth: 1,
  49811. /**
  49812. * The color of the small rifles in the middle of the scrollbar.
  49813. *
  49814. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49815. */
  49816. rifleColor: 'none',
  49817. /**
  49818. * The color of the track background.
  49819. *
  49820. * @sample stock/scrollbar/style/
  49821. * Scrollbar styling
  49822. *
  49823. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49824. */
  49825. trackBackgroundColor: 'rgba(255, 255, 255, 0.001)',
  49826. /**
  49827. * The color of the border of the scrollbar track.
  49828. *
  49829. * @sample stock/scrollbar/style/
  49830. * Scrollbar styling
  49831. *
  49832. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  49833. */
  49834. trackBorderColor: "#cccccc" /* Palette.neutralColor20 */,
  49835. /**
  49836. * The corner radius of the border of the scrollbar track.
  49837. *
  49838. * @sample stock/scrollbar/style/
  49839. * Scrollbar styling
  49840. */
  49841. trackBorderRadius: 5,
  49842. /**
  49843. * The width of the border of the scrollbar track.
  49844. *
  49845. * @sample stock/scrollbar/style/
  49846. * Scrollbar styling
  49847. */
  49848. trackBorderWidth: 1
  49849. };
  49850. /* *
  49851. *
  49852. * Default Export
  49853. *
  49854. * */
  49855. return ScrollbarDefaults;
  49856. });
  49857. _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) {
  49858. /* *
  49859. *
  49860. * (c) 2010-2021 Torstein Honsi
  49861. *
  49862. * License: www.highcharts.com/license
  49863. *
  49864. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  49865. *
  49866. * */
  49867. const { defaultOptions } = D;
  49868. const { addEvent, correctFloat, defined, destroyObjectProperties, fireEvent, merge, pick, removeEvent } = U;
  49869. /* *
  49870. *
  49871. * Constants
  49872. *
  49873. * */
  49874. /* eslint-disable no-invalid-this, valid-jsdoc */
  49875. /**
  49876. * A reusable scrollbar, internally used in Highcharts Stock's
  49877. * navigator and optionally on individual axes.
  49878. *
  49879. * @private
  49880. * @class
  49881. * @name Highcharts.Scrollbar
  49882. * @param {Highcharts.SVGRenderer} renderer
  49883. * @param {Highcharts.ScrollbarOptions} options
  49884. * @param {Highcharts.Chart} chart
  49885. */
  49886. class Scrollbar {
  49887. /* *
  49888. *
  49889. * Static Functions
  49890. *
  49891. * */
  49892. static compose(AxisClass) {
  49893. ScrollbarAxis.compose(AxisClass, Scrollbar);
  49894. }
  49895. /**
  49896. * When we have vertical scrollbar, rifles and arrow in buttons should be
  49897. * rotated. The same method is used in Navigator's handles, to rotate them.
  49898. *
  49899. * @function Highcharts.swapXY
  49900. *
  49901. * @param {Highcharts.SVGPathArray} path
  49902. * Path to be rotated.
  49903. *
  49904. * @param {boolean} [vertical]
  49905. * If vertical scrollbar, swap x-y values.
  49906. *
  49907. * @return {Highcharts.SVGPathArray}
  49908. * Rotated path.
  49909. *
  49910. * @requires modules/stock
  49911. */
  49912. static swapXY(path, vertical) {
  49913. if (vertical) {
  49914. path.forEach((seg) => {
  49915. const len = seg.length;
  49916. let temp;
  49917. for (let i = 0; i < len; i += 2) {
  49918. temp = seg[i + 1];
  49919. if (typeof temp === 'number') {
  49920. seg[i + 1] = seg[i + 2];
  49921. seg[i + 2] = temp;
  49922. }
  49923. }
  49924. });
  49925. }
  49926. return path;
  49927. }
  49928. /* *
  49929. *
  49930. * Constructors
  49931. *
  49932. * */
  49933. constructor(renderer, options, chart) {
  49934. /* *
  49935. *
  49936. * Properties
  49937. *
  49938. * */
  49939. this._events = [];
  49940. this.chart = void 0;
  49941. this.chartX = 0;
  49942. this.chartY = 0;
  49943. this.from = 0;
  49944. this.group = void 0;
  49945. this.options = void 0;
  49946. this.renderer = void 0;
  49947. this.scrollbar = void 0;
  49948. this.scrollbarButtons = [];
  49949. this.scrollbarGroup = void 0;
  49950. this.scrollbarLeft = 0;
  49951. this.scrollbarRifles = void 0;
  49952. this.scrollbarStrokeWidth = 1;
  49953. this.scrollbarTop = 0;
  49954. this.size = 0;
  49955. this.to = 0;
  49956. this.track = void 0;
  49957. this.trackBorderWidth = 1;
  49958. this.userOptions = void 0;
  49959. this.x = 0;
  49960. this.y = 0;
  49961. this.init(renderer, options, chart);
  49962. }
  49963. /* *
  49964. *
  49965. * Functions
  49966. *
  49967. * */
  49968. /**
  49969. * Set up the mouse and touch events for the Scrollbar
  49970. *
  49971. * @private
  49972. * @function Highcharts.Scrollbar#addEvents
  49973. */
  49974. addEvents() {
  49975. 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);
  49976. // Mouse events
  49977. const _events = [
  49978. [
  49979. buttons[buttonsOrder[0]].element,
  49980. 'click',
  49981. this.buttonToMinClick.bind(this)
  49982. ],
  49983. [
  49984. buttons[buttonsOrder[1]].element,
  49985. 'click',
  49986. this.buttonToMaxClick.bind(this)
  49987. ],
  49988. [track, 'click', this.trackClick.bind(this)],
  49989. [bar, 'mousedown', mouseDownHandler],
  49990. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  49991. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  49992. ];
  49993. // Touch events
  49994. if (H.hasTouch) {
  49995. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  49996. }
  49997. // Add them all
  49998. _events.forEach(function (args) {
  49999. addEvent.apply(null, args);
  50000. });
  50001. this._events = _events;
  50002. }
  50003. buttonToMaxClick(e) {
  50004. const scroller = this;
  50005. const range = ((scroller.to - scroller.from) *
  50006. pick(scroller.options.step, 0.2));
  50007. scroller.updatePosition(scroller.from + range, scroller.to + range);
  50008. fireEvent(scroller, 'changed', {
  50009. from: scroller.from,
  50010. to: scroller.to,
  50011. trigger: 'scrollbar',
  50012. DOMEvent: e
  50013. });
  50014. }
  50015. buttonToMinClick(e) {
  50016. const scroller = this;
  50017. const range = correctFloat(scroller.to - scroller.from) *
  50018. pick(scroller.options.step, 0.2);
  50019. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  50020. fireEvent(scroller, 'changed', {
  50021. from: scroller.from,
  50022. to: scroller.to,
  50023. trigger: 'scrollbar',
  50024. DOMEvent: e
  50025. });
  50026. }
  50027. /**
  50028. * Get normalized (0-1) cursor position over the scrollbar
  50029. *
  50030. * @private
  50031. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  50032. *
  50033. * @param {*} normalizedEvent
  50034. * normalized event, with chartX and chartY values
  50035. *
  50036. * @return {Highcharts.Dictionary<number>}
  50037. * Local position {chartX, chartY}
  50038. */
  50039. cursorToScrollbarPosition(normalizedEvent) {
  50040. const scroller = this, options = scroller.options, minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  50041. options.minWidth :
  50042. 0; // minWidth distorts translation
  50043. return {
  50044. chartX: (normalizedEvent.chartX - scroller.x -
  50045. scroller.xOffset) /
  50046. (scroller.barWidth - minWidthDifference),
  50047. chartY: (normalizedEvent.chartY - scroller.y -
  50048. scroller.yOffset) /
  50049. (scroller.barWidth - minWidthDifference)
  50050. };
  50051. }
  50052. /**
  50053. * Destroys allocated elements.
  50054. *
  50055. * @private
  50056. * @function Highcharts.Scrollbar#destroy
  50057. */
  50058. destroy() {
  50059. const scroller = this, navigator = scroller.chart.scroller;
  50060. // Disconnect events added in addEvents
  50061. scroller.removeEvents();
  50062. // Destroy properties
  50063. [
  50064. 'track',
  50065. 'scrollbarRifles',
  50066. 'scrollbar',
  50067. 'scrollbarGroup',
  50068. 'group'
  50069. ].forEach(function (prop) {
  50070. if (scroller[prop] && scroller[prop].destroy) {
  50071. scroller[prop] = scroller[prop].destroy();
  50072. }
  50073. });
  50074. // #6421, chart may have more scrollbars
  50075. if (navigator && scroller === navigator.scrollbar) {
  50076. navigator.scrollbar = null;
  50077. // Destroy elements in collection
  50078. destroyObjectProperties(navigator.scrollbarButtons);
  50079. }
  50080. }
  50081. /**
  50082. * Draw the scrollbar buttons with arrows
  50083. *
  50084. * @private
  50085. * @function Highcharts.Scrollbar#drawScrollbarButton
  50086. * @param {number} index
  50087. * 0 is left, 1 is right
  50088. */
  50089. drawScrollbarButton(index) {
  50090. const scroller = this, renderer = scroller.renderer, scrollbarButtons = scroller.scrollbarButtons, options = scroller.options, size = scroller.size, group = renderer.g().add(scroller.group);
  50091. scrollbarButtons.push(group);
  50092. if (options.buttonsEnabled) {
  50093. // Create a rectangle for the scrollbar button
  50094. const rect = renderer.rect()
  50095. .addClass('highcharts-scrollbar-button')
  50096. .add(group);
  50097. // Presentational attributes
  50098. if (!scroller.chart.styledMode) {
  50099. rect.attr({
  50100. stroke: options.buttonBorderColor,
  50101. 'stroke-width': options.buttonBorderWidth,
  50102. fill: options.buttonBackgroundColor
  50103. });
  50104. }
  50105. // Place the rectangle based on the rendered stroke width
  50106. rect.attr(rect.crisp({
  50107. x: -0.5,
  50108. y: -0.5,
  50109. // +1 to compensate for crispifying in rect method
  50110. width: size + 1,
  50111. height: size + 1,
  50112. r: options.buttonBorderRadius
  50113. }, rect.strokeWidth()));
  50114. // Button arrow
  50115. const arrow = renderer
  50116. .path(Scrollbar.swapXY([[
  50117. 'M',
  50118. size / 2 + (index ? -1 : 1),
  50119. size / 2 - 3
  50120. ], [
  50121. 'L',
  50122. size / 2 + (index ? -1 : 1),
  50123. size / 2 + 3
  50124. ], [
  50125. 'L',
  50126. size / 2 + (index ? 2 : -2),
  50127. size / 2
  50128. ]], options.vertical))
  50129. .addClass('highcharts-scrollbar-arrow')
  50130. .add(scrollbarButtons[index]);
  50131. if (!scroller.chart.styledMode) {
  50132. arrow.attr({
  50133. fill: options.buttonArrowColor
  50134. });
  50135. }
  50136. }
  50137. }
  50138. /**
  50139. * @private
  50140. * @function Highcharts.Scrollbar#init
  50141. * @param {Highcharts.SVGRenderer} renderer
  50142. * @param {Highcharts.ScrollbarOptions} options
  50143. * @param {Highcharts.Chart} chart
  50144. */
  50145. init(renderer, options, chart) {
  50146. const scroller = this;
  50147. scroller.scrollbarButtons = [];
  50148. scroller.renderer = renderer;
  50149. scroller.userOptions = options;
  50150. scroller.options = merge(ScrollbarDefaults, defaultOptions.scrollbar, options);
  50151. scroller.options.margin = pick(scroller.options.margin, 10);
  50152. scroller.chart = chart;
  50153. // backward compatibility
  50154. scroller.size = pick(scroller.options.size, scroller.options.height);
  50155. // Init
  50156. if (options.enabled) {
  50157. scroller.render();
  50158. scroller.addEvents();
  50159. }
  50160. }
  50161. mouseDownHandler(e) {
  50162. const scroller = this, normalizedEvent = scroller.chart.pointer.normalize(e), mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  50163. scroller.chartX = mousePosition.chartX;
  50164. scroller.chartY = mousePosition.chartY;
  50165. scroller.initPositions = [scroller.from, scroller.to];
  50166. scroller.grabbedCenter = true;
  50167. }
  50168. /**
  50169. * Event handler for the mouse move event.
  50170. * @private
  50171. */
  50172. mouseMoveHandler(e) {
  50173. const scroller = this, normalizedEvent = scroller.chart.pointer.normalize(e), options = scroller.options, direction = options.vertical ?
  50174. 'chartY' : 'chartX', initPositions = scroller.initPositions || [];
  50175. let scrollPosition, chartPosition, change;
  50176. // In iOS, a mousemove event with e.pageX === 0 is fired when
  50177. // holding the finger down in the center of the scrollbar. This
  50178. // should be ignored.
  50179. if (scroller.grabbedCenter &&
  50180. // #4696, scrollbar failed on Android
  50181. (!e.touches || e.touches[0][direction] !== 0)) {
  50182. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  50183. scrollPosition = scroller[direction];
  50184. change = chartPosition - scrollPosition;
  50185. scroller.hasDragged = true;
  50186. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  50187. if (scroller.hasDragged) {
  50188. fireEvent(scroller, 'changed', {
  50189. from: scroller.from,
  50190. to: scroller.to,
  50191. trigger: 'scrollbar',
  50192. DOMType: e.type,
  50193. DOMEvent: e
  50194. });
  50195. }
  50196. }
  50197. }
  50198. /**
  50199. * Event handler for the mouse up event.
  50200. * @private
  50201. */
  50202. mouseUpHandler(e) {
  50203. const scroller = this;
  50204. if (scroller.hasDragged) {
  50205. fireEvent(scroller, 'changed', {
  50206. from: scroller.from,
  50207. to: scroller.to,
  50208. trigger: 'scrollbar',
  50209. DOMType: e.type,
  50210. DOMEvent: e
  50211. });
  50212. }
  50213. scroller.grabbedCenter =
  50214. scroller.hasDragged =
  50215. scroller.chartX =
  50216. scroller.chartY = null;
  50217. }
  50218. /**
  50219. * Position the scrollbar, method called from a parent with defined
  50220. * dimensions.
  50221. *
  50222. * @private
  50223. * @function Highcharts.Scrollbar#position
  50224. * @param {number} x
  50225. * x-position on the chart
  50226. * @param {number} y
  50227. * y-position on the chart
  50228. * @param {number} width
  50229. * width of the scrollbar
  50230. * @param {number} height
  50231. * height of the scorllbar
  50232. */
  50233. position(x, y, width, height) {
  50234. const scroller = this, options = scroller.options, { buttonsEnabled, margin = 0, vertical } = options, method = scroller.rendered ? 'animate' : 'attr';
  50235. let xOffset = height, yOffset = 0;
  50236. // Make the scrollbar visible when it is repositioned, #15763.
  50237. scroller.group.show();
  50238. scroller.x = x;
  50239. scroller.y = y + this.trackBorderWidth;
  50240. scroller.width = width; // width with buttons
  50241. scroller.height = height;
  50242. scroller.xOffset = xOffset;
  50243. scroller.yOffset = yOffset;
  50244. // If Scrollbar is a vertical type, swap options:
  50245. if (vertical) {
  50246. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  50247. scroller.xOffset = xOffset = 0;
  50248. scroller.yOffset = yOffset = buttonsEnabled ? scroller.size : 0;
  50249. // width without buttons
  50250. scroller.barWidth = height - (buttonsEnabled ? width * 2 : 0);
  50251. scroller.x = x = x + margin;
  50252. }
  50253. else {
  50254. scroller.height = height = scroller.size;
  50255. scroller.xOffset = xOffset = buttonsEnabled ? scroller.size : 0;
  50256. // width without buttons
  50257. scroller.barWidth = width - (buttonsEnabled ? height * 2 : 0);
  50258. scroller.y = scroller.y + margin;
  50259. }
  50260. // Set general position for a group:
  50261. scroller.group[method]({
  50262. translateX: x,
  50263. translateY: scroller.y
  50264. });
  50265. // Resize background/track:
  50266. scroller.track[method]({
  50267. width: width,
  50268. height: height
  50269. });
  50270. // Move right/bottom button to its place:
  50271. scroller.scrollbarButtons[1][method]({
  50272. translateX: vertical ? 0 : width - xOffset,
  50273. translateY: vertical ? height - yOffset : 0
  50274. });
  50275. }
  50276. /**
  50277. * Removes the event handlers attached previously with addEvents.
  50278. *
  50279. * @private
  50280. * @function Highcharts.Scrollbar#removeEvents
  50281. */
  50282. removeEvents() {
  50283. this._events.forEach(function (args) {
  50284. removeEvent.apply(null, args);
  50285. });
  50286. this._events.length = 0;
  50287. }
  50288. /**
  50289. * Render scrollbar with all required items.
  50290. *
  50291. * @private
  50292. * @function Highcharts.Scrollbar#render
  50293. */
  50294. render() {
  50295. const scroller = this, renderer = scroller.renderer, options = scroller.options, size = scroller.size, styledMode = scroller.chart.styledMode, group = renderer.g('scrollbar')
  50296. .attr({
  50297. zIndex: options.zIndex
  50298. })
  50299. .hide() // initially hide the scrollbar #15863
  50300. .add();
  50301. // Draw the scrollbar group
  50302. scroller.group = group;
  50303. // Draw the scrollbar track:
  50304. scroller.track = renderer.rect()
  50305. .addClass('highcharts-scrollbar-track')
  50306. .attr({
  50307. r: options.trackBorderRadius || 0,
  50308. height: size,
  50309. width: size
  50310. }).add(group);
  50311. if (!styledMode) {
  50312. scroller.track.attr({
  50313. fill: options.trackBackgroundColor,
  50314. stroke: options.trackBorderColor,
  50315. 'stroke-width': options.trackBorderWidth
  50316. });
  50317. }
  50318. const trackBorderWidth = scroller.trackBorderWidth =
  50319. scroller.track.strokeWidth();
  50320. scroller.track.attr({
  50321. x: -trackBorderWidth % 2 / 2,
  50322. y: -trackBorderWidth % 2 / 2
  50323. });
  50324. // Draw the scrollbar itself
  50325. scroller.scrollbarGroup = renderer.g().add(group);
  50326. scroller.scrollbar = renderer.rect()
  50327. .addClass('highcharts-scrollbar-thumb')
  50328. .attr({
  50329. height: size - trackBorderWidth,
  50330. width: size - trackBorderWidth,
  50331. r: options.barBorderRadius || 0
  50332. }).add(scroller.scrollbarGroup);
  50333. scroller.scrollbarRifles = renderer
  50334. .path(Scrollbar.swapXY([
  50335. ['M', -3, size / 4],
  50336. ['L', -3, 2 * size / 3],
  50337. ['M', 0, size / 4],
  50338. ['L', 0, 2 * size / 3],
  50339. ['M', 3, size / 4],
  50340. ['L', 3, 2 * size / 3]
  50341. ], options.vertical))
  50342. .addClass('highcharts-scrollbar-rifles')
  50343. .add(scroller.scrollbarGroup);
  50344. if (!styledMode) {
  50345. scroller.scrollbar.attr({
  50346. fill: options.barBackgroundColor,
  50347. stroke: options.barBorderColor,
  50348. 'stroke-width': options.barBorderWidth
  50349. });
  50350. scroller.scrollbarRifles.attr({
  50351. stroke: options.rifleColor,
  50352. 'stroke-width': 1
  50353. });
  50354. }
  50355. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  50356. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  50357. // Draw the buttons:
  50358. scroller.drawScrollbarButton(0);
  50359. scroller.drawScrollbarButton(1);
  50360. }
  50361. /**
  50362. * Set scrollbar size, with a given scale.
  50363. *
  50364. * @private
  50365. * @function Highcharts.Scrollbar#setRange
  50366. * @param {number} from
  50367. * scale (0-1) where bar should start
  50368. * @param {number} to
  50369. * scale (0-1) where bar should end
  50370. */
  50371. setRange(from, to) {
  50372. const scroller = this, options = scroller.options, vertical = options.vertical, minWidth = options.minWidth, fullWidth = scroller.barWidth, method = (this.rendered &&
  50373. !this.hasDragged &&
  50374. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  50375. if (!defined(fullWidth)) {
  50376. return;
  50377. }
  50378. const toPX = fullWidth * Math.min(to, 1);
  50379. let fromPX, newSize;
  50380. from = Math.max(from, 0);
  50381. fromPX = Math.ceil(fullWidth * from);
  50382. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  50383. // We need to recalculate position, if minWidth is used
  50384. if (newSize < minWidth) {
  50385. fromPX = (fullWidth - minWidth + newSize) * from;
  50386. newSize = minWidth;
  50387. }
  50388. const newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  50389. const newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  50390. // Store current position:
  50391. scroller.from = from;
  50392. scroller.to = to;
  50393. if (!vertical) {
  50394. scroller.scrollbarGroup[method]({
  50395. translateX: newPos
  50396. });
  50397. scroller.scrollbar[method]({
  50398. width: newSize
  50399. });
  50400. scroller.scrollbarRifles[method]({
  50401. translateX: newRiflesPos
  50402. });
  50403. scroller.scrollbarLeft = newPos;
  50404. scroller.scrollbarTop = 0;
  50405. }
  50406. else {
  50407. scroller.scrollbarGroup[method]({
  50408. translateY: newPos
  50409. });
  50410. scroller.scrollbar[method]({
  50411. height: newSize
  50412. });
  50413. scroller.scrollbarRifles[method]({
  50414. translateY: newRiflesPos
  50415. });
  50416. scroller.scrollbarTop = newPos;
  50417. scroller.scrollbarLeft = 0;
  50418. }
  50419. if (newSize <= 12) {
  50420. scroller.scrollbarRifles.hide();
  50421. }
  50422. else {
  50423. scroller.scrollbarRifles.show();
  50424. }
  50425. // Show or hide the scrollbar based on the showFull setting
  50426. if (options.showFull === false) {
  50427. if (from <= 0 && to >= 1) {
  50428. scroller.group.hide();
  50429. }
  50430. else {
  50431. scroller.group.show();
  50432. }
  50433. }
  50434. scroller.rendered = true;
  50435. }
  50436. /**
  50437. * Checks if the extremes should be updated in response to a scrollbar
  50438. * change event.
  50439. *
  50440. * @private
  50441. * @function Highcharts.Scrollbar#shouldUpdateExtremes
  50442. */
  50443. shouldUpdateExtremes(eventType) {
  50444. return (pick(this.options.liveRedraw, H.svg &&
  50445. !H.isTouchDevice &&
  50446. !this.chart.boosted) ||
  50447. // Mouseup always should change extremes
  50448. eventType === 'mouseup' ||
  50449. eventType === 'touchend' ||
  50450. // Internal events
  50451. !defined(eventType));
  50452. }
  50453. trackClick(e) {
  50454. const scroller = this;
  50455. const normalizedEvent = scroller.chart.pointer.normalize(e), range = scroller.to - scroller.from, top = scroller.y + scroller.scrollbarTop, left = scroller.x + scroller.scrollbarLeft;
  50456. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  50457. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  50458. // On the top or on the left side of the track:
  50459. scroller.updatePosition(scroller.from + range, scroller.to + range);
  50460. }
  50461. else {
  50462. // On the bottom or the right side of the track:
  50463. scroller.updatePosition(scroller.from - range, scroller.to - range);
  50464. }
  50465. fireEvent(scroller, 'changed', {
  50466. from: scroller.from,
  50467. to: scroller.to,
  50468. trigger: 'scrollbar',
  50469. DOMEvent: e
  50470. });
  50471. }
  50472. /**
  50473. * Update the scrollbar with new options
  50474. *
  50475. * @private
  50476. * @function Highcharts.Scrollbar#update
  50477. * @param {Highcharts.ScrollbarOptions} options
  50478. */
  50479. update(options) {
  50480. this.destroy();
  50481. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  50482. }
  50483. /**
  50484. * Update position option in the Scrollbar, with normalized 0-1 scale
  50485. *
  50486. * @private
  50487. * @function Highcharts.Scrollbar#updatePosition
  50488. * @param {number} from
  50489. * @param {number} to
  50490. */
  50491. updatePosition(from, to) {
  50492. if (to > 1) {
  50493. from = correctFloat(1 - correctFloat(to - from));
  50494. to = 1;
  50495. }
  50496. if (from < 0) {
  50497. to = correctFloat(to - from);
  50498. from = 0;
  50499. }
  50500. this.from = from;
  50501. this.to = to;
  50502. }
  50503. }
  50504. /* *
  50505. *
  50506. * Static Properties
  50507. *
  50508. * */
  50509. Scrollbar.defaultOptions = ScrollbarDefaults;
  50510. /* *
  50511. *
  50512. * Registry
  50513. *
  50514. * */
  50515. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  50516. /* *
  50517. *
  50518. * Default Export
  50519. *
  50520. * */
  50521. return Scrollbar;
  50522. });
  50523. _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) {
  50524. /* *
  50525. *
  50526. * (c) 2010-2021 Torstein Honsi
  50527. *
  50528. * License: www.highcharts.com/license
  50529. *
  50530. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  50531. *
  50532. * */
  50533. const { defaultOptions } = D;
  50534. const { hasTouch, isTouchDevice } = H;
  50535. const { addEvent, clamp, correctFloat, defined, destroyObjectProperties, erase, extend, find, isArray, isNumber, merge, pick, removeEvent, splat } = U;
  50536. /* *
  50537. *
  50538. * Functions
  50539. *
  50540. * */
  50541. /**
  50542. * Finding the min or max of a set of variables where we don't know if they are
  50543. * defined, is a pattern that is repeated several places in Highcharts. Consider
  50544. * making this a global utility method.
  50545. * @private
  50546. */
  50547. function numExt(extreme, ...args) {
  50548. const numbers = [].filter.call(args, isNumber);
  50549. if (numbers.length) {
  50550. return Math[extreme].apply(0, numbers);
  50551. }
  50552. }
  50553. /* *
  50554. *
  50555. * Class
  50556. *
  50557. * */
  50558. /**
  50559. * The Navigator class
  50560. *
  50561. * @private
  50562. * @class
  50563. * @name Highcharts.Navigator
  50564. *
  50565. * @param {Highcharts.Chart} chart
  50566. * Chart object
  50567. */
  50568. class Navigator {
  50569. /* *
  50570. *
  50571. * Static Functions
  50572. *
  50573. * */
  50574. static compose(AxisClass, ChartClass, SeriesClass) {
  50575. NavigatorComposition.compose(AxisClass, ChartClass, Navigator, SeriesClass);
  50576. }
  50577. /* *
  50578. *
  50579. * Constructor
  50580. *
  50581. * */
  50582. constructor(chart) {
  50583. /* *
  50584. *
  50585. * Properties
  50586. *
  50587. * */
  50588. this.baseSeries = void 0;
  50589. this.chart = void 0;
  50590. this.handles = void 0;
  50591. this.height = void 0;
  50592. this.left = void 0;
  50593. this.navigatorEnabled = void 0;
  50594. this.navigatorGroup = void 0;
  50595. this.navigatorOptions = void 0;
  50596. this.navigatorSeries = void 0;
  50597. this.navigatorSize = void 0;
  50598. this.opposite = void 0;
  50599. this.outline = void 0;
  50600. this.range = void 0;
  50601. this.rendered = void 0;
  50602. this.scrollbarHeight = 0;
  50603. this.scrollButtonSize = void 0;
  50604. this.shades = void 0;
  50605. this.size = void 0;
  50606. this.top = void 0;
  50607. this.xAxis = void 0;
  50608. this.yAxis = void 0;
  50609. this.zoomedMax = void 0;
  50610. this.zoomedMin = void 0;
  50611. this.init(chart);
  50612. }
  50613. /* *
  50614. *
  50615. * Functions
  50616. *
  50617. * */
  50618. /**
  50619. * Draw one of the handles on the side of the zoomed range in the navigator.
  50620. *
  50621. * @private
  50622. * @function Highcharts.Navigator#drawHandle
  50623. *
  50624. * @param {number} x
  50625. * The x center for the handle
  50626. *
  50627. * @param {number} index
  50628. * 0 for left and 1 for right
  50629. *
  50630. * @param {boolean|undefined} inverted
  50631. * Flag for chart.inverted
  50632. *
  50633. * @param {string} verb
  50634. * Use 'animate' or 'attr'
  50635. */
  50636. drawHandle(x, index, inverted, verb) {
  50637. const navigator = this, height = navigator.navigatorOptions.handles.height;
  50638. // Place it
  50639. navigator.handles[index][verb](inverted ? {
  50640. translateX: Math.round(navigator.left + navigator.height / 2),
  50641. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  50642. } : {
  50643. translateX: Math.round(navigator.left + parseInt(x, 10)),
  50644. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  50645. });
  50646. }
  50647. /**
  50648. * Render outline around the zoomed range
  50649. *
  50650. * @private
  50651. * @function Highcharts.Navigator#drawOutline
  50652. *
  50653. * @param {number} zoomedMin
  50654. * in pixels position where zoomed range starts
  50655. *
  50656. * @param {number} zoomedMax
  50657. * in pixels position where zoomed range ends
  50658. *
  50659. * @param {boolean|undefined} inverted
  50660. * flag if chart is inverted
  50661. *
  50662. * @param {string} verb
  50663. * use 'animate' or 'attr'
  50664. */
  50665. drawOutline(zoomedMin, zoomedMax, inverted, verb) {
  50666. const navigator = this, maskInside = navigator.navigatorOptions.maskInside, outlineWidth = navigator.outline.strokeWidth(), halfOutline = outlineWidth / 2, outlineCorrection = (outlineWidth % 2) / 2, // #5800
  50667. scrollButtonSize = navigator.scrollButtonSize, navigatorSize = navigator.size, navigatorTop = navigator.top, height = navigator.height, lineTop = navigatorTop - halfOutline, lineBtm = navigatorTop + height;
  50668. let left = navigator.left, verticalMin, path;
  50669. if (inverted) {
  50670. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  50671. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  50672. path = [
  50673. [
  50674. 'M',
  50675. left + height,
  50676. navigatorTop - scrollButtonSize - outlineCorrection
  50677. ],
  50678. // top right of zoomed range
  50679. ['L', left + height, verticalMin],
  50680. ['L', left, verticalMin],
  50681. ['M', left, zoomedMax],
  50682. ['L', left + height, zoomedMax],
  50683. [
  50684. 'L',
  50685. left + height,
  50686. navigatorTop + navigatorSize + scrollButtonSize
  50687. ]
  50688. ];
  50689. if (maskInside) {
  50690. path.push(
  50691. // upper left of zoomed range
  50692. ['M', left + height, verticalMin - halfOutline],
  50693. // upper right of z.r.
  50694. [
  50695. 'L',
  50696. left + height,
  50697. zoomedMax + halfOutline
  50698. ]);
  50699. }
  50700. }
  50701. else {
  50702. left -= scrollButtonSize;
  50703. zoomedMin += left + scrollButtonSize - outlineCorrection;
  50704. zoomedMax += left + scrollButtonSize - outlineCorrection;
  50705. path = [
  50706. // left
  50707. ['M', left, lineTop],
  50708. // upper left of zoomed range
  50709. ['L', zoomedMin, lineTop],
  50710. // lower left of z.r.
  50711. ['L', zoomedMin, lineBtm],
  50712. // lower right of z.r.
  50713. ['M', zoomedMax, lineBtm],
  50714. // upper right of z.r.
  50715. ['L', zoomedMax, lineTop],
  50716. // right
  50717. [
  50718. 'L',
  50719. left + navigatorSize + scrollButtonSize * 2,
  50720. navigatorTop + halfOutline
  50721. ]
  50722. ];
  50723. if (maskInside) {
  50724. path.push(
  50725. // upper left of zoomed range
  50726. ['M', zoomedMin - halfOutline, lineTop],
  50727. // upper right of z.r.
  50728. ['L', zoomedMax + halfOutline, lineTop]);
  50729. }
  50730. }
  50731. navigator.outline[verb]({
  50732. d: path
  50733. });
  50734. }
  50735. /**
  50736. * Render outline around the zoomed range
  50737. *
  50738. * @private
  50739. * @function Highcharts.Navigator#drawMasks
  50740. *
  50741. * @param {number} zoomedMin
  50742. * in pixels position where zoomed range starts
  50743. *
  50744. * @param {number} zoomedMax
  50745. * in pixels position where zoomed range ends
  50746. *
  50747. * @param {boolean|undefined} inverted
  50748. * flag if chart is inverted
  50749. *
  50750. * @param {string} verb
  50751. * use 'animate' or 'attr'
  50752. */
  50753. drawMasks(zoomedMin, zoomedMax, inverted, verb) {
  50754. const navigator = this, left = navigator.left, top = navigator.top, navigatorHeight = navigator.height;
  50755. let height, width, x, y;
  50756. // Determine rectangle position & size
  50757. // According to (non)inverted position:
  50758. if (inverted) {
  50759. x = [left, left, left];
  50760. y = [top, top + zoomedMin, top + zoomedMax];
  50761. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  50762. height = [
  50763. zoomedMin,
  50764. zoomedMax - zoomedMin,
  50765. navigator.size - zoomedMax
  50766. ];
  50767. }
  50768. else {
  50769. x = [left, left + zoomedMin, left + zoomedMax];
  50770. y = [top, top, top];
  50771. width = [
  50772. zoomedMin,
  50773. zoomedMax - zoomedMin,
  50774. navigator.size - zoomedMax
  50775. ];
  50776. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  50777. }
  50778. navigator.shades.forEach((shade, i) => {
  50779. shade[verb]({
  50780. x: x[i],
  50781. y: y[i],
  50782. width: width[i],
  50783. height: height[i]
  50784. });
  50785. });
  50786. }
  50787. /**
  50788. * Generate DOM elements for a navigator:
  50789. *
  50790. * - main navigator group
  50791. *
  50792. * - all shades
  50793. *
  50794. * - outline
  50795. *
  50796. * - handles
  50797. *
  50798. * @private
  50799. * @function Highcharts.Navigator#renderElements
  50800. */
  50801. renderElements() {
  50802. const navigator = this, navigatorOptions = navigator.navigatorOptions, maskInside = navigatorOptions.maskInside, chart = navigator.chart, inverted = chart.inverted, renderer = chart.renderer, mouseCursor = {
  50803. cursor: inverted ? 'ns-resize' : 'ew-resize'
  50804. },
  50805. // Create the main navigator group
  50806. navigatorGroup = navigator.navigatorGroup = renderer
  50807. .g('navigator')
  50808. .attr({
  50809. zIndex: 8,
  50810. visibility: 'hidden'
  50811. })
  50812. .add();
  50813. // Create masks, each mask will get events and fill:
  50814. [
  50815. !maskInside,
  50816. maskInside,
  50817. !maskInside
  50818. ].forEach((hasMask, index) => {
  50819. const shade = renderer.rect()
  50820. .addClass('highcharts-navigator-mask' +
  50821. (index === 1 ? '-inside' : '-outside'))
  50822. .add(navigatorGroup);
  50823. if (!chart.styledMode) {
  50824. shade.attr({
  50825. fill: hasMask ?
  50826. navigatorOptions.maskFill :
  50827. 'rgba(0,0,0,0)'
  50828. });
  50829. if (index === 1) {
  50830. shade.css(mouseCursor);
  50831. }
  50832. }
  50833. navigator.shades[index] = shade;
  50834. });
  50835. // Create the outline:
  50836. navigator.outline = renderer.path()
  50837. .addClass('highcharts-navigator-outline')
  50838. .add(navigatorGroup);
  50839. if (!chart.styledMode) {
  50840. navigator.outline.attr({
  50841. 'stroke-width': navigatorOptions.outlineWidth,
  50842. stroke: navigatorOptions.outlineColor
  50843. });
  50844. }
  50845. // Create the handlers:
  50846. if (navigatorOptions.handles && navigatorOptions.handles.enabled) {
  50847. const handlesOptions = navigatorOptions.handles, { height, width } = handlesOptions;
  50848. [0, 1].forEach((index) => {
  50849. navigator.handles[index] = renderer.symbol(handlesOptions.symbols[index], -width / 2 - 1, 0, width, height, handlesOptions);
  50850. if (chart.inverted) {
  50851. navigator.handles[index].attr({
  50852. rotation: 90,
  50853. rotationOriginX: Math.floor(-width / 2),
  50854. rotationOriginY: (height + width) / 2
  50855. });
  50856. }
  50857. // zIndex = 6 for right handle, 7 for left.
  50858. // Can't be 10, because of the tooltip in inverted chart #2908
  50859. navigator.handles[index].attr({ zIndex: 7 - index })
  50860. .addClass('highcharts-navigator-handle ' +
  50861. 'highcharts-navigator-handle-' +
  50862. ['left', 'right'][index]).add(navigatorGroup);
  50863. if (!chart.styledMode) {
  50864. navigator.handles[index]
  50865. .attr({
  50866. fill: handlesOptions.backgroundColor,
  50867. stroke: handlesOptions.borderColor,
  50868. 'stroke-width': handlesOptions.lineWidth
  50869. })
  50870. .css(mouseCursor);
  50871. }
  50872. });
  50873. }
  50874. }
  50875. /**
  50876. * Update navigator
  50877. *
  50878. * @private
  50879. * @function Highcharts.Navigator#update
  50880. *
  50881. * @param {Highcharts.NavigatorOptions} options
  50882. * Options to merge in when updating navigator
  50883. */
  50884. update(options) {
  50885. // Remove references to old navigator series in base series
  50886. (this.series || []).forEach((series) => {
  50887. if (series.baseSeries) {
  50888. delete series.baseSeries.navigatorSeries;
  50889. }
  50890. });
  50891. // Destroy and rebuild navigator
  50892. this.destroy();
  50893. const chartOptions = this.chart.options;
  50894. merge(true, chartOptions.navigator, options);
  50895. this.init(this.chart);
  50896. }
  50897. /**
  50898. * Render the navigator
  50899. *
  50900. * @private
  50901. * @function Highcharts.Navigator#render
  50902. * @param {number} min
  50903. * X axis value minimum
  50904. * @param {number} max
  50905. * X axis value maximum
  50906. * @param {number} [pxMin]
  50907. * Pixel value minimum
  50908. * @param {number} [pxMax]
  50909. * Pixel value maximum
  50910. */
  50911. render(min, max, pxMin, pxMax) {
  50912. 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;
  50913. let navigatorWidth, scrollbarLeft, scrollbarTop, scrollbarHeight = navigator.scrollbarHeight, navigatorSize, verb;
  50914. // Don't redraw while moving the handles (#4703).
  50915. if (this.hasDragged && !defined(pxMin)) {
  50916. return;
  50917. }
  50918. min = correctFloat(min - pointRange / 2);
  50919. max = correctFloat(max + pointRange / 2);
  50920. // Don't render the navigator until we have data (#486, #4202, #5172).
  50921. if (!isNumber(min) || !isNumber(max)) {
  50922. // However, if navigator was already rendered, we may need to resize
  50923. // it. For example hidden series, but visible navigator (#6022).
  50924. if (rendered) {
  50925. pxMin = 0;
  50926. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  50927. }
  50928. else {
  50929. return;
  50930. }
  50931. }
  50932. navigator.left = pick(xAxis.left,
  50933. // in case of scrollbar only, without navigator
  50934. chart.plotLeft + scrollButtonSize +
  50935. (inverted ? chart.plotWidth : 0));
  50936. let zoomedMax = navigator.size = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  50937. 2 * scrollButtonSize);
  50938. if (inverted) {
  50939. navigatorWidth = scrollbarHeight;
  50940. }
  50941. else {
  50942. navigatorWidth = navigatorSize + 2 * scrollButtonSize;
  50943. }
  50944. // Get the pixel position of the handles
  50945. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  50946. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  50947. // Verify (#1851, #2238)
  50948. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  50949. pxMin = 0;
  50950. pxMax = navigatorWidth;
  50951. }
  50952. // Are we below the minRange? (#2618, #6191)
  50953. const newMin = xAxis.toValue(pxMin, true), newMax = xAxis.toValue(pxMax, true), currentRange = Math.abs(correctFloat(newMax - newMin));
  50954. if (currentRange < minRange) {
  50955. if (this.grabbedLeft) {
  50956. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  50957. }
  50958. else if (this.grabbedRight) {
  50959. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  50960. }
  50961. }
  50962. else if (defined(maxRange) &&
  50963. correctFloat(currentRange - pointRange) > maxRange) {
  50964. if (this.grabbedLeft) {
  50965. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  50966. }
  50967. else if (this.grabbedRight) {
  50968. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  50969. }
  50970. }
  50971. // Handles are allowed to cross, but never exceed the plot area
  50972. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  50973. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  50974. navigator.zoomedMax - navigator.fixedWidth :
  50975. Math.min(pxMin, pxMax), 0, zoomedMax);
  50976. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  50977. zoomedMax = Math.round(navigator.zoomedMax);
  50978. const zoomedMin = Math.round(navigator.zoomedMin);
  50979. if (navigatorEnabled) {
  50980. navigator.navigatorGroup.attr({
  50981. visibility: 'inherit'
  50982. });
  50983. // Place elements
  50984. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  50985. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  50986. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  50987. if (navigator.navigatorOptions.handles.enabled) {
  50988. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  50989. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  50990. }
  50991. }
  50992. if (navigator.scrollbar) {
  50993. if (inverted) {
  50994. scrollbarTop = navigator.top - scrollButtonSize;
  50995. scrollbarLeft = navigator.left - scrollbarHeight +
  50996. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  50997. // Multiple axes has offsets:
  50998. (scrollbarXAxis.titleOffset || 0) +
  50999. // Self margin from the axis.title
  51000. scrollbarXAxis.axisTitleMargin);
  51001. scrollbarHeight = navigatorSize + 2 * scrollButtonSize;
  51002. }
  51003. else {
  51004. scrollbarTop = navigator.top + (navigatorEnabled ?
  51005. navigator.height :
  51006. -scrollbarHeight);
  51007. scrollbarLeft = navigator.left - scrollButtonSize;
  51008. }
  51009. // Reposition scrollbar
  51010. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  51011. // Keep scale 0-1
  51012. navigator.scrollbar.setRange(
  51013. // Use real value, not rounded because range can be very small
  51014. // (#1716)
  51015. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  51016. }
  51017. navigator.rendered = true;
  51018. }
  51019. /**
  51020. * Set up the mouse and touch events for the navigator
  51021. *
  51022. * @private
  51023. * @function Highcharts.Navigator#addMouseEvents
  51024. */
  51025. addMouseEvents() {
  51026. const navigator = this, chart = navigator.chart, container = chart.container;
  51027. let eventsToUnbind = [], mouseMoveHandler, mouseUpHandler;
  51028. /**
  51029. * Create mouse events' handlers.
  51030. * Make them as separate functions to enable wrapping them:
  51031. */
  51032. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  51033. navigator.onMouseMove(e);
  51034. };
  51035. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  51036. navigator.onMouseUp(e);
  51037. };
  51038. // Add shades and handles mousedown events
  51039. eventsToUnbind = navigator.getPartsEvents('mousedown');
  51040. // Add mouse move and mouseup events. These are bind to doc/container,
  51041. // because Navigator.grabbedSomething flags are stored in mousedown
  51042. // events
  51043. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  51044. // Touch events
  51045. if (hasTouch) {
  51046. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  51047. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  51048. }
  51049. navigator.eventsToUnbind = eventsToUnbind;
  51050. // Data events
  51051. if (navigator.series && navigator.series[0]) {
  51052. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  51053. chart.navigator.modifyNavigatorAxisExtremes();
  51054. }));
  51055. }
  51056. }
  51057. /**
  51058. * Generate events for handles and masks
  51059. *
  51060. * @private
  51061. * @function Highcharts.Navigator#getPartsEvents
  51062. *
  51063. * @param {string} eventName
  51064. * Event name handler, 'mousedown' or 'touchstart'
  51065. *
  51066. * @return {Array<Function>}
  51067. * An array of functions to remove navigator functions from the
  51068. * events again.
  51069. */
  51070. getPartsEvents(eventName) {
  51071. const navigator = this, events = [];
  51072. ['shades', 'handles'].forEach(function (name) {
  51073. navigator[name].forEach(function (navigatorItem, index) {
  51074. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  51075. navigator[name + 'Mousedown'](e, index);
  51076. }));
  51077. });
  51078. });
  51079. return events;
  51080. }
  51081. /**
  51082. * Mousedown on a shaded mask, either:
  51083. *
  51084. * - will be stored for future drag&drop
  51085. *
  51086. * - will directly shift to a new range
  51087. *
  51088. * @private
  51089. * @function Highcharts.Navigator#shadesMousedown
  51090. *
  51091. * @param {Highcharts.PointerEventObject} e
  51092. * Mouse event
  51093. *
  51094. * @param {number} index
  51095. * Index of a mask in Navigator.shades array
  51096. */
  51097. shadesMousedown(e, index) {
  51098. e = this.chart.pointer.normalize(e);
  51099. const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, zoomedMin = navigator.zoomedMin, navigatorSize = navigator.size, range = navigator.range;
  51100. let navigatorPosition = navigator.left, chartX = e.chartX, fixedMax, fixedMin, ext, left;
  51101. // For inverted chart, swap some options:
  51102. if (chart.inverted) {
  51103. chartX = e.chartY;
  51104. navigatorPosition = navigator.top;
  51105. }
  51106. if (index === 1) {
  51107. // Store information for drag&drop
  51108. navigator.grabbedCenter = chartX;
  51109. navigator.fixedWidth = range;
  51110. navigator.dragOffset = chartX - zoomedMin;
  51111. }
  51112. else {
  51113. // Shift the range by clicking on shaded areas
  51114. left = chartX - navigatorPosition - range / 2;
  51115. if (index === 0) {
  51116. left = Math.max(0, left);
  51117. }
  51118. else if (index === 2 && left + range >= navigatorSize) {
  51119. left = navigatorSize - range;
  51120. if (navigator.reversedExtremes) {
  51121. // #7713
  51122. left -= range;
  51123. fixedMin = navigator.getUnionExtremes().dataMin;
  51124. }
  51125. else {
  51126. // #2293, #3543
  51127. fixedMax = navigator.getUnionExtremes().dataMax;
  51128. }
  51129. }
  51130. if (left !== zoomedMin) { // it has actually moved
  51131. navigator.fixedWidth = range; // #1370
  51132. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  51133. if (defined(ext.min)) { // #7411
  51134. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  51135. { trigger: 'navigator' });
  51136. }
  51137. }
  51138. }
  51139. }
  51140. /**
  51141. * Mousedown on a handle mask.
  51142. * Will store necessary information for drag&drop.
  51143. *
  51144. * @private
  51145. * @function Highcharts.Navigator#handlesMousedown
  51146. * @param {Highcharts.PointerEventObject} e
  51147. * Mouse event
  51148. * @param {number} index
  51149. * Index of a handle in Navigator.handles array
  51150. */
  51151. handlesMousedown(e, index) {
  51152. e = this.chart.pointer.normalize(e);
  51153. const navigator = this, chart = navigator.chart, baseXAxis = chart.xAxis[0],
  51154. // For reversed axes, min and max are changed,
  51155. // so the other extreme should be stored
  51156. reverse = navigator.reversedExtremes;
  51157. if (index === 0) {
  51158. // Grab the left handle
  51159. navigator.grabbedLeft = true;
  51160. navigator.otherHandlePos = navigator.zoomedMax;
  51161. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  51162. }
  51163. else {
  51164. // Grab the right handle
  51165. navigator.grabbedRight = true;
  51166. navigator.otherHandlePos = navigator.zoomedMin;
  51167. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  51168. }
  51169. chart.fixedRange = null;
  51170. }
  51171. /**
  51172. * Mouse move event based on x/y mouse position.
  51173. *
  51174. * @private
  51175. * @function Highcharts.Navigator#onMouseMove
  51176. *
  51177. * @param {Highcharts.PointerEventObject} e
  51178. * Mouse event
  51179. */
  51180. onMouseMove(e) {
  51181. const navigator = this, chart = navigator.chart, navigatorSize = navigator.navigatorSize, range = navigator.range, dragOffset = navigator.dragOffset, inverted = chart.inverted;
  51182. let left = navigator.left, chartX;
  51183. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  51184. // the finger down in the center of the scrollbar. This should be
  51185. // ignored.
  51186. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  51187. e = chart.pointer.normalize(e);
  51188. chartX = e.chartX;
  51189. // Swap some options for inverted chart
  51190. if (inverted) {
  51191. left = navigator.top;
  51192. chartX = e.chartY;
  51193. }
  51194. // Drag left handle or top handle
  51195. if (navigator.grabbedLeft) {
  51196. navigator.hasDragged = true;
  51197. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  51198. // Drag right handle or bottom handle
  51199. }
  51200. else if (navigator.grabbedRight) {
  51201. navigator.hasDragged = true;
  51202. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  51203. // Drag scrollbar or open area in navigator
  51204. }
  51205. else if (navigator.grabbedCenter) {
  51206. navigator.hasDragged = true;
  51207. if (chartX < dragOffset) { // outside left
  51208. chartX = dragOffset;
  51209. // outside right
  51210. }
  51211. else if (chartX >
  51212. navigatorSize + dragOffset - range) {
  51213. chartX = navigatorSize + dragOffset - range;
  51214. }
  51215. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  51216. }
  51217. if (navigator.hasDragged &&
  51218. navigator.scrollbar &&
  51219. pick(navigator.scrollbar.options.liveRedraw,
  51220. // By default, don't run live redraw on touch
  51221. // devices or if the chart is in boost.
  51222. !isTouchDevice &&
  51223. !this.chart.boosted)) {
  51224. e.DOMType = e.type;
  51225. setTimeout(function () {
  51226. navigator.onMouseUp(e);
  51227. }, 0);
  51228. }
  51229. }
  51230. }
  51231. /**
  51232. * Mouse up event based on x/y mouse position.
  51233. *
  51234. * @private
  51235. * @function Highcharts.Navigator#onMouseUp
  51236. * @param {Highcharts.PointerEventObject} e
  51237. * Mouse event
  51238. */
  51239. onMouseUp(e) {
  51240. const navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, scrollbar = navigator.scrollbar, DOMEvent = e.DOMEvent || e, inverted = chart.inverted, verb = navigator.rendered && !navigator.hasDragged ?
  51241. 'animate' : 'attr';
  51242. let zoomedMax, zoomedMin, unionExtremes, fixedMin, fixedMax, ext;
  51243. if (
  51244. // MouseUp is called for both, navigator and scrollbar (that order),
  51245. // which causes calling afterSetExtremes twice. Prevent first call
  51246. // by checking if scrollbar is going to set new extremes (#6334)
  51247. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  51248. e.trigger === 'scrollbar') {
  51249. unionExtremes = navigator.getUnionExtremes();
  51250. // When dragging one handle, make sure the other one doesn't change
  51251. if (navigator.zoomedMin === navigator.otherHandlePos) {
  51252. fixedMin = navigator.fixedExtreme;
  51253. }
  51254. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  51255. fixedMax = navigator.fixedExtreme;
  51256. }
  51257. // Snap to right edge (#4076)
  51258. if (navigator.zoomedMax === navigator.size) {
  51259. fixedMax = navigator.reversedExtremes ?
  51260. unionExtremes.dataMin :
  51261. unionExtremes.dataMax;
  51262. }
  51263. // Snap to left edge (#7576)
  51264. if (navigator.zoomedMin === 0) {
  51265. fixedMin = navigator.reversedExtremes ?
  51266. unionExtremes.dataMax :
  51267. unionExtremes.dataMin;
  51268. }
  51269. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  51270. if (defined(ext.min)) {
  51271. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  51272. // Run animation when clicking buttons, scrollbar track etc,
  51273. // but not when dragging handles or scrollbar
  51274. navigator.hasDragged ? false : null, {
  51275. trigger: 'navigator',
  51276. triggerOp: 'navigator-drag',
  51277. DOMEvent: DOMEvent // #1838
  51278. });
  51279. }
  51280. }
  51281. if (e.DOMType !== 'mousemove' &&
  51282. e.DOMType !== 'touchmove') {
  51283. navigator.grabbedLeft = navigator.grabbedRight =
  51284. navigator.grabbedCenter = navigator.fixedWidth =
  51285. navigator.fixedExtreme = navigator.otherHandlePos =
  51286. navigator.hasDragged = navigator.dragOffset = null;
  51287. }
  51288. // Update position of navigator shades, outline and handles (#12573)
  51289. if (navigator.navigatorEnabled &&
  51290. isNumber(navigator.zoomedMin) &&
  51291. isNumber(navigator.zoomedMax)) {
  51292. zoomedMin = Math.round(navigator.zoomedMin);
  51293. zoomedMax = Math.round(navigator.zoomedMax);
  51294. if (navigator.shades) {
  51295. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  51296. }
  51297. if (navigator.outline) {
  51298. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  51299. }
  51300. if (navigator.navigatorOptions.handles.enabled &&
  51301. Object.keys(navigator.handles).length ===
  51302. navigator.handles.length) {
  51303. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  51304. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  51305. }
  51306. }
  51307. }
  51308. /**
  51309. * Removes the event handlers attached previously with addEvents.
  51310. *
  51311. * @private
  51312. * @function Highcharts.Navigator#removeEvents
  51313. */
  51314. removeEvents() {
  51315. if (this.eventsToUnbind) {
  51316. this.eventsToUnbind.forEach(function (unbind) {
  51317. unbind();
  51318. });
  51319. this.eventsToUnbind = void 0;
  51320. }
  51321. this.removeBaseSeriesEvents();
  51322. }
  51323. /**
  51324. * Remove data events.
  51325. *
  51326. * @private
  51327. * @function Highcharts.Navigator#removeBaseSeriesEvents
  51328. */
  51329. removeBaseSeriesEvents() {
  51330. const baseSeries = this.baseSeries || [];
  51331. if (this.navigatorEnabled && baseSeries[0]) {
  51332. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  51333. baseSeries.forEach(function (series) {
  51334. removeEvent(series, 'updatedData', this.updatedDataHandler);
  51335. }, this);
  51336. }
  51337. // We only listen for extremes-events on the first baseSeries
  51338. if (baseSeries[0].xAxis) {
  51339. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  51340. }
  51341. }
  51342. }
  51343. /**
  51344. * Initialize the Navigator object
  51345. *
  51346. * @private
  51347. * @function Highcharts.Navigator#init
  51348. */
  51349. init(chart) {
  51350. 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;
  51351. this.handles = [];
  51352. this.shades = [];
  51353. this.chart = chart;
  51354. this.setBaseSeries();
  51355. this.height = height;
  51356. this.scrollbarHeight = scrollbarHeight;
  51357. this.scrollButtonSize = scrollButtonSize;
  51358. this.scrollbarEnabled = scrollbarEnabled;
  51359. this.navigatorEnabled = navigatorEnabled;
  51360. this.navigatorOptions = navigatorOptions;
  51361. this.scrollbarOptions = scrollbarOptions;
  51362. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  51363. const navigator = this, baseSeries = navigator.baseSeries, xAxisIndex = chart.xAxis.length, yAxisIndex = chart.yAxis.length, baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  51364. chart.xAxis[0] || { options: {} };
  51365. chart.isDirtyBox = true;
  51366. if (navigator.navigatorEnabled) {
  51367. // an x axis is required for scrollbar also
  51368. navigator.xAxis = new Axis(chart, merge({
  51369. // inherit base xAxis' break and ordinal options
  51370. breaks: baseXaxis.options.breaks,
  51371. ordinal: baseXaxis.options.ordinal
  51372. }, navigatorOptions.xAxis, {
  51373. id: 'navigator-x-axis',
  51374. yAxis: 'navigator-y-axis',
  51375. type: 'datetime',
  51376. index: xAxisIndex,
  51377. isInternal: true,
  51378. offset: 0,
  51379. keepOrdinalPadding: true,
  51380. startOnTick: false,
  51381. endOnTick: false,
  51382. minPadding: 0,
  51383. maxPadding: 0,
  51384. zoomEnabled: false
  51385. }, chart.inverted ? {
  51386. offsets: [scrollButtonSize, 0, -scrollButtonSize, 0],
  51387. width: height
  51388. } : {
  51389. offsets: [0, -scrollButtonSize, 0, scrollButtonSize],
  51390. height: height
  51391. }), 'xAxis');
  51392. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  51393. id: 'navigator-y-axis',
  51394. alignTicks: false,
  51395. offset: 0,
  51396. index: yAxisIndex,
  51397. isInternal: true,
  51398. reversed: pick((navigatorOptions.yAxis &&
  51399. navigatorOptions.yAxis.reversed), (chart.yAxis[0] && chart.yAxis[0].reversed), false),
  51400. zoomEnabled: false
  51401. }, chart.inverted ? {
  51402. width: height
  51403. } : {
  51404. height: height
  51405. }), 'yAxis');
  51406. // If we have a base series, initialize the navigator series
  51407. if (baseSeries || navigatorOptions.series.data) {
  51408. navigator.updateNavigatorSeries(false);
  51409. // If not, set up an event to listen for added series
  51410. }
  51411. else if (chart.series.length === 0) {
  51412. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  51413. // We've got one, now add it as base
  51414. if (chart.series.length > 0 && !navigator.series) {
  51415. navigator.setBaseSeries();
  51416. navigator.unbindRedraw(); // reset
  51417. }
  51418. });
  51419. }
  51420. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  51421. // Render items, so we can bind events to them:
  51422. navigator.renderElements();
  51423. // Add mouse events
  51424. navigator.addMouseEvents();
  51425. // in case of scrollbar only, fake an x axis to get translation
  51426. }
  51427. else {
  51428. navigator.xAxis = {
  51429. chart,
  51430. navigatorAxis: {
  51431. fake: true
  51432. },
  51433. translate: function (value, reverse) {
  51434. 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;
  51435. return reverse ?
  51436. // from pixel to value
  51437. (value * valueRange / scrollTrackWidth) + min :
  51438. // from value to pixel
  51439. scrollTrackWidth * (value - min) / valueRange;
  51440. },
  51441. toPixels: function (value) {
  51442. return this.translate(value);
  51443. },
  51444. toValue: function (value) {
  51445. return this.translate(value, true);
  51446. }
  51447. };
  51448. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  51449. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxisAdditions.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  51450. }
  51451. // Initialize the scrollbar
  51452. if (chart.options.scrollbar.enabled) {
  51453. const options = merge(chart.options.scrollbar, { vertical: chart.inverted });
  51454. if (!isNumber(options.margin) && navigator.navigatorEnabled) {
  51455. options.margin = chart.inverted ? -3 : 3;
  51456. }
  51457. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, options, chart);
  51458. addEvent(navigator.scrollbar, 'changed', function (e) {
  51459. const range = navigator.size, to = range * this.to, from = range * this.from;
  51460. navigator.hasDragged = navigator.scrollbar.hasDragged;
  51461. navigator.render(0, 0, from, to);
  51462. if (this.shouldUpdateExtremes(e.DOMType)) {
  51463. setTimeout(function () {
  51464. navigator.onMouseUp(e);
  51465. });
  51466. }
  51467. });
  51468. }
  51469. // Add data events
  51470. navigator.addBaseSeriesEvents();
  51471. // Add redraw events
  51472. navigator.addChartEvents();
  51473. }
  51474. /**
  51475. * Get the union data extremes of the chart - the outer data extremes of the
  51476. * base X axis and the navigator axis.
  51477. *
  51478. * @private
  51479. * @function Highcharts.Navigator#getUnionExtremes
  51480. */
  51481. getUnionExtremes(returnFalseOnNoBaseSeries) {
  51482. const baseAxis = this.chart.xAxis[0], navAxis = this.xAxis, navAxisOptions = navAxis.options, baseAxisOptions = baseAxis.options;
  51483. let ret;
  51484. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  51485. ret = {
  51486. dataMin: pick(// #4053
  51487. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  51488. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  51489. };
  51490. }
  51491. return ret;
  51492. }
  51493. /**
  51494. * Set the base series and update the navigator series from this. With a bit
  51495. * of modification we should be able to make this an API method to be called
  51496. * from the outside
  51497. *
  51498. * @private
  51499. * @function Highcharts.Navigator#setBaseSeries
  51500. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  51501. * Additional series options for a navigator
  51502. * @param {boolean} [redraw]
  51503. * Whether to redraw after update.
  51504. */
  51505. setBaseSeries(baseSeriesOptions, redraw) {
  51506. const chart = this.chart, baseSeries = this.baseSeries = [];
  51507. baseSeriesOptions = (baseSeriesOptions ||
  51508. chart.options && chart.options.navigator.baseSeries ||
  51509. (chart.series.length ?
  51510. // Find the first non-navigator series (#8430)
  51511. find(chart.series, (s) => (!s.options.isInternal)).index :
  51512. 0));
  51513. // Iterate through series and add the ones that should be shown in
  51514. // navigator.
  51515. (chart.series || []).forEach((series, i) => {
  51516. if (
  51517. // Don't include existing nav series
  51518. !series.options.isInternal &&
  51519. (series.options.showInNavigator ||
  51520. (i === baseSeriesOptions ||
  51521. series.options.id === baseSeriesOptions) &&
  51522. series.options.showInNavigator !== false)) {
  51523. baseSeries.push(series);
  51524. }
  51525. });
  51526. // When run after render, this.xAxis already exists
  51527. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  51528. this.updateNavigatorSeries(true, redraw);
  51529. }
  51530. }
  51531. /**
  51532. * Update series in the navigator from baseSeries, adding new if does not
  51533. * exist.
  51534. *
  51535. * @private
  51536. * @function Highcharts.Navigator.updateNavigatorSeries
  51537. */
  51538. updateNavigatorSeries(addEvents, redraw) {
  51539. const navigator = this, chart = navigator.chart, baseSeries = navigator.baseSeries, navSeriesMixin = {
  51540. enableMouseTracking: false,
  51541. index: null,
  51542. linkedTo: null,
  51543. group: 'nav',
  51544. padXAxis: false,
  51545. xAxis: 'navigator-x-axis',
  51546. yAxis: 'navigator-y-axis',
  51547. showInLegend: false,
  51548. stacking: void 0,
  51549. isInternal: true,
  51550. states: {
  51551. inactive: {
  51552. opacity: 1
  51553. }
  51554. }
  51555. },
  51556. // Remove navigator series that are no longer in the baseSeries
  51557. navigatorSeries = navigator.series =
  51558. (navigator.series || []).filter((navSeries) => {
  51559. const base = navSeries.baseSeries;
  51560. if (baseSeries.indexOf(base) < 0) { // Not in array
  51561. // If there is still a base series connected to this
  51562. // series, remove event handler and reference.
  51563. if (base) {
  51564. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  51565. delete base.navigatorSeries;
  51566. }
  51567. // Kill the nav series. It may already have been
  51568. // destroyed (#8715).
  51569. if (navSeries.chart) {
  51570. navSeries.destroy();
  51571. }
  51572. return false;
  51573. }
  51574. return true;
  51575. });
  51576. let baseOptions, mergedNavSeriesOptions, chartNavigatorSeriesOptions = navigator.navigatorOptions.series, baseNavigatorOptions;
  51577. // Go through each base series and merge the options to create new
  51578. // series
  51579. if (baseSeries && baseSeries.length) {
  51580. baseSeries.forEach((base) => {
  51581. const linkedNavSeries = base.navigatorSeries, userNavOptions = extend(
  51582. // Grab color and visibility from base as default
  51583. {
  51584. color: base.color,
  51585. visible: base.visible
  51586. }, !isArray(chartNavigatorSeriesOptions) ?
  51587. chartNavigatorSeriesOptions :
  51588. defaultOptions.navigator.series);
  51589. // Don't update if the series exists in nav and we have disabled
  51590. // adaptToUpdatedData.
  51591. if (linkedNavSeries &&
  51592. navigator.navigatorOptions.adaptToUpdatedData === false) {
  51593. return;
  51594. }
  51595. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  51596. baseOptions = base.options || {};
  51597. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  51598. // The dataLabels options are not merged correctly
  51599. // if the settings are an array, #13847.
  51600. userNavOptions.dataLabels = splat(userNavOptions.dataLabels);
  51601. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  51602. // Once nav series type is resolved, pick correct pointRange
  51603. mergedNavSeriesOptions.pointRange = pick(
  51604. // Stricte set pointRange in options
  51605. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  51606. // Fallback to default values, e.g. `null` for column
  51607. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  51608. // Merge data separately. Do a slice to avoid mutating the
  51609. // navigator options from base series (#4923).
  51610. const navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  51611. navigator.hasNavigatorData =
  51612. navigator.hasNavigatorData || !!navigatorSeriesData;
  51613. mergedNavSeriesOptions.data =
  51614. navigatorSeriesData ||
  51615. baseOptions.data && baseOptions.data.slice(0);
  51616. // Update or add the series
  51617. if (linkedNavSeries && linkedNavSeries.options) {
  51618. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  51619. }
  51620. else {
  51621. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  51622. base.navigatorSeries.baseSeries = base; // Store ref
  51623. navigatorSeries.push(base.navigatorSeries);
  51624. }
  51625. });
  51626. }
  51627. // If user has defined data (and no base series) or explicitly defined
  51628. // navigator.series as an array, we create these series on top of any
  51629. // base series.
  51630. if (chartNavigatorSeriesOptions.data &&
  51631. !(baseSeries && baseSeries.length) ||
  51632. isArray(chartNavigatorSeriesOptions)) {
  51633. navigator.hasNavigatorData = false;
  51634. // Allow navigator.series to be an array
  51635. chartNavigatorSeriesOptions =
  51636. splat(chartNavigatorSeriesOptions);
  51637. chartNavigatorSeriesOptions.forEach((userSeriesOptions, i) => {
  51638. navSeriesMixin.name =
  51639. 'Navigator ' + (navigatorSeries.length + 1);
  51640. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  51641. // Since we don't have a base series to pull color from,
  51642. // try to fake it by using color from series with same
  51643. // index. Otherwise pull from the colors array. We need
  51644. // an explicit color as otherwise updates will increment
  51645. // color counter and we'll get a new color for each
  51646. // update of the nav series.
  51647. color: chart.series[i] &&
  51648. !chart.series[i].options.isInternal &&
  51649. chart.series[i].color ||
  51650. chart.options.colors[i] ||
  51651. chart.options.colors[0]
  51652. }, navSeriesMixin, userSeriesOptions);
  51653. mergedNavSeriesOptions.data = userSeriesOptions.data;
  51654. if (mergedNavSeriesOptions.data) {
  51655. navigator.hasNavigatorData = true;
  51656. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  51657. }
  51658. });
  51659. }
  51660. if (addEvents) {
  51661. this.addBaseSeriesEvents();
  51662. }
  51663. }
  51664. /**
  51665. * Add data events.
  51666. * For example when main series is updated we need to recalculate extremes
  51667. *
  51668. * @private
  51669. * @function Highcharts.Navigator#addBaseSeriesEvent
  51670. */
  51671. addBaseSeriesEvents() {
  51672. const navigator = this, baseSeries = navigator.baseSeries || [];
  51673. // Bind modified extremes event to first base's xAxis only.
  51674. // In event of > 1 base-xAxes, the navigator will ignore those.
  51675. // Adding this multiple times to the same axis is no problem, as
  51676. // duplicates should be discarded by the browser.
  51677. if (baseSeries[0] && baseSeries[0].xAxis) {
  51678. baseSeries[0].eventsToUnbind.push(addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes));
  51679. }
  51680. baseSeries.forEach((base) => {
  51681. // Link base series show/hide to navigator series visibility
  51682. base.eventsToUnbind.push(addEvent(base, 'show', function () {
  51683. if (this.navigatorSeries) {
  51684. this.navigatorSeries.setVisible(true, false);
  51685. }
  51686. }));
  51687. base.eventsToUnbind.push(addEvent(base, 'hide', function () {
  51688. if (this.navigatorSeries) {
  51689. this.navigatorSeries.setVisible(false, false);
  51690. }
  51691. }));
  51692. // Respond to updated data in the base series, unless explicitily
  51693. // not adapting to data changes.
  51694. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  51695. if (base.xAxis) {
  51696. base.eventsToUnbind.push(addEvent(base, 'updatedData', this.updatedDataHandler));
  51697. }
  51698. }
  51699. // Handle series removal
  51700. base.eventsToUnbind.push(addEvent(base, 'remove', function () {
  51701. if (this.navigatorSeries) {
  51702. erase(navigator.series, this.navigatorSeries);
  51703. if (defined(this.navigatorSeries.options)) {
  51704. this.navigatorSeries.remove(false);
  51705. }
  51706. delete this.navigatorSeries;
  51707. }
  51708. }));
  51709. });
  51710. }
  51711. /**
  51712. * Get minimum from all base series connected to the navigator
  51713. * @private
  51714. * @param {number} currentSeriesMin
  51715. * Minium from the current series
  51716. * @return {number}
  51717. * Minimum from all series
  51718. */
  51719. getBaseSeriesMin(currentSeriesMin) {
  51720. return this.baseSeries.reduce(function (min, series) {
  51721. // (#10193)
  51722. return Math.min(min, series.xData && series.xData.length ?
  51723. series.xData[0] : min);
  51724. }, currentSeriesMin);
  51725. }
  51726. /**
  51727. * Set the navigator x axis extremes to reflect the total. The navigator
  51728. * extremes should always be the extremes of the union of all series in the
  51729. * chart as well as the navigator series.
  51730. *
  51731. * @private
  51732. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  51733. */
  51734. modifyNavigatorAxisExtremes() {
  51735. const xAxis = this.xAxis;
  51736. if (typeof xAxis.getExtremes !== 'undefined') {
  51737. const unionExtremes = this.getUnionExtremes(true);
  51738. if (unionExtremes &&
  51739. (unionExtremes.dataMin !== xAxis.min ||
  51740. unionExtremes.dataMax !== xAxis.max)) {
  51741. xAxis.min = unionExtremes.dataMin;
  51742. xAxis.max = unionExtremes.dataMax;
  51743. }
  51744. }
  51745. }
  51746. /**
  51747. * Hook to modify the base axis extremes with information from the Navigator
  51748. *
  51749. * @private
  51750. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  51751. */
  51752. modifyBaseAxisExtremes() {
  51753. 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,
  51754. // When the extremes have been set by range selector button, don't
  51755. // stick to min or max. The range selector buttons will handle the
  51756. // extremes. (#5489)
  51757. unmutable = baseXAxis.eventArgs &&
  51758. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  51759. let newMax, newMin;
  51760. if (!unmutable) {
  51761. // If the zoomed range is already at the min, move it to the right
  51762. // as new data comes in
  51763. if (stickToMin) {
  51764. newMin = baseDataMin;
  51765. newMax = newMin + range;
  51766. }
  51767. // If the zoomed range is already at the max, move it to the right
  51768. // as new data comes in
  51769. if (stickToMax) {
  51770. newMax = baseDataMax + overscroll;
  51771. // If stickToMin is true, the new min value is set above
  51772. if (!stickToMin) {
  51773. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  51774. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  51775. navigatorSeries.xData[0] :
  51776. -Number.MAX_VALUE));
  51777. }
  51778. }
  51779. // Update the extremes
  51780. if (hasSetExtremes && (stickToMin || stickToMax)) {
  51781. if (isNumber(newMin)) {
  51782. baseXAxis.min = baseXAxis.userMin = newMin;
  51783. baseXAxis.max = baseXAxis.userMax = newMax;
  51784. }
  51785. }
  51786. }
  51787. // Reset
  51788. navigator.stickToMin =
  51789. navigator.stickToMax = null;
  51790. }
  51791. /**
  51792. * Handler for updated data on the base series. When data is modified, the
  51793. * navigator series must reflect it. This is called from the Chart.redraw
  51794. * function before axis and series extremes are computed.
  51795. *
  51796. * @private
  51797. * @function Highcharts.Navigator#updateDataHandler
  51798. */
  51799. updatedDataHandler() {
  51800. const navigator = this.chart.navigator, baseSeries = this, navigatorSeries = this.navigatorSeries, shouldStickToMax = navigator.reversedExtremes ?
  51801. Math.round(navigator.zoomedMin) === 0 :
  51802. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  51803. // If the scrollbar is scrolled all the way to the right, keep right as
  51804. // new data comes in, unless user set navigator.stickToMax to false.
  51805. navigator.stickToMax = pick(this.chart.options.navigator &&
  51806. this.chart.options.navigator.stickToMax, shouldStickToMax);
  51807. navigator.stickToMin = navigator.shouldStickToMin(baseSeries, navigator);
  51808. // Set the navigator series data to the new data of the base series
  51809. if (navigatorSeries && !navigator.hasNavigatorData) {
  51810. navigatorSeries.options.pointStart = baseSeries.xData[0];
  51811. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  51812. }
  51813. }
  51814. /**
  51815. * Detect if the zoomed area should stick to the minimum, #14742.
  51816. *
  51817. * @private
  51818. * @function Highcharts.Navigator#shouldStickToMin
  51819. */
  51820. shouldStickToMin(baseSeries, navigator) {
  51821. const xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]), xAxis = baseSeries.xAxis, max = xAxis.max, min = xAxis.min, range = xAxis.options.range;
  51822. let stickToMin = true;
  51823. if (isNumber(max) && isNumber(min)) {
  51824. // If range declared, stick to the minimum only if the range
  51825. // is smaller than the data set range.
  51826. if (range && max - xDataMin > 0) {
  51827. stickToMin = max - xDataMin < range;
  51828. }
  51829. else {
  51830. // If the current axis minimum falls outside the new
  51831. // updated dataset, we must adjust.
  51832. stickToMin = min <= xDataMin;
  51833. }
  51834. }
  51835. else {
  51836. stickToMin = false; // #15864
  51837. }
  51838. return stickToMin;
  51839. }
  51840. /**
  51841. * Add chart events, like redrawing navigator, when chart requires that.
  51842. *
  51843. * @private
  51844. * @function Highcharts.Navigator#addChartEvents
  51845. */
  51846. addChartEvents() {
  51847. if (!this.eventsToUnbind) {
  51848. this.eventsToUnbind = [];
  51849. }
  51850. this.eventsToUnbind.push(
  51851. // Move the scrollbar after redraw, like after data updata even if
  51852. // axes don't redraw
  51853. addEvent(this.chart, 'redraw', function () {
  51854. const navigator = this.navigator, xAxis = navigator && (navigator.baseSeries &&
  51855. navigator.baseSeries[0] &&
  51856. navigator.baseSeries[0].xAxis ||
  51857. this.xAxis[0]); // #5709, #13114
  51858. if (xAxis) {
  51859. navigator.render(xAxis.min, xAxis.max);
  51860. }
  51861. }),
  51862. // Make room for the navigator, can be placed around the chart:
  51863. addEvent(this.chart, 'getMargins', function () {
  51864. let chart = this, navigator = chart.navigator, marginName = navigator.opposite ?
  51865. 'plotTop' : 'marginBottom';
  51866. if (chart.inverted) {
  51867. marginName = navigator.opposite ?
  51868. 'marginRight' : 'plotLeft';
  51869. }
  51870. chart[marginName] =
  51871. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  51872. navigator.height + navigator.scrollbarHeight :
  51873. 0) + navigator.navigatorOptions.margin;
  51874. }));
  51875. }
  51876. /**
  51877. * Destroys allocated elements.
  51878. *
  51879. * @private
  51880. * @function Highcharts.Navigator#destroy
  51881. */
  51882. destroy() {
  51883. // Disconnect events added in addEvents
  51884. this.removeEvents();
  51885. if (this.xAxis) {
  51886. erase(this.chart.xAxis, this.xAxis);
  51887. erase(this.chart.axes, this.xAxis);
  51888. }
  51889. if (this.yAxis) {
  51890. erase(this.chart.yAxis, this.yAxis);
  51891. erase(this.chart.axes, this.yAxis);
  51892. }
  51893. // Destroy series
  51894. (this.series || []).forEach((s) => {
  51895. if (s.destroy) {
  51896. s.destroy();
  51897. }
  51898. });
  51899. // Destroy properties
  51900. [
  51901. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  51902. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  51903. 'rendered'
  51904. ].forEach((prop) => {
  51905. if (this[prop] && this[prop].destroy) {
  51906. this[prop].destroy();
  51907. }
  51908. this[prop] = null;
  51909. });
  51910. // Destroy elements in collection
  51911. [this.handles].forEach((coll) => {
  51912. destroyObjectProperties(coll);
  51913. });
  51914. }
  51915. }
  51916. /* *
  51917. *
  51918. * Default Export
  51919. *
  51920. * */
  51921. return Navigator;
  51922. });
  51923. _registerModule(_modules, 'Stock/RangeSelector/RangeSelectorDefaults.js', [], function () {
  51924. /* *
  51925. *
  51926. * (c) 2010-2021 Torstein Honsi
  51927. *
  51928. * License: www.highcharts.com/license
  51929. *
  51930. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  51931. *
  51932. * */
  51933. /* *
  51934. *
  51935. * Declarations
  51936. *
  51937. * */
  51938. /**
  51939. * Language object. The language object is global and it can't be set
  51940. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  51941. * set it before any chart is initialized.
  51942. *
  51943. * ```js
  51944. * Highcharts.setOptions({
  51945. * lang: {
  51946. * months: [
  51947. * 'Janvier', 'Février', 'Mars', 'Avril',
  51948. * 'Mai', 'Juin', 'Juillet', 'Août',
  51949. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  51950. * ],
  51951. * weekdays: [
  51952. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  51953. * 'Jeudi', 'Vendredi', 'Samedi'
  51954. * ]
  51955. * }
  51956. * });
  51957. * ```
  51958. *
  51959. * @optionparent lang
  51960. */
  51961. const lang = {
  51962. /**
  51963. * The text for the label for the range selector buttons.
  51964. *
  51965. * @product highstock gantt
  51966. */
  51967. rangeSelectorZoom: 'Zoom',
  51968. /**
  51969. * The text for the label for the "from" input box in the range
  51970. * selector. Since v9.0, this string is empty as the label is not
  51971. * rendered by default.
  51972. *
  51973. * @product highstock gantt
  51974. */
  51975. rangeSelectorFrom: '',
  51976. /**
  51977. * The text for the label for the "to" input box in the range selector.
  51978. *
  51979. * @product highstock gantt
  51980. */
  51981. rangeSelectorTo: '→'
  51982. };
  51983. /**
  51984. * The range selector is a tool for selecting ranges to display within
  51985. * the chart. It provides buttons to select preconfigured ranges in
  51986. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  51987. * boxes where min and max dates can be manually input.
  51988. *
  51989. * @product highstock gantt
  51990. * @optionparent rangeSelector
  51991. */
  51992. const rangeSelector = {
  51993. /**
  51994. * Whether to enable all buttons from the start. By default buttons are
  51995. * only enabled if the corresponding time range exists on the X axis,
  51996. * but enabling all buttons allows for dynamically loading different
  51997. * time ranges.
  51998. *
  51999. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  52000. * All buttons enabled
  52001. *
  52002. * @since 2.0.3
  52003. */
  52004. allButtonsEnabled: false,
  52005. /**
  52006. * An array of configuration objects for the buttons.
  52007. *
  52008. * Defaults to:
  52009. * ```js
  52010. * buttons: [{
  52011. * type: 'month',
  52012. * count: 1,
  52013. * text: '1m',
  52014. * title: 'View 1 month'
  52015. * }, {
  52016. * type: 'month',
  52017. * count: 3,
  52018. * text: '3m',
  52019. * title: 'View 3 months'
  52020. * }, {
  52021. * type: 'month',
  52022. * count: 6,
  52023. * text: '6m',
  52024. * title: 'View 6 months'
  52025. * }, {
  52026. * type: 'ytd',
  52027. * text: 'YTD',
  52028. * title: 'View year to date'
  52029. * }, {
  52030. * type: 'year',
  52031. * count: 1,
  52032. * text: '1y',
  52033. * title: 'View 1 year'
  52034. * }, {
  52035. * type: 'all',
  52036. * text: 'All',
  52037. * title: 'View all'
  52038. * }]
  52039. * ```
  52040. *
  52041. * @sample {highstock} stock/rangeselector/datagrouping/
  52042. * Data grouping by buttons
  52043. *
  52044. * @type {Array<*>}
  52045. */
  52046. buttons: void 0,
  52047. /**
  52048. * How many units of the defined type the button should span. If `type`
  52049. * is "month" and `count` is 3, the button spans three months.
  52050. *
  52051. * @type {number}
  52052. * @default 1
  52053. * @apioption rangeSelector.buttons.count
  52054. */
  52055. /**
  52056. * Fires when clicking on the rangeSelector button. One parameter,
  52057. * event, is passed to the function, containing common event
  52058. * information.
  52059. *
  52060. * ```js
  52061. * click: function(e) {
  52062. * console.log(this);
  52063. * }
  52064. * ```
  52065. *
  52066. * Return false to stop default button's click action.
  52067. *
  52068. * @sample {highstock} stock/rangeselector/button-click/
  52069. * Click event on the button
  52070. *
  52071. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  52072. * @apioption rangeSelector.buttons.events.click
  52073. */
  52074. /**
  52075. * Additional range (in milliseconds) added to the end of the calculated
  52076. * time span.
  52077. *
  52078. * @sample {highstock} stock/rangeselector/min-max-offsets/
  52079. * Button offsets
  52080. *
  52081. * @type {number}
  52082. * @default 0
  52083. * @since 6.0.0
  52084. * @apioption rangeSelector.buttons.offsetMax
  52085. */
  52086. /**
  52087. * Additional range (in milliseconds) added to the start of the
  52088. * calculated time span.
  52089. *
  52090. * @sample {highstock} stock/rangeselector/min-max-offsets/
  52091. * Button offsets
  52092. *
  52093. * @type {number}
  52094. * @default 0
  52095. * @since 6.0.0
  52096. * @apioption rangeSelector.buttons.offsetMin
  52097. */
  52098. /**
  52099. * When buttons apply dataGrouping on a series, by default zooming
  52100. * in/out will deselect buttons and unset dataGrouping. Enable this
  52101. * option to keep buttons selected when extremes change.
  52102. *
  52103. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  52104. * Different preserveDataGrouping settings
  52105. *
  52106. * @type {boolean}
  52107. * @default false
  52108. * @since 6.1.2
  52109. * @apioption rangeSelector.buttons.preserveDataGrouping
  52110. */
  52111. /**
  52112. * A custom data grouping object for each button.
  52113. *
  52114. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  52115. *
  52116. * @sample {highstock} stock/rangeselector/datagrouping/
  52117. * Data grouping by range selector buttons
  52118. *
  52119. * @type {*}
  52120. * @extends plotOptions.series.dataGrouping
  52121. * @apioption rangeSelector.buttons.dataGrouping
  52122. */
  52123. /**
  52124. * The text for the button itself.
  52125. *
  52126. * @type {string}
  52127. * @apioption rangeSelector.buttons.text
  52128. */
  52129. /**
  52130. * Explanation for the button, shown as a tooltip on hover, and used by
  52131. * assistive technology.
  52132. *
  52133. * @type {string}
  52134. * @apioption rangeSelector.buttons.title
  52135. */
  52136. /**
  52137. * Defined the time span for the button. Can be one of `millisecond`,
  52138. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  52139. * and `all`.
  52140. *
  52141. * @type {Highcharts.RangeSelectorButtonTypeValue}
  52142. * @apioption rangeSelector.buttons.type
  52143. */
  52144. /**
  52145. * The space in pixels between the buttons in the range selector.
  52146. */
  52147. buttonSpacing: 5,
  52148. /**
  52149. * Whether to collapse the range selector buttons into a dropdown when
  52150. * there is not enough room to show everything in a single row, instead
  52151. * of dividing the range selector into multiple rows.
  52152. * Can be one of the following:
  52153. * - `always`: Always collapse
  52154. * - `responsive`: Only collapse when there is not enough room
  52155. * - `never`: Never collapse
  52156. *
  52157. * @sample {highstock} stock/rangeselector/dropdown/
  52158. * Dropdown option
  52159. *
  52160. * @validvalue ["always", "responsive", "never"]
  52161. * @since 9.0.0
  52162. */
  52163. dropdown: 'responsive',
  52164. /**
  52165. * Enable or disable the range selector. Default to `true` for stock
  52166. * charts, using the `stockChart` factory.
  52167. *
  52168. * @sample {highstock} stock/rangeselector/enabled/
  52169. * Disable the range selector
  52170. *
  52171. * @type {boolean|undefined}
  52172. * @default {highstock} true
  52173. */
  52174. enabled: void 0,
  52175. /**
  52176. * The vertical alignment of the rangeselector box. Allowed properties
  52177. * are `top`, `middle`, `bottom`.
  52178. *
  52179. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  52180. * Middle
  52181. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  52182. * Bottom
  52183. *
  52184. * @type {Highcharts.VerticalAlignValue}
  52185. * @since 6.0.0
  52186. */
  52187. verticalAlign: 'top',
  52188. /**
  52189. * A collection of attributes for the buttons. The object takes SVG
  52190. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  52191. * a collection of CSS properties for the text.
  52192. *
  52193. * The object can also be extended with states, so you can set
  52194. * presentational options for `hover`, `select` or `disabled` button
  52195. * states.
  52196. *
  52197. * CSS styles for the text label.
  52198. *
  52199. * In styled mode, the buttons are styled by the
  52200. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  52201. * different states.
  52202. *
  52203. * @sample {highstock} stock/rangeselector/styling/
  52204. * Styling the buttons and inputs
  52205. *
  52206. * @type {Highcharts.SVGAttributes}
  52207. */
  52208. buttonTheme: {
  52209. /** @ignore */
  52210. width: 28,
  52211. /** @ignore */
  52212. height: 18,
  52213. /** @ignore */
  52214. padding: 2,
  52215. /** @ignore */
  52216. zIndex: 7 // #484, #852
  52217. },
  52218. /**
  52219. * When the rangeselector is floating, the plot area does not reserve
  52220. * space for it. This opens for positioning anywhere on the chart.
  52221. *
  52222. * @sample {highstock} stock/rangeselector/floating/
  52223. * Placing the range selector between the plot area and the
  52224. * navigator
  52225. *
  52226. * @since 6.0.0
  52227. */
  52228. floating: false,
  52229. /**
  52230. * The x offset of the range selector relative to its horizontal
  52231. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  52232. *
  52233. * @since 6.0.0
  52234. */
  52235. x: 0,
  52236. /**
  52237. * The y offset of the range selector relative to its horizontal
  52238. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  52239. *
  52240. * @since 6.0.0
  52241. */
  52242. y: 0,
  52243. /**
  52244. * Deprecated. The height of the range selector. Currently it is
  52245. * calculated dynamically.
  52246. *
  52247. * @deprecated
  52248. * @type {number|undefined}
  52249. * @since 2.1.9
  52250. */
  52251. height: void 0,
  52252. /**
  52253. * The border color of the date input boxes.
  52254. *
  52255. * @sample {highstock} stock/rangeselector/styling/
  52256. * Styling the buttons and inputs
  52257. *
  52258. * @type {Highcharts.ColorString}
  52259. * @since 1.3.7
  52260. */
  52261. inputBoxBorderColor: 'none',
  52262. /**
  52263. * The pixel height of the date input boxes.
  52264. *
  52265. * @sample {highstock} stock/rangeselector/styling/
  52266. * Styling the buttons and inputs
  52267. *
  52268. * @since 1.3.7
  52269. */
  52270. inputBoxHeight: 17,
  52271. /**
  52272. * The pixel width of the date input boxes. When `undefined`, the width
  52273. * is fitted to the rendered content.
  52274. *
  52275. * @sample {highstock} stock/rangeselector/styling/
  52276. * Styling the buttons and inputs
  52277. *
  52278. * @type {number|undefined}
  52279. * @since 1.3.7
  52280. */
  52281. inputBoxWidth: void 0,
  52282. /**
  52283. * The date format in the input boxes when not selected for editing.
  52284. * Defaults to `%e %b %Y`.
  52285. *
  52286. * This is used to determine which type of input to show,
  52287. * `datetime-local`, `date` or `time` and falling back to `text` when
  52288. * the browser does not support the input type or the format contains
  52289. * milliseconds.
  52290. *
  52291. * @sample {highstock} stock/rangeselector/input-type/
  52292. * Input types
  52293. * @sample {highstock} stock/rangeselector/input-format/
  52294. * Milliseconds in the range selector
  52295. *
  52296. */
  52297. inputDateFormat: '%e %b %Y',
  52298. /**
  52299. * A custom callback function to parse values entered in the input boxes
  52300. * and return a valid JavaScript time as milliseconds since 1970.
  52301. * The first argument passed is a value to parse,
  52302. * second is a boolean indicating use of the UTC time.
  52303. *
  52304. * This will only get called for inputs of type `text`. Since v8.2.3,
  52305. * the input type is dynamically determined based on the granularity
  52306. * of the `inputDateFormat` and the browser support.
  52307. *
  52308. * @sample {highstock} stock/rangeselector/input-format/
  52309. * Milliseconds in the range selector
  52310. *
  52311. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  52312. * @since 1.3.3
  52313. */
  52314. inputDateParser: void 0,
  52315. /**
  52316. * The date format in the input boxes when they are selected for
  52317. * editing. This must be a format that is recognized by JavaScript
  52318. * Date.parse.
  52319. *
  52320. * This will only be used for inputs of type `text`. Since v8.2.3,
  52321. * the input type is dynamically determined based on the granularity
  52322. * of the `inputDateFormat` and the browser support.
  52323. *
  52324. * @sample {highstock} stock/rangeselector/input-format/
  52325. * Milliseconds in the range selector
  52326. *
  52327. */
  52328. inputEditDateFormat: '%Y-%m-%d',
  52329. /**
  52330. * Enable or disable the date input boxes.
  52331. */
  52332. inputEnabled: true,
  52333. /**
  52334. * Positioning for the input boxes. Allowed properties are `align`,
  52335. * `x` and `y`.
  52336. *
  52337. * @since 1.2.4
  52338. */
  52339. inputPosition: {
  52340. /**
  52341. * The alignment of the input box. Allowed properties are `left`,
  52342. * `center`, `right`.
  52343. *
  52344. * @sample {highstock} stock/rangeselector/input-button-position/
  52345. * Alignment
  52346. *
  52347. * @type {Highcharts.AlignValue}
  52348. * @since 6.0.0
  52349. */
  52350. align: 'right',
  52351. /**
  52352. * X offset of the input row.
  52353. */
  52354. x: 0,
  52355. /**
  52356. * Y offset of the input row.
  52357. */
  52358. y: 0
  52359. },
  52360. /**
  52361. * The space in pixels between the labels and the date input boxes in
  52362. * the range selector.
  52363. *
  52364. * @since 9.0.0
  52365. */
  52366. inputSpacing: 5,
  52367. /**
  52368. * The index of the button to appear pre-selected.
  52369. *
  52370. * @type {number}
  52371. */
  52372. selected: void 0,
  52373. /**
  52374. * Positioning for the button row.
  52375. *
  52376. * @since 1.2.4
  52377. */
  52378. buttonPosition: {
  52379. /**
  52380. * The alignment of the input box. Allowed properties are `left`,
  52381. * `center`, `right`.
  52382. *
  52383. * @sample {highstock} stock/rangeselector/input-button-position/
  52384. * Alignment
  52385. *
  52386. * @type {Highcharts.AlignValue}
  52387. * @since 6.0.0
  52388. */
  52389. align: 'left',
  52390. /**
  52391. * X offset of the button row.
  52392. */
  52393. x: 0,
  52394. /**
  52395. * Y offset of the button row.
  52396. */
  52397. y: 0
  52398. },
  52399. /**
  52400. * CSS for the HTML inputs in the range selector.
  52401. *
  52402. * In styled mode, the inputs are styled by the
  52403. * `.highcharts-range-input text` rule in SVG mode, and
  52404. * `input.highcharts-range-selector` when active.
  52405. *
  52406. * @sample {highstock} stock/rangeselector/styling/
  52407. * Styling the buttons and inputs
  52408. *
  52409. * @type {Highcharts.CSSObject}
  52410. * @apioption rangeSelector.inputStyle
  52411. */
  52412. inputStyle: {
  52413. /** @ignore */
  52414. color: "#334eff" /* Palette.highlightColor80 */,
  52415. /** @ignore */
  52416. cursor: 'pointer',
  52417. /** @ignore */
  52418. fontSize: '0.8em'
  52419. },
  52420. /**
  52421. * CSS styles for the labels - the Zoom, From and To texts.
  52422. *
  52423. * In styled mode, the labels are styled by the
  52424. * `.highcharts-range-label` class.
  52425. *
  52426. * @sample {highstock} stock/rangeselector/styling/
  52427. * Styling the buttons and inputs
  52428. *
  52429. * @type {Highcharts.CSSObject}
  52430. */
  52431. labelStyle: {
  52432. /** @ignore */
  52433. color: "#666666" /* Palette.neutralColor60 */,
  52434. /** @ignore */
  52435. fontSize: '0.8em'
  52436. }
  52437. };
  52438. /* *
  52439. *
  52440. * Default Export
  52441. *
  52442. * */
  52443. const RangeSelectorDefaults = {
  52444. lang,
  52445. rangeSelector
  52446. };
  52447. return RangeSelectorDefaults;
  52448. });
  52449. _registerModule(_modules, 'Stock/RangeSelector/RangeSelectorComposition.js', [_modules['Core/Defaults.js'], _modules['Stock/RangeSelector/RangeSelectorDefaults.js'], _modules['Core/Utilities.js']], function (D, RangeSelectorDefaults, U) {
  52450. /* *
  52451. *
  52452. * (c) 2010-2021 Torstein Honsi
  52453. *
  52454. * License: www.highcharts.com/license
  52455. *
  52456. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  52457. *
  52458. * */
  52459. const { defaultOptions, setOptions } = D;
  52460. const { addEvent, defined, extend, find, isNumber, merge, pick } = U;
  52461. /* *
  52462. *
  52463. * Constants
  52464. *
  52465. * */
  52466. const chartDestroyEvents = [];
  52467. const composedMembers = [];
  52468. /* *
  52469. *
  52470. * Variables
  52471. *
  52472. * */
  52473. let RangeSelectorConstructor;
  52474. /* *
  52475. *
  52476. * Functions
  52477. *
  52478. * */
  52479. /**
  52480. * Get the axis min value based on the range option and the current max. For
  52481. * stock charts this is extended via the {@link RangeSelector} so that if the
  52482. * selected range is a multiple of months or years, it is compensated for
  52483. * various month lengths.
  52484. *
  52485. * @private
  52486. * @function Highcharts.Axis#minFromRange
  52487. * @return {number|undefined}
  52488. * The new minimum value.
  52489. */
  52490. function axisMinFromRange() {
  52491. const rangeOptions = this.range, type = rangeOptions.type, max = this.max, time = this.chart.time,
  52492. // Get the true range from a start date
  52493. getTrueRange = function (base, count) {
  52494. const timeName = type === 'year' ?
  52495. 'FullYear' : 'Month';
  52496. const date = new time.Date(base);
  52497. const basePeriod = time.get(timeName, date);
  52498. time.set(timeName, date, basePeriod + count);
  52499. if (basePeriod === time.get(timeName, date)) {
  52500. time.set('Date', date, 0); // #6537
  52501. }
  52502. return date.getTime() - base;
  52503. };
  52504. let min, range;
  52505. if (isNumber(rangeOptions)) {
  52506. min = max - rangeOptions;
  52507. range = rangeOptions;
  52508. }
  52509. else if (rangeOptions) {
  52510. min = max + getTrueRange(max, -(rangeOptions.count || 1));
  52511. // Let the fixedRange reflect initial settings (#5930)
  52512. if (this.chart) {
  52513. this.chart.fixedRange = max - min;
  52514. }
  52515. }
  52516. const dataMin = pick(this.dataMin, Number.MIN_VALUE);
  52517. if (!isNumber(min)) {
  52518. min = dataMin;
  52519. }
  52520. if (min <= dataMin) {
  52521. min = dataMin;
  52522. if (typeof range === 'undefined') { // #4501
  52523. range = getTrueRange(min, rangeOptions.count);
  52524. }
  52525. this.newMax = Math.min(min + range, pick(this.dataMax, Number.MAX_VALUE));
  52526. }
  52527. if (!isNumber(max)) {
  52528. min = void 0;
  52529. }
  52530. else if (!isNumber(rangeOptions) &&
  52531. rangeOptions &&
  52532. rangeOptions._offsetMin) {
  52533. min += rangeOptions._offsetMin;
  52534. }
  52535. return min;
  52536. }
  52537. /**
  52538. * @private
  52539. */
  52540. function compose(AxisClass, ChartClass, RangeSelectorClass) {
  52541. RangeSelectorConstructor = RangeSelectorClass;
  52542. if (U.pushUnique(composedMembers, AxisClass)) {
  52543. AxisClass.prototype.minFromRange = axisMinFromRange;
  52544. }
  52545. if (U.pushUnique(composedMembers, ChartClass)) {
  52546. addEvent(ChartClass, 'afterGetContainer', onChartAfterGetContainer);
  52547. addEvent(ChartClass, 'beforeRender', onChartBeforeRender);
  52548. addEvent(ChartClass, 'destroy', onChartDestroy);
  52549. addEvent(ChartClass, 'getMargins', onChartGetMargins);
  52550. addEvent(ChartClass, 'render', onChartRender);
  52551. addEvent(ChartClass, 'update', onChartUpdate);
  52552. const chartProto = ChartClass.prototype;
  52553. chartProto.callbacks.push(onChartCallback);
  52554. }
  52555. if (U.pushUnique(composedMembers, setOptions)) {
  52556. extend(defaultOptions, { rangeSelector: RangeSelectorDefaults.rangeSelector });
  52557. extend(defaultOptions.lang, RangeSelectorDefaults.lang);
  52558. }
  52559. }
  52560. /**
  52561. * Initialize rangeselector for stock charts
  52562. * @private
  52563. */
  52564. function onChartAfterGetContainer() {
  52565. if (this.options.rangeSelector &&
  52566. this.options.rangeSelector.enabled) {
  52567. this.rangeSelector = new RangeSelectorConstructor(this);
  52568. }
  52569. }
  52570. /**
  52571. * @private
  52572. */
  52573. function onChartBeforeRender() {
  52574. const chart = this, axes = chart.axes, rangeSelector = chart.rangeSelector;
  52575. if (rangeSelector) {
  52576. if (isNumber(rangeSelector.deferredYTDClick)) {
  52577. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  52578. delete rangeSelector.deferredYTDClick;
  52579. }
  52580. axes.forEach((axis) => {
  52581. axis.updateNames();
  52582. axis.setScale();
  52583. });
  52584. chart.getAxisMargins();
  52585. rangeSelector.render();
  52586. const verticalAlign = rangeSelector.options.verticalAlign;
  52587. if (!rangeSelector.options.floating) {
  52588. if (verticalAlign === 'bottom') {
  52589. this.extraBottomMargin = true;
  52590. }
  52591. else if (verticalAlign !== 'middle') {
  52592. this.extraTopMargin = true;
  52593. }
  52594. }
  52595. }
  52596. }
  52597. /**
  52598. * @private
  52599. */
  52600. function onChartCallback(chart) {
  52601. let extremes, legend, alignTo, verticalAlign;
  52602. const rangeSelector = chart.rangeSelector, redraw = () => {
  52603. if (rangeSelector) {
  52604. extremes = chart.xAxis[0].getExtremes();
  52605. legend = chart.legend;
  52606. verticalAlign = (rangeSelector &&
  52607. rangeSelector.options.verticalAlign);
  52608. if (isNumber(extremes.min)) {
  52609. rangeSelector.render(extremes.min, extremes.max);
  52610. }
  52611. // Re-align the legend so that it's below the rangeselector
  52612. if (legend.display &&
  52613. verticalAlign === 'top' &&
  52614. verticalAlign === legend.options.verticalAlign) {
  52615. // Create a new alignment box for the legend.
  52616. alignTo = merge(chart.spacingBox);
  52617. if (legend.options.layout === 'vertical') {
  52618. alignTo.y = chart.plotTop;
  52619. }
  52620. else {
  52621. alignTo.y += rangeSelector.getHeight();
  52622. }
  52623. legend.group.placed = false; // Don't animate the alignment.
  52624. legend.align(alignTo);
  52625. }
  52626. }
  52627. };
  52628. if (rangeSelector) {
  52629. const events = find(chartDestroyEvents, (e) => e[0] === chart);
  52630. if (!events) {
  52631. chartDestroyEvents.push([chart, [
  52632. // redraw the scroller on setExtremes
  52633. addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  52634. if (rangeSelector) {
  52635. rangeSelector.render(e.min, e.max);
  52636. }
  52637. }),
  52638. // redraw the scroller chart resize
  52639. addEvent(chart, 'redraw', redraw)
  52640. ]]);
  52641. }
  52642. // do it now
  52643. redraw();
  52644. }
  52645. }
  52646. /**
  52647. * Remove resize/afterSetExtremes at chart destroy.
  52648. * @private
  52649. */
  52650. function onChartDestroy() {
  52651. for (let i = 0, iEnd = chartDestroyEvents.length; i < iEnd; ++i) {
  52652. const events = chartDestroyEvents[i];
  52653. if (events[0] === this) {
  52654. events[1].forEach((unbind) => unbind());
  52655. chartDestroyEvents.splice(i, 1);
  52656. return;
  52657. }
  52658. }
  52659. }
  52660. function onChartGetMargins() {
  52661. const rangeSelector = this.rangeSelector;
  52662. if (rangeSelector) {
  52663. const rangeSelectorHeight = rangeSelector.getHeight();
  52664. if (this.extraTopMargin) {
  52665. this.plotTop += rangeSelectorHeight;
  52666. }
  52667. if (this.extraBottomMargin) {
  52668. this.marginBottom += rangeSelectorHeight;
  52669. }
  52670. }
  52671. }
  52672. /**
  52673. * @private
  52674. */
  52675. function onChartRender() {
  52676. const chart = this, rangeSelector = chart.rangeSelector;
  52677. if (rangeSelector && !rangeSelector.options.floating) {
  52678. rangeSelector.render();
  52679. const verticalAlign = rangeSelector.options.verticalAlign;
  52680. if (verticalAlign === 'bottom') {
  52681. this.extraBottomMargin = true;
  52682. }
  52683. else if (verticalAlign !== 'middle') {
  52684. this.extraTopMargin = true;
  52685. }
  52686. }
  52687. }
  52688. /**
  52689. * @private
  52690. */
  52691. function onChartUpdate(e) {
  52692. const chart = this, options = e.options, optionsRangeSelector = options.rangeSelector, extraBottomMarginWas = this.extraBottomMargin, extraTopMarginWas = this.extraTopMargin;
  52693. let rangeSelector = chart.rangeSelector;
  52694. if (optionsRangeSelector &&
  52695. optionsRangeSelector.enabled &&
  52696. !defined(rangeSelector) &&
  52697. this.options.rangeSelector) {
  52698. this.options.rangeSelector.enabled = true;
  52699. this.rangeSelector = rangeSelector = new RangeSelectorConstructor(this);
  52700. }
  52701. this.extraBottomMargin = false;
  52702. this.extraTopMargin = false;
  52703. if (rangeSelector) {
  52704. onChartCallback(this);
  52705. const verticalAlign = (optionsRangeSelector &&
  52706. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  52707. if (!rangeSelector.options.floating) {
  52708. if (verticalAlign === 'bottom') {
  52709. this.extraBottomMargin = true;
  52710. }
  52711. else if (verticalAlign !== 'middle') {
  52712. this.extraTopMargin = true;
  52713. }
  52714. }
  52715. if (this.extraBottomMargin !== extraBottomMarginWas ||
  52716. this.extraTopMargin !== extraTopMarginWas) {
  52717. this.isDirtyBox = true;
  52718. }
  52719. }
  52720. }
  52721. /* *
  52722. *
  52723. * Default Export
  52724. *
  52725. * */
  52726. const RangeSelectorComposition = {
  52727. compose
  52728. };
  52729. return RangeSelectorComposition;
  52730. });
  52731. _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) {
  52732. /* *
  52733. *
  52734. * (c) 2010-2021 Torstein Honsi
  52735. *
  52736. * License: www.highcharts.com/license
  52737. *
  52738. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  52739. *
  52740. * */
  52741. const { defaultOptions } = D;
  52742. const { addEvent, createElement, css, defined, destroyObjectProperties, discardElement, extend, fireEvent, isNumber, merge, objectEach, pad, pick, pInt, splat } = U;
  52743. /* *
  52744. *
  52745. * Functions
  52746. *
  52747. * */
  52748. /**
  52749. * Get the preferred input type based on a date format string.
  52750. *
  52751. * @private
  52752. * @function preferredInputType
  52753. */
  52754. function preferredInputType(format) {
  52755. const ms = format.indexOf('%L') !== -1;
  52756. if (ms) {
  52757. return 'text';
  52758. }
  52759. const date = ['a', 'A', 'd', 'e', 'w', 'b', 'B', 'm', 'o', 'y', 'Y']
  52760. .some((char) => format.indexOf('%' + char) !== -1);
  52761. const time = ['H', 'k', 'I', 'l', 'M', 'S']
  52762. .some((char) => format.indexOf('%' + char) !== -1);
  52763. if (date && time) {
  52764. return 'datetime-local';
  52765. }
  52766. if (date) {
  52767. return 'date';
  52768. }
  52769. if (time) {
  52770. return 'time';
  52771. }
  52772. return 'text';
  52773. }
  52774. /* *
  52775. *
  52776. * Class
  52777. *
  52778. * */
  52779. /**
  52780. * The range selector.
  52781. *
  52782. * @private
  52783. * @class
  52784. * @name Highcharts.RangeSelector
  52785. * @param {Highcharts.Chart} chart
  52786. */
  52787. class RangeSelector {
  52788. /* *
  52789. *
  52790. * Static Functions
  52791. *
  52792. * */
  52793. /**
  52794. * @private
  52795. */
  52796. static compose(AxisClass, ChartClass) {
  52797. RangeSelectorComposition.compose(AxisClass, ChartClass, RangeSelector);
  52798. }
  52799. /* *
  52800. *
  52801. * Constructor
  52802. *
  52803. * */
  52804. constructor(chart) {
  52805. /* *
  52806. *
  52807. * Properties
  52808. *
  52809. * */
  52810. this.buttons = void 0;
  52811. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  52812. this.initialButtonGroupWidth = 0;
  52813. this.options = void 0;
  52814. this.chart = chart;
  52815. this.init(chart);
  52816. }
  52817. /* *
  52818. *
  52819. * Functions
  52820. *
  52821. * */
  52822. /**
  52823. * The method to run when one of the buttons in the range selectors is
  52824. * clicked
  52825. *
  52826. * @private
  52827. * @function Highcharts.RangeSelector#clickButton
  52828. * @param {number} i
  52829. * The index of the button
  52830. * @param {boolean} [redraw]
  52831. */
  52832. clickButton(i, redraw) {
  52833. 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;
  52834. let dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, newMin, newMax = baseAxis && Math.round(Math.min(baseAxis.max, pick(dataMax, baseAxis.max))), // #1568
  52835. baseXAxisOptions, range = rangeOptions._range, rangeMin, minSetting, rangeSetting, ctx, ytdExtremes, addOffsetMin = true;
  52836. // chart has no data, base series is removed
  52837. if (dataMin === null || dataMax === null) {
  52838. return;
  52839. }
  52840. // Set the fixed range before range is altered
  52841. chart.fixedRange = range;
  52842. rangeSelector.setSelected(i);
  52843. // Apply dataGrouping associated to button
  52844. if (dataGrouping) {
  52845. this.forcedDataGrouping = true;
  52846. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  52847. this.frozenStates = rangeOptions.preserveDataGrouping;
  52848. }
  52849. // Apply range
  52850. if (type === 'month' || type === 'year') {
  52851. if (!baseAxis) {
  52852. // This is set to the user options and picked up later when the
  52853. // axis is instantiated so that we know the min and max.
  52854. range = rangeOptions;
  52855. }
  52856. else {
  52857. ctx = {
  52858. range: rangeOptions,
  52859. max: newMax,
  52860. chart: chart,
  52861. dataMin: dataMin,
  52862. dataMax: dataMax
  52863. };
  52864. newMin = baseAxis.minFromRange.call(ctx);
  52865. if (isNumber(ctx.newMax)) {
  52866. newMax = ctx.newMax;
  52867. }
  52868. // #15799: offsetMin is added in minFromRange so that it works
  52869. // with pre-selected buttons as well
  52870. addOffsetMin = false;
  52871. }
  52872. // Fixed times like minutes, hours, days
  52873. }
  52874. else if (range) {
  52875. newMin = Math.max(newMax - range, dataMin);
  52876. newMax = Math.min(newMin + range, dataMax);
  52877. addOffsetMin = false;
  52878. }
  52879. else if (type === 'ytd') {
  52880. // On user clicks on the buttons, or a delayed action running from
  52881. // the beforeRender event (below), the baseAxis is defined.
  52882. if (baseAxis) {
  52883. // When "ytd" is the pre-selected button for the initial view,
  52884. // its calculation is delayed and rerun in the beforeRender
  52885. // event (below). When the series are initialized, but before
  52886. // the chart is rendered, we have access to the xData array
  52887. // (#942).
  52888. if (typeof dataMax === 'undefined' ||
  52889. typeof dataMin === 'undefined') {
  52890. dataMin = Number.MAX_VALUE;
  52891. dataMax = Number.MIN_VALUE;
  52892. chart.series.forEach((series) => {
  52893. // reassign it to the last item
  52894. const xData = series.xData;
  52895. if (xData) {
  52896. dataMin = Math.min(xData[0], dataMin);
  52897. dataMax = Math.max(xData[xData.length - 1], dataMax);
  52898. }
  52899. });
  52900. redraw = false;
  52901. }
  52902. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  52903. newMin = rangeMin = ytdExtremes.min;
  52904. newMax = ytdExtremes.max;
  52905. // "ytd" is pre-selected. We don't yet have access to processed
  52906. // point and extremes data (things like pointStart and pointInterval
  52907. // are missing), so we delay the process (#942)
  52908. }
  52909. else {
  52910. rangeSelector.deferredYTDClick = i;
  52911. return;
  52912. }
  52913. }
  52914. else if (type === 'all' && baseAxis) {
  52915. // If the navigator exist and the axis range is declared reset that
  52916. // range and from now on only use the range set by a user, #14742.
  52917. if (chart.navigator && chart.navigator.baseSeries[0]) {
  52918. chart.navigator.baseSeries[0].xAxis.options.range = void 0;
  52919. }
  52920. newMin = dataMin;
  52921. newMax = dataMax;
  52922. }
  52923. if (addOffsetMin && rangeOptions._offsetMin && defined(newMin)) {
  52924. newMin += rangeOptions._offsetMin;
  52925. }
  52926. if (rangeOptions._offsetMax && defined(newMax)) {
  52927. newMax += rangeOptions._offsetMax;
  52928. }
  52929. if (this.dropdown) {
  52930. this.dropdown.selectedIndex = i + 1;
  52931. }
  52932. // Update the chart
  52933. if (!baseAxis) {
  52934. // Axis not yet instanciated. Temporarily set min and range
  52935. // options and remove them on chart load (#4317).
  52936. baseXAxisOptions = splat(chart.options.xAxis)[0];
  52937. rangeSetting = baseXAxisOptions.range;
  52938. baseXAxisOptions.range = range;
  52939. minSetting = baseXAxisOptions.min;
  52940. baseXAxisOptions.min = rangeMin;
  52941. addEvent(chart, 'load', function resetMinAndRange() {
  52942. baseXAxisOptions.range = rangeSetting;
  52943. baseXAxisOptions.min = minSetting;
  52944. });
  52945. }
  52946. else {
  52947. // Existing axis object. Set extremes after render time.
  52948. baseAxis.setExtremes(newMin, newMax, pick(redraw, true), void 0, // auto animation
  52949. {
  52950. trigger: 'rangeSelectorButton',
  52951. rangeSelectorButton: rangeOptions
  52952. });
  52953. }
  52954. fireEvent(this, 'afterBtnClick');
  52955. }
  52956. /**
  52957. * Set the selected option. This method only sets the internal flag, it
  52958. * doesn't update the buttons or the actual zoomed range.
  52959. *
  52960. * @private
  52961. * @function Highcharts.RangeSelector#setSelected
  52962. * @param {number} [selected]
  52963. */
  52964. setSelected(selected) {
  52965. this.selected = this.options.selected = selected;
  52966. }
  52967. /**
  52968. * Initialize the range selector
  52969. *
  52970. * @private
  52971. * @function Highcharts.RangeSelector#init
  52972. * @param {Highcharts.Chart} chart
  52973. */
  52974. init(chart) {
  52975. const rangeSelector = this, options = chart.options.rangeSelector, buttonOptions = (options.buttons || rangeSelector.defaultButtons.slice()), selectedOption = options.selected, blurInputs = function () {
  52976. const minInput = rangeSelector.minInput, maxInput = rangeSelector.maxInput;
  52977. // #3274 in some case blur is not defined
  52978. if (minInput && (minInput.blur)) {
  52979. fireEvent(minInput, 'blur');
  52980. }
  52981. if (maxInput && (maxInput.blur)) {
  52982. fireEvent(maxInput, 'blur');
  52983. }
  52984. };
  52985. rangeSelector.chart = chart;
  52986. rangeSelector.options = options;
  52987. rangeSelector.buttons = [];
  52988. rangeSelector.buttonOptions = buttonOptions;
  52989. this.eventsToUnbind = [];
  52990. this.eventsToUnbind.push(addEvent(chart.container, 'mousedown', blurInputs));
  52991. this.eventsToUnbind.push(addEvent(chart, 'resize', blurInputs));
  52992. // Extend the buttonOptions with actual range
  52993. buttonOptions.forEach(rangeSelector.computeButtonRange);
  52994. // zoomed range based on a pre-selected button index
  52995. if (typeof selectedOption !== 'undefined' &&
  52996. buttonOptions[selectedOption]) {
  52997. this.clickButton(selectedOption, false);
  52998. }
  52999. this.eventsToUnbind.push(addEvent(chart, 'load', function () {
  53000. // If a data grouping is applied to the current button, release it
  53001. // when extremes change
  53002. if (chart.xAxis && chart.xAxis[0]) {
  53003. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  53004. if (this.max - this.min !==
  53005. chart.fixedRange &&
  53006. e.trigger !== 'rangeSelectorButton' &&
  53007. e.trigger !== 'updatedData' &&
  53008. rangeSelector.forcedDataGrouping &&
  53009. !rangeSelector.frozenStates) {
  53010. this.setDataGrouping(false, false);
  53011. }
  53012. });
  53013. }
  53014. }));
  53015. }
  53016. /**
  53017. * Dynamically update the range selector buttons after a new range has been
  53018. * set
  53019. *
  53020. * @private
  53021. * @function Highcharts.RangeSelector#updateButtonStates
  53022. */
  53023. updateButtonStates() {
  53024. 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
  53025. unionExtremes = (chart.scroller &&
  53026. 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;
  53027. let selectedExists = isNumber(selected);
  53028. rangeSelector.buttonOptions.forEach((rangeOptions, i) => {
  53029. const range = rangeOptions._range, type = rangeOptions.type, count = rangeOptions.count || 1, button = buttons[i], offsetRange = rangeOptions._offsetMax -
  53030. rangeOptions._offsetMin, isSelected = i === selected,
  53031. // Disable buttons where the range exceeds what is allowed in
  53032. // the current view
  53033. isTooGreatRange = range >
  53034. dataMax - dataMin,
  53035. // Disable buttons where the range is smaller than the minimum
  53036. // range
  53037. isTooSmallRange = range < baseAxis.minRange;
  53038. let state = 0,
  53039. // Do not select the YTD button if not explicitly told so
  53040. isYTDButNotSelected = false,
  53041. // Disable the All button if we're already showing all
  53042. isAllButAlreadyShowingAll = false, isSameRange = range === actualRange;
  53043. // Months and years have a variable range so we check the extremes
  53044. if ((type === 'month' || type === 'year') &&
  53045. (actualRange + 36e5 >=
  53046. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  53047. (actualRange - 36e5 <=
  53048. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  53049. isSameRange = true;
  53050. }
  53051. else if (type === 'ytd') {
  53052. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  53053. isYTDButNotSelected = !isSelected;
  53054. }
  53055. else if (type === 'all') {
  53056. isSameRange = (baseAxis.max - baseAxis.min >=
  53057. dataMax - dataMin);
  53058. isAllButAlreadyShowingAll = (!isSelected &&
  53059. selectedExists &&
  53060. isSameRange);
  53061. }
  53062. // The new zoom area happens to match the range for a button - mark
  53063. // it selected. This happens when scrolling across an ordinal gap.
  53064. // It can be seen in the intraday demos when selecting 1h and scroll
  53065. // across the night gap.
  53066. const disable = (!allButtonsEnabled &&
  53067. (isTooGreatRange ||
  53068. isTooSmallRange ||
  53069. isAllButAlreadyShowingAll ||
  53070. hasNoData));
  53071. const select = ((isSelected && isSameRange) ||
  53072. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  53073. (isSelected && rangeSelector.frozenStates));
  53074. if (disable) {
  53075. state = 3;
  53076. }
  53077. else if (select) {
  53078. selectedExists = true; // Only one button can be selected
  53079. state = 2;
  53080. }
  53081. // If state has changed, update the button
  53082. if (button.state !== state) {
  53083. button.setState(state);
  53084. if (dropdown) {
  53085. dropdown.options[i + 1].disabled = disable;
  53086. if (state === 2) {
  53087. dropdown.selectedIndex = i + 1;
  53088. }
  53089. }
  53090. // Reset (#9209)
  53091. if (state === 0 && selected === i) {
  53092. rangeSelector.setSelected();
  53093. }
  53094. }
  53095. });
  53096. }
  53097. /**
  53098. * Compute and cache the range for an individual button
  53099. *
  53100. * @private
  53101. * @function Highcharts.RangeSelector#computeButtonRange
  53102. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  53103. */
  53104. computeButtonRange(rangeOptions) {
  53105. const type = rangeOptions.type, count = rangeOptions.count || 1,
  53106. // these time intervals have a fixed number of milliseconds, as
  53107. // opposed to month, ytd and year
  53108. fixedTimes = {
  53109. millisecond: 1,
  53110. second: 1000,
  53111. minute: 60 * 1000,
  53112. hour: 3600 * 1000,
  53113. day: 24 * 3600 * 1000,
  53114. week: 7 * 24 * 3600 * 1000
  53115. };
  53116. // Store the range on the button object
  53117. if (fixedTimes[type]) {
  53118. rangeOptions._range = fixedTimes[type] * count;
  53119. }
  53120. else if (type === 'month' || type === 'year') {
  53121. rangeOptions._range = {
  53122. month: 30,
  53123. year: 365
  53124. }[type] * 24 * 36e5 * count;
  53125. }
  53126. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  53127. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  53128. rangeOptions._range +=
  53129. rangeOptions._offsetMax - rangeOptions._offsetMin;
  53130. }
  53131. /**
  53132. * Get the unix timestamp of a HTML input for the dates
  53133. *
  53134. * @private
  53135. * @function Highcharts.RangeSelector#getInputValue
  53136. */
  53137. getInputValue(name) {
  53138. const input = name === 'min' ? this.minInput : this.maxInput;
  53139. const options = this.chart.options
  53140. .rangeSelector;
  53141. const time = this.chart.time;
  53142. if (input) {
  53143. return ((input.type === 'text' && options.inputDateParser) ||
  53144. this.defaultInputDateParser)(input.value, time.useUTC, time);
  53145. }
  53146. return 0;
  53147. }
  53148. /**
  53149. * Set the internal and displayed value of a HTML input for the dates
  53150. *
  53151. * @private
  53152. * @function Highcharts.RangeSelector#setInputValue
  53153. */
  53154. setInputValue(name, inputTime) {
  53155. const options = this.options, time = this.chart.time, input = name === 'min' ? this.minInput : this.maxInput, dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  53156. if (input) {
  53157. const hcTimeAttr = input.getAttribute('data-hc-time');
  53158. let updatedTime = defined(hcTimeAttr) ? Number(hcTimeAttr) : void 0;
  53159. if (defined(inputTime)) {
  53160. const previousTime = updatedTime;
  53161. if (defined(previousTime)) {
  53162. input.setAttribute('data-hc-time-previous', previousTime);
  53163. }
  53164. input.setAttribute('data-hc-time', inputTime);
  53165. updatedTime = inputTime;
  53166. }
  53167. input.value = time.dateFormat((this.inputTypeFormats[input.type] ||
  53168. options.inputEditDateFormat), updatedTime);
  53169. if (dateBox) {
  53170. dateBox.attr({
  53171. text: time.dateFormat(options.inputDateFormat, updatedTime)
  53172. });
  53173. }
  53174. }
  53175. }
  53176. /**
  53177. * Set the min and max value of a HTML input for the dates
  53178. *
  53179. * @private
  53180. * @function Highcharts.RangeSelector#setInputExtremes
  53181. */
  53182. setInputExtremes(name, min, max) {
  53183. const input = name === 'min' ? this.minInput : this.maxInput;
  53184. if (input) {
  53185. const format = this.inputTypeFormats[input.type];
  53186. const time = this.chart.time;
  53187. if (format) {
  53188. const newMin = time.dateFormat(format, min);
  53189. if (input.min !== newMin) {
  53190. input.min = newMin;
  53191. }
  53192. const newMax = time.dateFormat(format, max);
  53193. if (input.max !== newMax) {
  53194. input.max = newMax;
  53195. }
  53196. }
  53197. }
  53198. }
  53199. /**
  53200. * @private
  53201. * @function Highcharts.RangeSelector#showInput
  53202. * @param {string} name
  53203. */
  53204. showInput(name) {
  53205. const dateBox = name === 'min' ? this.minDateBox : this.maxDateBox;
  53206. const input = name === 'min' ? this.minInput : this.maxInput;
  53207. if (input && dateBox && this.inputGroup) {
  53208. const isTextInput = input.type === 'text';
  53209. const { translateX, translateY } = this.inputGroup;
  53210. const { inputBoxWidth } = this.options;
  53211. css(input, {
  53212. width: isTextInput ?
  53213. ((dateBox.width + (inputBoxWidth ? -2 : 20)) + 'px') :
  53214. 'auto',
  53215. height: (dateBox.height - 2) + 'px',
  53216. border: '2px solid silver'
  53217. });
  53218. if (isTextInput && inputBoxWidth) {
  53219. css(input, {
  53220. left: (translateX + dateBox.x) + 'px',
  53221. top: translateY + 'px'
  53222. });
  53223. // Inputs of types date, time or datetime-local should be centered
  53224. // on top of the dateBox
  53225. }
  53226. else {
  53227. css(input, {
  53228. left: Math.min(Math.round(dateBox.x +
  53229. translateX -
  53230. (input.offsetWidth - dateBox.width) / 2), this.chart.chartWidth - input.offsetWidth) + 'px',
  53231. top: (translateY - (input.offsetHeight - dateBox.height) / 2) + 'px'
  53232. });
  53233. }
  53234. }
  53235. }
  53236. /**
  53237. * @private
  53238. * @function Highcharts.RangeSelector#hideInput
  53239. * @param {string} name
  53240. */
  53241. hideInput(name) {
  53242. const input = name === 'min' ? this.minInput : this.maxInput;
  53243. if (input) {
  53244. css(input, {
  53245. top: '-9999em',
  53246. border: 0,
  53247. width: '1px',
  53248. height: '1px'
  53249. });
  53250. }
  53251. }
  53252. /**
  53253. * @private
  53254. * @function Highcharts.RangeSelector#defaultInputDateParser
  53255. */
  53256. defaultInputDateParser(inputDate, useUTC, time) {
  53257. const hasTimezone = (str) => str.length > 6 &&
  53258. (str.lastIndexOf('-') === str.length - 6 ||
  53259. str.lastIndexOf('+') === str.length - 6);
  53260. let input = inputDate.split('/').join('-').split(' ').join('T');
  53261. if (input.indexOf('T') === -1) {
  53262. input += 'T00:00';
  53263. }
  53264. if (useUTC) {
  53265. input += 'Z';
  53266. }
  53267. else if (H.isSafari && !hasTimezone(input)) {
  53268. const offset = new Date(input).getTimezoneOffset() / 60;
  53269. input += offset <= 0 ? `+${pad(-offset)}:00` : `-${pad(offset)}:00`;
  53270. }
  53271. let date = Date.parse(input);
  53272. // If the value isn't parsed directly to a value by the
  53273. // browser's Date.parse method, try
  53274. // parsing it a different way
  53275. if (!isNumber(date)) {
  53276. const parts = inputDate.split('-');
  53277. date = Date.UTC(pInt(parts[0]), pInt(parts[1]) - 1, pInt(parts[2]));
  53278. }
  53279. if (time && useUTC && isNumber(date)) {
  53280. date += time.getTimezoneOffset(date);
  53281. }
  53282. return date;
  53283. }
  53284. /**
  53285. * Draw either the 'from' or the 'to' HTML input box of the range selector
  53286. *
  53287. * @private
  53288. * @function Highcharts.RangeSelector#drawInput
  53289. */
  53290. drawInput(name) {
  53291. const { chart, div, inputGroup } = this;
  53292. const rangeSelector = this, chartStyle = chart.renderer.style || {}, renderer = chart.renderer, options = chart.options.rangeSelector, lang = defaultOptions.lang, isMin = name === 'min';
  53293. /**
  53294. * @private
  53295. */
  53296. function updateExtremes() {
  53297. const { maxInput, minInput } = rangeSelector, chartAxis = chart.xAxis[0], dataAxis = chart.scroller && chart.scroller.xAxis ?
  53298. chart.scroller.xAxis :
  53299. chartAxis, dataMin = dataAxis.dataMin, dataMax = dataAxis.dataMax;
  53300. let value = rangeSelector.getInputValue(name);
  53301. if (value !== Number(input.getAttribute('data-hc-time-previous')) &&
  53302. isNumber(value)) {
  53303. input.setAttribute('data-hc-time-previous', value);
  53304. // Validate the extremes. If it goes beyound the data min or
  53305. // max, use the actual data extreme (#2438).
  53306. if (isMin && maxInput && isNumber(dataMin)) {
  53307. if (value > Number(maxInput.getAttribute('data-hc-time'))) {
  53308. value = void 0;
  53309. }
  53310. else if (value < dataMin) {
  53311. value = dataMin;
  53312. }
  53313. }
  53314. else if (minInput && isNumber(dataMax)) {
  53315. if (value < Number(minInput.getAttribute('data-hc-time'))) {
  53316. value = void 0;
  53317. }
  53318. else if (value > dataMax) {
  53319. value = dataMax;
  53320. }
  53321. }
  53322. // Set the extremes
  53323. if (typeof value !== 'undefined') { // @todo typof undefined
  53324. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  53325. }
  53326. }
  53327. }
  53328. // Create the text label
  53329. const text = lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'] || '';
  53330. const label = renderer
  53331. .label(text, 0)
  53332. .addClass('highcharts-range-label')
  53333. .attr({
  53334. padding: text ? 2 : 0,
  53335. height: text ? options.inputBoxHeight : 0
  53336. })
  53337. .add(inputGroup);
  53338. // Create an SVG label that shows updated date ranges and and records
  53339. // click events that bring in the HTML input.
  53340. const dateBox = renderer
  53341. .label('', 0)
  53342. .addClass('highcharts-range-input')
  53343. .attr({
  53344. padding: 2,
  53345. width: options.inputBoxWidth,
  53346. height: options.inputBoxHeight,
  53347. 'text-align': 'center'
  53348. })
  53349. .on('click', function () {
  53350. // If it is already focused, the onfocus event doesn't fire
  53351. // (#3713)
  53352. rangeSelector.showInput(name);
  53353. rangeSelector[name + 'Input'].focus();
  53354. });
  53355. if (!chart.styledMode) {
  53356. dateBox.attr({
  53357. stroke: options.inputBoxBorderColor,
  53358. 'stroke-width': 1
  53359. });
  53360. }
  53361. dateBox.add(inputGroup);
  53362. // Create the HTML input element. This is rendered as 1x1 pixel then set
  53363. // to the right size when focused.
  53364. const input = createElement('input', {
  53365. name: name,
  53366. className: 'highcharts-range-selector'
  53367. }, void 0, div);
  53368. // #14788: Setting input.type to an unsupported type throws in IE, so
  53369. // we need to use setAttribute instead
  53370. input.setAttribute('type', preferredInputType(options.inputDateFormat || '%e %b %Y'));
  53371. if (!chart.styledMode) {
  53372. // Styles
  53373. label.css(merge(chartStyle, options.labelStyle));
  53374. dateBox.css(merge({
  53375. color: "#333333" /* Palette.neutralColor80 */
  53376. }, chartStyle, options.inputStyle));
  53377. css(input, extend({
  53378. position: 'absolute',
  53379. border: 0,
  53380. boxShadow: '0 0 15px rgba(0,0,0,0.3)',
  53381. width: '1px',
  53382. height: '1px',
  53383. padding: 0,
  53384. textAlign: 'center',
  53385. fontSize: chartStyle.fontSize,
  53386. fontFamily: chartStyle.fontFamily,
  53387. top: '-9999em' // #4798
  53388. }, options.inputStyle));
  53389. }
  53390. // Blow up the input box
  53391. input.onfocus = () => {
  53392. rangeSelector.showInput(name);
  53393. };
  53394. // Hide away the input box
  53395. input.onblur = () => {
  53396. // update extermes only when inputs are active
  53397. if (input === H.doc.activeElement) { // Only when focused
  53398. // Update also when no `change` event is triggered, like when
  53399. // clicking inside the SVG (#4710)
  53400. updateExtremes();
  53401. }
  53402. // #10404 - move hide and blur outside focus
  53403. rangeSelector.hideInput(name);
  53404. rangeSelector.setInputValue(name);
  53405. input.blur(); // #4606
  53406. };
  53407. let keyDown = false;
  53408. // handle changes in the input boxes
  53409. input.onchange = () => {
  53410. // Update extremes and blur input when clicking date input calendar
  53411. if (!keyDown) {
  53412. updateExtremes();
  53413. rangeSelector.hideInput(name);
  53414. input.blur();
  53415. }
  53416. };
  53417. input.onkeypress = (event) => {
  53418. // IE does not fire onchange on enter
  53419. if (event.keyCode === 13) {
  53420. updateExtremes();
  53421. }
  53422. };
  53423. input.onkeydown = (event) => {
  53424. keyDown = true;
  53425. // Arrow keys
  53426. if (event.keyCode === 38 || event.keyCode === 40) {
  53427. updateExtremes();
  53428. }
  53429. };
  53430. input.onkeyup = () => {
  53431. keyDown = false;
  53432. };
  53433. return { dateBox, input, label };
  53434. }
  53435. /**
  53436. * Get the position of the range selector buttons and inputs. This can be
  53437. * overridden from outside for custom positioning.
  53438. *
  53439. * @private
  53440. * @function Highcharts.RangeSelector#getPosition
  53441. */
  53442. getPosition() {
  53443. const chart = this.chart, options = chart.options.rangeSelector, top = options.verticalAlign === 'top' ?
  53444. chart.plotTop - chart.axisOffset[0] :
  53445. 0; // set offset only for varticalAlign top
  53446. return {
  53447. buttonTop: top + options.buttonPosition.y,
  53448. inputTop: top + options.inputPosition.y - 10
  53449. };
  53450. }
  53451. /**
  53452. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  53453. * the current timestamp. Will choose dataMin if its value is higher than
  53454. * the timestamp for the start of current year.
  53455. *
  53456. * @private
  53457. * @function Highcharts.RangeSelector#getYTDExtremes
  53458. * @return {*}
  53459. * Returns min and max for the YTD
  53460. */
  53461. getYTDExtremes(dataMax, dataMin, useUTC) {
  53462. const time = this.chart.time, now = new time.Date(dataMax), year = time.get('FullYear', now), startOfYear = useUTC ?
  53463. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  53464. +new time.Date(year, 0, 1), min = Math.max(dataMin, startOfYear), ts = now.getTime();
  53465. return {
  53466. max: Math.min(dataMax || ts, ts),
  53467. min
  53468. };
  53469. }
  53470. /**
  53471. * Render the range selector including the buttons and the inputs. The first
  53472. * time render is called, the elements are created and positioned. On
  53473. * subsequent calls, they are moved and updated.
  53474. *
  53475. * @private
  53476. * @function Highcharts.RangeSelector#render
  53477. * @param {number} [min]
  53478. * X axis minimum
  53479. * @param {number} [max]
  53480. * X axis maximum
  53481. */
  53482. render(min, max) {
  53483. const chart = this.chart, renderer = chart.renderer, container = chart.container, chartOptions = chart.options, options = chartOptions.rangeSelector,
  53484. // Place inputs above the container
  53485. inputsZIndex = pick(chartOptions.chart.style &&
  53486. chartOptions.chart.style.zIndex, 0) + 1, inputEnabled = options.inputEnabled, rendered = this.rendered;
  53487. if (options.enabled === false) {
  53488. return;
  53489. }
  53490. // create the elements
  53491. if (!rendered) {
  53492. this.group = renderer.g('range-selector-group')
  53493. .attr({
  53494. zIndex: 7
  53495. })
  53496. .add();
  53497. this.div = createElement('div', void 0, {
  53498. position: 'relative',
  53499. height: 0,
  53500. zIndex: inputsZIndex
  53501. });
  53502. if (this.buttonOptions.length) {
  53503. this.renderButtons();
  53504. }
  53505. // First create a wrapper outside the container in order to make
  53506. // the inputs work and make export correct
  53507. if (container.parentNode) {
  53508. container.parentNode.insertBefore(this.div, container);
  53509. }
  53510. if (inputEnabled) {
  53511. // Create the group to keep the inputs
  53512. this.inputGroup = renderer.g('input-group').add(this.group);
  53513. const minElems = this.drawInput('min');
  53514. this.minDateBox = minElems.dateBox;
  53515. this.minLabel = minElems.label;
  53516. this.minInput = minElems.input;
  53517. const maxElems = this.drawInput('max');
  53518. this.maxDateBox = maxElems.dateBox;
  53519. this.maxLabel = maxElems.label;
  53520. this.maxInput = maxElems.input;
  53521. }
  53522. }
  53523. if (inputEnabled) {
  53524. // Set or reset the input values
  53525. this.setInputValue('min', min);
  53526. this.setInputValue('max', max);
  53527. const unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || chart.xAxis[0] || {};
  53528. if (defined(unionExtremes.dataMin) &&
  53529. defined(unionExtremes.dataMax)) {
  53530. const minRange = chart.xAxis[0].minRange || 0;
  53531. this.setInputExtremes('min', unionExtremes.dataMin, Math.min(unionExtremes.dataMax, this.getInputValue('max')) - minRange);
  53532. this.setInputExtremes('max', Math.max(unionExtremes.dataMin, this.getInputValue('min')) + minRange, unionExtremes.dataMax);
  53533. }
  53534. // Reflow
  53535. if (this.inputGroup) {
  53536. let x = 0;
  53537. [
  53538. this.minLabel,
  53539. this.minDateBox,
  53540. this.maxLabel,
  53541. this.maxDateBox
  53542. ].forEach((label) => {
  53543. if (label) {
  53544. const { width } = label.getBBox();
  53545. if (width) {
  53546. label.attr({ x });
  53547. x += width + options.inputSpacing;
  53548. }
  53549. }
  53550. });
  53551. }
  53552. }
  53553. this.alignElements();
  53554. this.rendered = true;
  53555. }
  53556. /**
  53557. * Render the range buttons. This only runs the first time, later the
  53558. * positioning is laid out in alignElements.
  53559. *
  53560. * @private
  53561. * @function Highcharts.RangeSelector#renderButtons
  53562. */
  53563. renderButtons() {
  53564. const { buttons, chart, options } = this;
  53565. const lang = defaultOptions.lang;
  53566. const renderer = chart.renderer;
  53567. const buttonTheme = merge(options.buttonTheme);
  53568. const states = buttonTheme && buttonTheme.states;
  53569. // Prevent the button from resetting the width when the button state
  53570. // changes since we need more control over the width when collapsing
  53571. // the buttons
  53572. const width = buttonTheme.width || 28;
  53573. delete buttonTheme.width;
  53574. delete buttonTheme.states;
  53575. this.buttonGroup = renderer.g('range-selector-buttons').add(this.group);
  53576. const dropdown = this.dropdown = createElement('select', void 0, {
  53577. position: 'absolute',
  53578. width: '1px',
  53579. height: '1px',
  53580. padding: 0,
  53581. border: 0,
  53582. top: '-9999em',
  53583. cursor: 'pointer',
  53584. opacity: 0.0001
  53585. }, this.div);
  53586. // Prevent page zoom on iPhone
  53587. addEvent(dropdown, 'touchstart', () => {
  53588. dropdown.style.fontSize = '16px';
  53589. });
  53590. // Forward events from select to button
  53591. [
  53592. [H.isMS ? 'mouseover' : 'mouseenter'],
  53593. [H.isMS ? 'mouseout' : 'mouseleave'],
  53594. ['change', 'click']
  53595. ].forEach(([from, to]) => {
  53596. addEvent(dropdown, from, () => {
  53597. const button = buttons[this.currentButtonIndex()];
  53598. if (button) {
  53599. fireEvent(button.element, to || from);
  53600. }
  53601. });
  53602. });
  53603. this.zoomText = renderer
  53604. .label((lang && lang.rangeSelectorZoom) || '', 0)
  53605. .attr({
  53606. padding: options.buttonTheme.padding,
  53607. height: options.buttonTheme.height,
  53608. paddingLeft: 0,
  53609. paddingRight: 0
  53610. })
  53611. .add(this.buttonGroup);
  53612. if (!this.chart.styledMode) {
  53613. this.zoomText.css(options.labelStyle);
  53614. buttonTheme['stroke-width'] = pick(buttonTheme['stroke-width'], 0);
  53615. }
  53616. createElement('option', {
  53617. textContent: this.zoomText.textStr,
  53618. disabled: true
  53619. }, void 0, dropdown);
  53620. this.buttonOptions.forEach((rangeOptions, i) => {
  53621. createElement('option', {
  53622. textContent: rangeOptions.title || rangeOptions.text
  53623. }, void 0, dropdown);
  53624. buttons[i] = renderer
  53625. .button(rangeOptions.text, 0, 0, (e) => {
  53626. // extract events from button object and call
  53627. const buttonEvents = (rangeOptions.events && rangeOptions.events.click);
  53628. let callDefaultEvent;
  53629. if (buttonEvents) {
  53630. callDefaultEvent =
  53631. buttonEvents.call(rangeOptions, e);
  53632. }
  53633. if (callDefaultEvent !== false) {
  53634. this.clickButton(i);
  53635. }
  53636. this.isActive = true;
  53637. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  53638. .attr({
  53639. 'text-align': 'center',
  53640. width
  53641. })
  53642. .add(this.buttonGroup);
  53643. if (rangeOptions.title) {
  53644. buttons[i].attr('title', rangeOptions.title);
  53645. }
  53646. });
  53647. }
  53648. /**
  53649. * Align the elements horizontally and vertically.
  53650. *
  53651. * @private
  53652. * @function Highcharts.RangeSelector#alignElements
  53653. */
  53654. alignElements() {
  53655. const { buttonGroup, buttons, chart, group, inputGroup, options, zoomText } = this;
  53656. const chartOptions = chart.options;
  53657. const navButtonOptions = (chartOptions.exporting &&
  53658. chartOptions.exporting.enabled !== false &&
  53659. chartOptions.navigation &&
  53660. chartOptions.navigation.buttonOptions);
  53661. const { buttonPosition, inputPosition, verticalAlign } = options;
  53662. // Get the X offset required to avoid overlapping with the exporting
  53663. // button. This is is used both by the buttonGroup and the inputGroup.
  53664. const getXOffsetForExportButton = (group, position) => {
  53665. if (navButtonOptions &&
  53666. this.titleCollision(chart) &&
  53667. verticalAlign === 'top' &&
  53668. position.align === 'right' && ((position.y -
  53669. group.getBBox().height - 12) <
  53670. ((navButtonOptions.y || 0) +
  53671. (navButtonOptions.height || 0) +
  53672. chart.spacing[0]))) {
  53673. return -40;
  53674. }
  53675. return 0;
  53676. };
  53677. let plotLeft = chart.plotLeft;
  53678. if (group && buttonPosition && inputPosition) {
  53679. let translateX = buttonPosition.x - chart.spacing[3];
  53680. if (buttonGroup) {
  53681. this.positionButtons();
  53682. if (!this.initialButtonGroupWidth) {
  53683. let width = 0;
  53684. if (zoomText) {
  53685. width += zoomText.getBBox().width + 5;
  53686. }
  53687. buttons.forEach((button, i) => {
  53688. width += button.width;
  53689. if (i !== buttons.length - 1) {
  53690. width += options.buttonSpacing;
  53691. }
  53692. });
  53693. this.initialButtonGroupWidth = width;
  53694. }
  53695. plotLeft -= chart.spacing[3];
  53696. this.updateButtonStates();
  53697. // Detect collision between button group and exporting
  53698. const xOffsetForExportButton = getXOffsetForExportButton(buttonGroup, buttonPosition);
  53699. this.alignButtonGroup(xOffsetForExportButton);
  53700. // Skip animation
  53701. group.placed = buttonGroup.placed = chart.hasLoaded;
  53702. }
  53703. let xOffsetForExportButton = 0;
  53704. if (inputGroup) {
  53705. // Detect collision between the input group and exporting button
  53706. xOffsetForExportButton = getXOffsetForExportButton(inputGroup, inputPosition);
  53707. if (inputPosition.align === 'left') {
  53708. translateX = plotLeft;
  53709. }
  53710. else if (inputPosition.align === 'right') {
  53711. translateX = -Math.max(chart.axisOffset[1], -xOffsetForExportButton);
  53712. }
  53713. // Update the alignment to the updated spacing box
  53714. inputGroup.align({
  53715. y: inputPosition.y,
  53716. width: inputGroup.getBBox().width,
  53717. align: inputPosition.align,
  53718. // fix wrong getBBox() value on right align
  53719. x: inputPosition.x + translateX - 2
  53720. }, true, chart.spacingBox);
  53721. // Skip animation
  53722. inputGroup.placed = chart.hasLoaded;
  53723. }
  53724. this.handleCollision(xOffsetForExportButton);
  53725. // Vertical align
  53726. group.align({
  53727. verticalAlign
  53728. }, true, chart.spacingBox);
  53729. const alignTranslateY = group.alignAttr.translateY;
  53730. // Set position
  53731. let groupHeight = group.getBBox().height + 20; // # 20 padding
  53732. let translateY = 0;
  53733. // Calculate bottom position
  53734. if (verticalAlign === 'bottom') {
  53735. const legendOptions = chart.legend && chart.legend.options;
  53736. const legendHeight = (legendOptions &&
  53737. legendOptions.verticalAlign === 'bottom' &&
  53738. legendOptions.enabled &&
  53739. !legendOptions.floating ?
  53740. (chart.legend.legendHeight +
  53741. pick(legendOptions.margin, 10)) :
  53742. 0);
  53743. groupHeight = groupHeight + legendHeight - 20;
  53744. translateY = (alignTranslateY -
  53745. groupHeight -
  53746. (options.floating ? 0 : options.y) -
  53747. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  53748. 10 // 10 spacing
  53749. );
  53750. }
  53751. if (verticalAlign === 'top') {
  53752. if (options.floating) {
  53753. translateY = 0;
  53754. }
  53755. if (chart.titleOffset && chart.titleOffset[0]) {
  53756. translateY = chart.titleOffset[0];
  53757. }
  53758. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  53759. }
  53760. else if (verticalAlign === 'middle') {
  53761. if (inputPosition.y === buttonPosition.y) {
  53762. translateY = alignTranslateY;
  53763. }
  53764. else if (inputPosition.y || buttonPosition.y) {
  53765. if (inputPosition.y < 0 ||
  53766. buttonPosition.y < 0) {
  53767. translateY -= Math.min(inputPosition.y, buttonPosition.y);
  53768. }
  53769. else {
  53770. translateY = alignTranslateY - groupHeight;
  53771. }
  53772. }
  53773. }
  53774. group.translate(options.x, options.y + Math.floor(translateY));
  53775. // Translate HTML inputs
  53776. const { minInput, maxInput, dropdown } = this;
  53777. if (options.inputEnabled && minInput && maxInput) {
  53778. minInput.style.marginTop = group.translateY + 'px';
  53779. maxInput.style.marginTop = group.translateY + 'px';
  53780. }
  53781. if (dropdown) {
  53782. dropdown.style.marginTop = group.translateY + 'px';
  53783. }
  53784. }
  53785. }
  53786. /**
  53787. * Align the button group horizontally and vertically.
  53788. *
  53789. * @private
  53790. * @function Highcharts.RangeSelector#alignButtonGroup
  53791. * @param {number} xOffsetForExportButton
  53792. * @param {number} [width]
  53793. */
  53794. alignButtonGroup(xOffsetForExportButton, width) {
  53795. const { chart, options, buttonGroup, buttons } = this;
  53796. const { buttonPosition } = options;
  53797. const plotLeft = chart.plotLeft - chart.spacing[3];
  53798. let translateX = buttonPosition.x - chart.spacing[3];
  53799. if (buttonPosition.align === 'right') {
  53800. translateX += xOffsetForExportButton - plotLeft; // #13014
  53801. }
  53802. else if (buttonPosition.align === 'center') {
  53803. translateX -= plotLeft / 2;
  53804. }
  53805. if (buttonGroup) {
  53806. // Align button group
  53807. buttonGroup.align({
  53808. y: buttonPosition.y,
  53809. width: pick(width, this.initialButtonGroupWidth),
  53810. align: buttonPosition.align,
  53811. x: translateX
  53812. }, true, chart.spacingBox);
  53813. }
  53814. }
  53815. /**
  53816. * @private
  53817. * @function Highcharts.RangeSelector#positionButtons
  53818. */
  53819. positionButtons() {
  53820. const { buttons, chart, options, zoomText } = this;
  53821. const verb = chart.hasLoaded ? 'animate' : 'attr';
  53822. const { buttonPosition } = options;
  53823. const plotLeft = chart.plotLeft;
  53824. let buttonLeft = plotLeft;
  53825. if (zoomText && zoomText.visibility !== 'hidden') {
  53826. // #8769, allow dynamically updating margins
  53827. zoomText[verb]({
  53828. x: pick(plotLeft + buttonPosition.x, plotLeft)
  53829. });
  53830. // Button start position
  53831. buttonLeft += buttonPosition.x +
  53832. zoomText.getBBox().width + 5;
  53833. }
  53834. for (let i = 0, iEnd = this.buttonOptions.length; i < iEnd; ++i) {
  53835. if (buttons[i].visibility !== 'hidden') {
  53836. buttons[i][verb]({ x: buttonLeft });
  53837. // increase button position for the next button
  53838. buttonLeft += buttons[i].width + options.buttonSpacing;
  53839. }
  53840. else {
  53841. buttons[i][verb]({ x: plotLeft });
  53842. }
  53843. }
  53844. }
  53845. /**
  53846. * Handle collision between the button group and the input group
  53847. *
  53848. * @private
  53849. * @function Highcharts.RangeSelector#handleCollision
  53850. *
  53851. * @param {number} xOffsetForExportButton
  53852. * The X offset of the group required to make room for the
  53853. * exporting button
  53854. */
  53855. handleCollision(xOffsetForExportButton) {
  53856. const { chart, buttonGroup, inputGroup } = this;
  53857. const { buttonPosition, dropdown, inputPosition } = this.options;
  53858. const maxButtonWidth = () => {
  53859. let buttonWidth = 0;
  53860. this.buttons.forEach((button) => {
  53861. const bBox = button.getBBox();
  53862. if (bBox.width > buttonWidth) {
  53863. buttonWidth = bBox.width;
  53864. }
  53865. });
  53866. return buttonWidth;
  53867. };
  53868. const groupsOverlap = (buttonGroupWidth) => {
  53869. if (inputGroup && buttonGroup) {
  53870. const inputGroupX = (inputGroup.alignAttr.translateX +
  53871. inputGroup.alignOptions.x -
  53872. xOffsetForExportButton +
  53873. // getBBox for detecing left margin
  53874. inputGroup.getBBox().x +
  53875. // 2px padding to not overlap input and label
  53876. 2);
  53877. const inputGroupWidth = inputGroup.alignOptions.width;
  53878. const buttonGroupX = buttonGroup.alignAttr.translateX +
  53879. buttonGroup.getBBox().x;
  53880. return (buttonGroupX + buttonGroupWidth > inputGroupX) &&
  53881. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  53882. (buttonPosition.y <
  53883. (inputPosition.y +
  53884. inputGroup.getBBox().height));
  53885. }
  53886. return false;
  53887. };
  53888. const moveInputsDown = () => {
  53889. if (inputGroup && buttonGroup) {
  53890. inputGroup.attr({
  53891. translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -xOffsetForExportButton ?
  53892. 0 :
  53893. -xOffsetForExportButton),
  53894. translateY: inputGroup.alignAttr.translateY +
  53895. buttonGroup.getBBox().height + 10
  53896. });
  53897. }
  53898. };
  53899. if (buttonGroup) {
  53900. if (dropdown === 'always') {
  53901. this.collapseButtons(xOffsetForExportButton);
  53902. if (groupsOverlap(maxButtonWidth())) {
  53903. // Move the inputs down if there is still a collision
  53904. // after collapsing the buttons
  53905. moveInputsDown();
  53906. }
  53907. return;
  53908. }
  53909. if (dropdown === 'never') {
  53910. this.expandButtons();
  53911. }
  53912. }
  53913. // Detect collision
  53914. if (inputGroup && buttonGroup) {
  53915. if ((inputPosition.align === buttonPosition.align) ||
  53916. // 20 is minimal spacing between elements
  53917. groupsOverlap(this.initialButtonGroupWidth + 20)) {
  53918. if (dropdown === 'responsive') {
  53919. this.collapseButtons(xOffsetForExportButton);
  53920. if (groupsOverlap(maxButtonWidth())) {
  53921. moveInputsDown();
  53922. }
  53923. }
  53924. else {
  53925. moveInputsDown();
  53926. }
  53927. }
  53928. else if (dropdown === 'responsive') {
  53929. this.expandButtons();
  53930. }
  53931. }
  53932. else if (buttonGroup && dropdown === 'responsive') {
  53933. if (this.initialButtonGroupWidth > chart.plotWidth) {
  53934. this.collapseButtons(xOffsetForExportButton);
  53935. }
  53936. else {
  53937. this.expandButtons();
  53938. }
  53939. }
  53940. }
  53941. /**
  53942. * Collapse the buttons and put the select element on top.
  53943. *
  53944. * @private
  53945. * @function Highcharts.RangeSelector#collapseButtons
  53946. * @param {number} xOffsetForExportButton
  53947. */
  53948. collapseButtons(xOffsetForExportButton) {
  53949. const { buttons, buttonOptions, chart, dropdown, options, zoomText } = this;
  53950. const userButtonTheme = (chart.userOptions.rangeSelector &&
  53951. chart.userOptions.rangeSelector.buttonTheme) || {};
  53952. const getAttribs = (text) => ({
  53953. text: text ? `${text} ▾` : '▾',
  53954. width: 'auto',
  53955. paddingLeft: pick(options.buttonTheme.paddingLeft, userButtonTheme.padding, 8),
  53956. paddingRight: pick(options.buttonTheme.paddingRight, userButtonTheme.padding, 8)
  53957. });
  53958. if (zoomText) {
  53959. zoomText.hide();
  53960. }
  53961. let hasActiveButton = false;
  53962. buttonOptions.forEach((rangeOptions, i) => {
  53963. const button = buttons[i];
  53964. if (button.state !== 2) {
  53965. button.hide();
  53966. }
  53967. else {
  53968. button.show();
  53969. button.attr(getAttribs(rangeOptions.text));
  53970. hasActiveButton = true;
  53971. }
  53972. });
  53973. if (!hasActiveButton) {
  53974. if (dropdown) {
  53975. dropdown.selectedIndex = 0;
  53976. }
  53977. buttons[0].show();
  53978. buttons[0].attr(getAttribs(this.zoomText && this.zoomText.textStr));
  53979. }
  53980. const { align } = options.buttonPosition;
  53981. this.positionButtons();
  53982. if (align === 'right' || align === 'center') {
  53983. this.alignButtonGroup(xOffsetForExportButton, buttons[this.currentButtonIndex()].getBBox().width);
  53984. }
  53985. this.showDropdown();
  53986. }
  53987. /**
  53988. * Show all the buttons and hide the select element.
  53989. *
  53990. * @private
  53991. * @function Highcharts.RangeSelector#expandButtons
  53992. */
  53993. expandButtons() {
  53994. const { buttons, buttonOptions, options, zoomText } = this;
  53995. this.hideDropdown();
  53996. if (zoomText) {
  53997. zoomText.show();
  53998. }
  53999. buttonOptions.forEach((rangeOptions, i) => {
  54000. const button = buttons[i];
  54001. button.show();
  54002. button.attr({
  54003. text: rangeOptions.text,
  54004. width: options.buttonTheme.width || 28,
  54005. paddingLeft: pick(options.buttonTheme.paddingLeft, 'unset'),
  54006. paddingRight: pick(options.buttonTheme.paddingRight, 'unset')
  54007. });
  54008. if (button.state < 2) {
  54009. button.setState(0);
  54010. }
  54011. });
  54012. this.positionButtons();
  54013. }
  54014. /**
  54015. * Get the index of the visible button when the buttons are collapsed.
  54016. *
  54017. * @private
  54018. * @function Highcharts.RangeSelector#currentButtonIndex
  54019. */
  54020. currentButtonIndex() {
  54021. const { dropdown } = this;
  54022. if (dropdown && dropdown.selectedIndex > 0) {
  54023. return dropdown.selectedIndex - 1;
  54024. }
  54025. return 0;
  54026. }
  54027. /**
  54028. * Position the select element on top of the button.
  54029. *
  54030. * @private
  54031. * @function Highcharts.RangeSelector#showDropdown
  54032. */
  54033. showDropdown() {
  54034. const { buttonGroup, buttons, chart, dropdown } = this;
  54035. if (buttonGroup && dropdown) {
  54036. const { translateX, translateY } = buttonGroup;
  54037. const bBox = buttons[this.currentButtonIndex()].getBBox();
  54038. css(dropdown, {
  54039. left: (chart.plotLeft + translateX) + 'px',
  54040. top: (translateY + 0.5) + 'px',
  54041. width: bBox.width + 'px',
  54042. height: bBox.height + 'px'
  54043. });
  54044. this.hasVisibleDropdown = true;
  54045. }
  54046. }
  54047. /**
  54048. * @private
  54049. * @function Highcharts.RangeSelector#hideDropdown
  54050. */
  54051. hideDropdown() {
  54052. const { dropdown } = this;
  54053. if (dropdown) {
  54054. css(dropdown, {
  54055. top: '-9999em',
  54056. width: '1px',
  54057. height: '1px'
  54058. });
  54059. this.hasVisibleDropdown = false;
  54060. }
  54061. }
  54062. /**
  54063. * Extracts height of range selector
  54064. *
  54065. * @private
  54066. * @function Highcharts.RangeSelector#getHeight
  54067. * @return {number}
  54068. * Returns rangeSelector height
  54069. */
  54070. getHeight() {
  54071. const rangeSelector = this, options = rangeSelector.options, rangeSelectorGroup = rangeSelector.group, inputPosition = options.inputPosition, buttonPosition = options.buttonPosition, yPosition = options.y, buttonPositionY = buttonPosition.y, inputPositionY = inputPosition.y;
  54072. let rangeSelectorHeight = 0;
  54073. if (options.height) {
  54074. return options.height;
  54075. }
  54076. // Align the elements before we read the height in case we're switching
  54077. // between wrapped and non-wrapped layout
  54078. this.alignElements();
  54079. rangeSelectorHeight = rangeSelectorGroup ?
  54080. // 13px to keep back compatibility
  54081. (rangeSelectorGroup.getBBox(true).height) + 13 +
  54082. yPosition :
  54083. 0;
  54084. const minPosition = Math.min(inputPositionY, buttonPositionY);
  54085. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  54086. (inputPositionY > 0 && buttonPositionY > 0)) {
  54087. rangeSelectorHeight += Math.abs(minPosition);
  54088. }
  54089. return rangeSelectorHeight;
  54090. }
  54091. /**
  54092. * Detect collision with title or subtitle
  54093. *
  54094. * @private
  54095. * @function Highcharts.RangeSelector#titleCollision
  54096. * @return {boolean}
  54097. * Returns collision status
  54098. */
  54099. titleCollision(chart) {
  54100. return !(chart.options.title.text ||
  54101. chart.options.subtitle.text);
  54102. }
  54103. /**
  54104. * Update the range selector with new options
  54105. *
  54106. * @private
  54107. * @function Highcharts.RangeSelector#update
  54108. * @param {Highcharts.RangeSelectorOptions} options
  54109. */
  54110. update(options) {
  54111. const chart = this.chart;
  54112. merge(true, chart.options.rangeSelector, options);
  54113. this.destroy();
  54114. this.init(chart);
  54115. this.render();
  54116. }
  54117. /**
  54118. * Destroys allocated elements.
  54119. *
  54120. * @private
  54121. * @function Highcharts.RangeSelector#destroy
  54122. */
  54123. destroy() {
  54124. const rSelector = this, minInput = rSelector.minInput, maxInput = rSelector.maxInput;
  54125. if (rSelector.eventsToUnbind) {
  54126. rSelector.eventsToUnbind.forEach((unbind) => unbind());
  54127. rSelector.eventsToUnbind = void 0;
  54128. }
  54129. // Destroy elements in collections
  54130. destroyObjectProperties(rSelector.buttons);
  54131. // Clear input element events
  54132. if (minInput) {
  54133. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  54134. }
  54135. if (maxInput) {
  54136. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  54137. }
  54138. // Destroy HTML and SVG elements
  54139. objectEach(rSelector, function (val, key) {
  54140. if (val && key !== 'chart') {
  54141. if (val instanceof SVGElement) {
  54142. // SVGElement
  54143. val.destroy();
  54144. }
  54145. else if (val instanceof window.HTMLElement) {
  54146. // HTML element
  54147. discardElement(val);
  54148. }
  54149. }
  54150. if (val !== RangeSelector.prototype[key]) {
  54151. rSelector[key] = null;
  54152. }
  54153. }, this);
  54154. }
  54155. }
  54156. extend(RangeSelector.prototype, {
  54157. /**
  54158. * The default buttons for pre-selecting time frames.
  54159. * @private
  54160. */
  54161. defaultButtons: [{
  54162. type: 'month',
  54163. count: 1,
  54164. text: '1m',
  54165. title: 'View 1 month'
  54166. }, {
  54167. type: 'month',
  54168. count: 3,
  54169. text: '3m',
  54170. title: 'View 3 months'
  54171. }, {
  54172. type: 'month',
  54173. count: 6,
  54174. text: '6m',
  54175. title: 'View 6 months'
  54176. }, {
  54177. type: 'ytd',
  54178. text: 'YTD',
  54179. title: 'View year to date'
  54180. }, {
  54181. type: 'year',
  54182. count: 1,
  54183. text: '1y',
  54184. title: 'View 1 year'
  54185. }, {
  54186. type: 'all',
  54187. text: 'All',
  54188. title: 'View all'
  54189. }],
  54190. /**
  54191. * The date formats to use when setting min, max and value on date inputs.
  54192. * @private
  54193. */
  54194. inputTypeFormats: {
  54195. 'datetime-local': '%Y-%m-%dT%H:%M:%S',
  54196. 'date': '%Y-%m-%d',
  54197. 'time': '%H:%M:%S'
  54198. }
  54199. });
  54200. /* *
  54201. *
  54202. * Default Export
  54203. *
  54204. * */
  54205. /* *
  54206. *
  54207. * API Options
  54208. *
  54209. * */
  54210. /**
  54211. * Define the time span for the button
  54212. *
  54213. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  54214. */
  54215. /**
  54216. * Callback function to react on button clicks.
  54217. *
  54218. * @callback Highcharts.RangeSelectorClickCallbackFunction
  54219. *
  54220. * @param {global.Event} e
  54221. * Event arguments.
  54222. *
  54223. * @param {boolean|undefined}
  54224. * Return false to cancel the default button event.
  54225. */
  54226. /**
  54227. * Callback function to parse values entered in the input boxes and return a
  54228. * valid JavaScript time as milliseconds since 1970.
  54229. *
  54230. * @callback Highcharts.RangeSelectorParseCallbackFunction
  54231. *
  54232. * @param {string} value
  54233. * Input value to parse.
  54234. *
  54235. * @return {number}
  54236. * Parsed JavaScript time value.
  54237. */
  54238. (''); // keeps doclets above in JS file
  54239. return RangeSelector;
  54240. });
  54241. _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) {
  54242. /* *
  54243. *
  54244. * (c) 2010-2021 Torstein Honsi
  54245. *
  54246. * License: www.highcharts.com/license
  54247. *
  54248. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  54249. *
  54250. * */
  54251. const { addEvent, correctFloat, css, defined, error, pick, timeUnits } = U;
  54252. /* *
  54253. *
  54254. * Constants
  54255. *
  54256. * */
  54257. const composedMembers = [];
  54258. /* eslint-disable valid-jsdoc */
  54259. /* *
  54260. *
  54261. * Composition
  54262. *
  54263. * */
  54264. /**
  54265. * Extends the axis with ordinal support.
  54266. * @private
  54267. */
  54268. var OrdinalAxis;
  54269. (function (OrdinalAxis) {
  54270. /* *
  54271. *
  54272. * Declarations
  54273. *
  54274. * */
  54275. /* *
  54276. *
  54277. * Functions
  54278. *
  54279. * */
  54280. /**
  54281. * Extends the axis with ordinal support.
  54282. *
  54283. * @private
  54284. *
  54285. * @param AxisClass
  54286. * Axis class to extend.
  54287. *
  54288. * @param ChartClass
  54289. * Chart class to use.
  54290. *
  54291. * @param SeriesClass
  54292. * Series class to use.
  54293. */
  54294. function compose(AxisClass, SeriesClass, ChartClass) {
  54295. if (U.pushUnique(composedMembers, AxisClass)) {
  54296. const axisProto = AxisClass.prototype;
  54297. axisProto.getTimeTicks = getTimeTicks;
  54298. axisProto.index2val = index2val;
  54299. axisProto.lin2val = lin2val;
  54300. axisProto.val2lin = val2lin;
  54301. // Record this to prevent overwriting by broken-axis module (#5979)
  54302. axisProto.ordinal2lin = axisProto.val2lin;
  54303. addEvent(AxisClass, 'afterInit', onAxisAfterInit);
  54304. addEvent(AxisClass, 'foundExtremes', onAxisFoundExtremes);
  54305. addEvent(AxisClass, 'afterSetScale', onAxisAfterSetScale);
  54306. addEvent(AxisClass, 'initialAxisTranslation', onAxisInitialAxisTranslation);
  54307. }
  54308. if (U.pushUnique(composedMembers, ChartClass)) {
  54309. addEvent(ChartClass, 'pan', onChartPan);
  54310. }
  54311. if (U.pushUnique(composedMembers, SeriesClass)) {
  54312. addEvent(SeriesClass, 'updatedData', onSeriesUpdatedData);
  54313. }
  54314. return AxisClass;
  54315. }
  54316. OrdinalAxis.compose = compose;
  54317. /**
  54318. * In an ordinal axis, there might be areas with dense consentrations of
  54319. * points, then large gaps between some. Creating equally distributed
  54320. * ticks over this entire range may lead to a huge number of ticks that
  54321. * will later be removed. So instead, break the positions up in
  54322. * segments, find the tick positions for each segment then concatenize
  54323. * them. This method is used from both data grouping logic and X axis
  54324. * tick position logic.
  54325. * @private
  54326. */
  54327. function getTimeTicks(normalizedInterval, min, max, startOfWeek, positions = [], closestDistance = 0, findHigherRanks) {
  54328. const higherRanks = {}, tickPixelIntervalOption = this.options.tickPixelInterval, time = this.chart.time,
  54329. // Record all the start positions of a segment, to use when
  54330. // deciding what's a gap in the data.
  54331. segmentStarts = [];
  54332. let end, segmentPositions, hasCrossedHigherRank, info, outsideMax, start = 0, groupPositions = [], lastGroupPosition = -Number.MAX_VALUE;
  54333. // The positions are not always defined, for example for ordinal
  54334. // positions when data has regular interval (#1557, #2090)
  54335. if ((!this.options.ordinal && !this.options.breaks) ||
  54336. !positions ||
  54337. positions.length < 3 ||
  54338. typeof min === 'undefined') {
  54339. return time.getTimeTicks.apply(time, arguments);
  54340. }
  54341. // Analyze the positions array to split it into segments on gaps
  54342. // larger than 5 times the closest distance. The closest distance is
  54343. // already found at this point, so we reuse that instead of
  54344. // computing it again.
  54345. const posLength = positions.length;
  54346. for (end = 0; end < posLength; end++) {
  54347. outsideMax = end && positions[end - 1] > max;
  54348. if (positions[end] < min) { // Set the last position before min
  54349. start = end;
  54350. }
  54351. if (end === posLength - 1 ||
  54352. positions[end + 1] - positions[end] > closestDistance * 5 ||
  54353. outsideMax) {
  54354. // For each segment, calculate the tick positions from the
  54355. // getTimeTicks utility function. The interval will be the
  54356. // same regardless of how long the segment is.
  54357. if (positions[end] > lastGroupPosition) { // #1475
  54358. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  54359. // Prevent duplicate groups, for example for multiple
  54360. // segments within one larger time frame (#1475)
  54361. while (segmentPositions.length &&
  54362. segmentPositions[0] <= lastGroupPosition) {
  54363. segmentPositions.shift();
  54364. }
  54365. if (segmentPositions.length) {
  54366. lastGroupPosition =
  54367. segmentPositions[segmentPositions.length - 1];
  54368. }
  54369. segmentStarts.push(groupPositions.length);
  54370. groupPositions = groupPositions.concat(segmentPositions);
  54371. }
  54372. // Set start of next segment
  54373. start = end + 1;
  54374. }
  54375. if (outsideMax) {
  54376. break;
  54377. }
  54378. }
  54379. // Get the grouping info from the last of the segments. The info is
  54380. // the same for all segments.
  54381. if (segmentPositions) {
  54382. info = segmentPositions.info;
  54383. // Optionally identify ticks with higher rank, for example
  54384. // when the ticks have crossed midnight.
  54385. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  54386. end = groupPositions.length - 1;
  54387. // Compare points two by two
  54388. for (start = 1; start < end; start++) {
  54389. if (time.dateFormat('%d', groupPositions[start]) !==
  54390. time.dateFormat('%d', groupPositions[start - 1])) {
  54391. higherRanks[groupPositions[start]] = 'day';
  54392. hasCrossedHigherRank = true;
  54393. }
  54394. }
  54395. // If the complete array has crossed midnight, we want
  54396. // to mark the first positions also as higher rank
  54397. if (hasCrossedHigherRank) {
  54398. higherRanks[groupPositions[0]] = 'day';
  54399. }
  54400. info.higherRanks = higherRanks;
  54401. }
  54402. // Save the info
  54403. info.segmentStarts = segmentStarts;
  54404. groupPositions.info = info;
  54405. }
  54406. else {
  54407. error(12, false, this.chart);
  54408. }
  54409. // Don't show ticks within a gap in the ordinal axis, where the
  54410. // space between two points is greater than a portion of the tick
  54411. // pixel interval
  54412. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  54413. const length = groupPositions.length, translatedArr = [], distances = [];
  54414. let itemToRemove, translated, lastTranslated, medianDistance, distance, i = length;
  54415. // Find median pixel distance in order to keep a reasonably even
  54416. // distance between ticks (#748)
  54417. while (i--) {
  54418. translated = this.translate(groupPositions[i]);
  54419. if (lastTranslated) {
  54420. distances[i] = lastTranslated - translated;
  54421. }
  54422. translatedArr[i] = lastTranslated = translated;
  54423. }
  54424. distances.sort();
  54425. medianDistance = distances[Math.floor(distances.length / 2)];
  54426. if (medianDistance < tickPixelIntervalOption * 0.6) {
  54427. medianDistance = null;
  54428. }
  54429. // Now loop over again and remove ticks where needed
  54430. i = groupPositions[length - 1] > max ? length - 1 : length; // #817
  54431. lastTranslated = void 0;
  54432. while (i--) {
  54433. translated = translatedArr[i];
  54434. distance = Math.abs(lastTranslated - translated);
  54435. // #4175 - when axis is reversed, the distance, is negative but
  54436. // tickPixelIntervalOption positive, so we need to compare the
  54437. // same values
  54438. // Remove ticks that are closer than 0.6 times the pixel
  54439. // interval from the one to the right, but not if it is close to
  54440. // the median distance (#748).
  54441. if (lastTranslated &&
  54442. distance < tickPixelIntervalOption * 0.8 &&
  54443. (medianDistance === null || distance < medianDistance * 0.8)) {
  54444. // Is this a higher ranked position with a normal
  54445. // position to the right?
  54446. if (higherRanks[groupPositions[i]] &&
  54447. !higherRanks[groupPositions[i + 1]]) {
  54448. // Yes: remove the lower ranked neighbour to the
  54449. // right
  54450. itemToRemove = i + 1;
  54451. lastTranslated = translated; // #709
  54452. }
  54453. else {
  54454. // No: remove this one
  54455. itemToRemove = i;
  54456. }
  54457. groupPositions.splice(itemToRemove, 1);
  54458. }
  54459. else {
  54460. lastTranslated = translated;
  54461. }
  54462. }
  54463. }
  54464. return groupPositions;
  54465. }
  54466. /**
  54467. * Get axis position of given index of the extended ordinal positions.
  54468. * Used only when panning an ordinal axis.
  54469. *
  54470. * @private
  54471. * @function Highcharts.Axis#index2val
  54472. * @param {number} index
  54473. * The index value of searched point
  54474. */
  54475. function index2val(index) {
  54476. const axis = this, ordinal = axis.ordinal,
  54477. // Context could be changed to extendedOrdinalPositions.
  54478. ordinalPositions = ordinal.positions;
  54479. // The visible range contains only equally spaced values.
  54480. if (!ordinalPositions) {
  54481. return index;
  54482. }
  54483. let i = ordinalPositions.length - 1, distance;
  54484. if (index < 0) { // out of range, in effect panning to the left
  54485. index = ordinalPositions[0];
  54486. }
  54487. else if (index > i) { // out of range, panning to the right
  54488. index = ordinalPositions[i];
  54489. }
  54490. else { // split it up
  54491. i = Math.floor(index);
  54492. distance = index - i; // the decimal
  54493. }
  54494. if (typeof distance !== 'undefined' &&
  54495. typeof ordinalPositions[i] !== 'undefined') {
  54496. return ordinalPositions[i] + (distance ?
  54497. distance *
  54498. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  54499. 0);
  54500. }
  54501. return index;
  54502. }
  54503. /**
  54504. * Translate from linear (internal) to axis value.
  54505. *
  54506. * @private
  54507. * @function Highcharts.Axis#lin2val
  54508. * @param {number} val
  54509. * The linear abstracted value.
  54510. */
  54511. function lin2val(val) {
  54512. const axis = this, ordinal = axis.ordinal, localMin = axis.old ? axis.old.min : axis.min, localA = axis.old ? axis.old.transA : axis.transA;
  54513. let positions = ordinal.positions; // for the current visible range
  54514. // The visible range contains only equally spaced values.
  54515. if (!positions) {
  54516. return val;
  54517. }
  54518. // Convert back from modivied value to pixels. // #15970
  54519. const pixelVal = correctFloat((val - localMin) * localA +
  54520. axis.minPixelPadding), isInside = val >= positions[0] &&
  54521. val <= positions[positions.length - 1];
  54522. // If the value is not inside the plot area, use the extended positions.
  54523. // (array contains also points that are outside of the plotArea).
  54524. if (!isInside) {
  54525. // When iterating for the first time,
  54526. // get the extended ordinal positional and assign them.
  54527. if (!ordinal.extendedOrdinalPositions) {
  54528. ordinal.extendedOrdinalPositions = (ordinal.getExtendedPositions());
  54529. }
  54530. positions = ordinal.extendedOrdinalPositions;
  54531. }
  54532. // In some cases (especially in early stages of the chart creation) the
  54533. // getExtendedPositions might return undefined.
  54534. if (positions && positions.length) {
  54535. const indexOf = positions.indexOf(val);
  54536. const index = indexOf !== -1 ? indexOf : correctFloat(ordinal.getIndexOfPoint(pixelVal, positions)), mantissa = correctFloat(index % 1);
  54537. // Check if the index is inside position array. If true,
  54538. // read/approximate value for that exact index.
  54539. if (index >= 0 && index <= positions.length - 1) {
  54540. const leftNeighbour = positions[Math.floor(index)], rightNeighbour = positions[Math.ceil(index)], distance = rightNeighbour - leftNeighbour;
  54541. return positions[Math.floor(index)] + mantissa * distance;
  54542. }
  54543. // For cases when the index is not in the extended ordinal position
  54544. // array, like when the value we are looking for exceed the
  54545. // available data, approximate that value based on the calculated
  54546. // slope.
  54547. const positionsLength = positions.length, firstPositionsValue = positions[0], lastPositionsValue = positions[positionsLength - 1], slope = (lastPositionsValue - firstPositionsValue) / (positionsLength - 1);
  54548. if (index < 0) {
  54549. return firstPositionsValue + slope * index;
  54550. }
  54551. return lastPositionsValue + slope * (index - positionsLength);
  54552. }
  54553. return val;
  54554. }
  54555. /**
  54556. * Internal function to calculate the precise index in ordinalPositions
  54557. * array.
  54558. * @private
  54559. */
  54560. function getIndexInArray(ordinalPositions, val) {
  54561. const index = OrdinalAxis.Additions.findIndexOf(ordinalPositions, val, true);
  54562. if (ordinalPositions[index] === val) {
  54563. return index;
  54564. }
  54565. const percent = (val - ordinalPositions[index]) /
  54566. (ordinalPositions[index + 1] - ordinalPositions[index]);
  54567. return index + percent;
  54568. }
  54569. /**
  54570. * @private
  54571. */
  54572. function onAxisAfterInit() {
  54573. const axis = this;
  54574. if (!axis.ordinal) {
  54575. axis.ordinal = new OrdinalAxis.Additions(axis);
  54576. }
  54577. }
  54578. /**
  54579. * @private
  54580. */
  54581. function onAxisFoundExtremes() {
  54582. const axis = this;
  54583. if (axis.isXAxis &&
  54584. defined(axis.options.overscroll) &&
  54585. axis.max === axis.dataMax &&
  54586. (
  54587. // Panning is an execption. We don't want to apply
  54588. // overscroll when panning over the dataMax
  54589. !axis.chart.mouseIsDown ||
  54590. axis.isInternal) && (
  54591. // Scrollbar buttons are the other execption:
  54592. !axis.eventArgs ||
  54593. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  54594. axis.max += axis.options.overscroll;
  54595. // Live data and buttons require translation for the min:
  54596. if (!axis.isInternal && defined(axis.userMin)) {
  54597. axis.min += axis.options.overscroll;
  54598. }
  54599. }
  54600. }
  54601. /**
  54602. * For ordinal axis, that loads data async, redraw axis after data is
  54603. * loaded. If we don't do that, axis will have the same extremes as
  54604. * previously, but ordinal positions won't be calculated. See #10290
  54605. * @private
  54606. */
  54607. function onAxisAfterSetScale() {
  54608. const axis = this;
  54609. if (axis.horiz && !axis.isDirty) {
  54610. axis.isDirty = axis.isOrdinal &&
  54611. axis.chart.navigator &&
  54612. !axis.chart.navigator.adaptToUpdatedData;
  54613. }
  54614. }
  54615. /**
  54616. * @private
  54617. */
  54618. function onAxisInitialAxisTranslation() {
  54619. const axis = this;
  54620. if (axis.ordinal) {
  54621. axis.ordinal.beforeSetTickPositions();
  54622. axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
  54623. }
  54624. }
  54625. /**
  54626. * Extending the Chart.pan method for ordinal axes
  54627. * @private
  54628. */
  54629. function onChartPan(e) {
  54630. const chart = this, xAxis = chart.xAxis[0], overscroll = xAxis.options.overscroll, chartX = e.originalEvent.chartX, panning = chart.options.chart.panning;
  54631. let runBase = false;
  54632. if (panning &&
  54633. panning.type !== 'y' &&
  54634. xAxis.options.ordinal &&
  54635. xAxis.series.length) {
  54636. const mouseDownX = chart.mouseDownX, extremes = xAxis.getExtremes(), dataMax = extremes.dataMax, min = extremes.min, max = extremes.max, hoverPoints = chart.hoverPoints, closestPointRange = (xAxis.closestPointRange ||
  54637. (xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)), pointPixelWidth = (xAxis.translationSlope *
  54638. (xAxis.ordinal.slope || closestPointRange)),
  54639. // how many ordinal units did we move?
  54640. movedUnits = Math.round((mouseDownX - chartX) / pointPixelWidth),
  54641. // get index of all the chart's points
  54642. extendedOrdinalPositions = xAxis.ordinal.getExtendedPositions(), extendedAxis = {
  54643. ordinal: {
  54644. positions: extendedOrdinalPositions,
  54645. extendedOrdinalPositions: extendedOrdinalPositions
  54646. }
  54647. }, index2val = xAxis.index2val, val2lin = xAxis.val2lin;
  54648. let trimmedRange, ordinalPositions, searchAxisLeft, searchAxisRight;
  54649. // we have an ordinal axis, but the data is equally spaced
  54650. if (!extendedAxis.ordinal.positions) {
  54651. runBase = true;
  54652. }
  54653. else if (Math.abs(movedUnits) > 1) {
  54654. // Remove active points for shared tooltip
  54655. if (hoverPoints) {
  54656. hoverPoints.forEach(function (point) {
  54657. point.setState();
  54658. });
  54659. }
  54660. if (movedUnits < 0) {
  54661. searchAxisLeft = extendedAxis;
  54662. searchAxisRight = xAxis.ordinal.positions ?
  54663. xAxis : extendedAxis;
  54664. }
  54665. else {
  54666. searchAxisLeft = xAxis.ordinal.positions ?
  54667. xAxis : extendedAxis;
  54668. searchAxisRight = extendedAxis;
  54669. }
  54670. // In grouped data series, the last ordinal position represents
  54671. // the grouped data, which is to the left of the real data max.
  54672. // If we don't compensate for this, we will be allowed to pan
  54673. // grouped data series passed the right of the plot area.
  54674. ordinalPositions = searchAxisRight.ordinal.positions;
  54675. if (dataMax >
  54676. ordinalPositions[ordinalPositions.length - 1]) {
  54677. ordinalPositions.push(dataMax);
  54678. }
  54679. // Get the new min and max values by getting the ordinal index
  54680. // for the current extreme, then add the moved units and
  54681. // translate back to values. This happens on the extended
  54682. // ordinal positions if the new position is out of range, else
  54683. // it happens on the current x axis which is smaller and faster.
  54684. chart.fixedRange = max - min;
  54685. trimmedRange = xAxis.navigatorAxis
  54686. .toFixedRange(void 0, void 0, index2val.apply(searchAxisLeft, [
  54687. val2lin.apply(searchAxisLeft, [min, true]) +
  54688. movedUnits
  54689. ]), index2val.apply(searchAxisRight, [
  54690. val2lin.apply(searchAxisRight, [max, true]) +
  54691. movedUnits
  54692. ]));
  54693. // Apply it if it is within the available data range
  54694. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  54695. trimmedRange.max <= Math.max(dataMax, max) +
  54696. overscroll) {
  54697. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  54698. }
  54699. chart.mouseDownX = chartX; // set new reference for next run
  54700. css(chart.container, { cursor: 'move' });
  54701. }
  54702. }
  54703. else {
  54704. runBase = true;
  54705. }
  54706. // revert to the linear chart.pan version
  54707. if (runBase || (panning && /y/.test(panning.type))) {
  54708. if (overscroll) {
  54709. xAxis.max = xAxis.dataMax + overscroll;
  54710. }
  54711. }
  54712. else {
  54713. e.preventDefault();
  54714. }
  54715. }
  54716. /**
  54717. * @private
  54718. */
  54719. function onSeriesUpdatedData() {
  54720. const xAxis = this.xAxis;
  54721. // Destroy the extended ordinal index on updated data
  54722. // and destroy extendedOrdinalPositions, #16055.
  54723. if (xAxis && xAxis.options.ordinal) {
  54724. delete xAxis.ordinal.index;
  54725. delete xAxis.ordinal.extendedOrdinalPositions;
  54726. }
  54727. }
  54728. /**
  54729. * Translate from a linear axis value to the corresponding ordinal axis
  54730. * position. If there are no gaps in the ordinal axis this will be the
  54731. * same. The translated value is the value that the point would have if
  54732. * the axis was linear, using the same min and max.
  54733. *
  54734. * @private
  54735. * @function Highcharts.Axis#val2lin
  54736. * @param {number} val
  54737. * The axis value.
  54738. * @param {boolean} [toIndex]
  54739. * Whether to return the index in the ordinalPositions or the new value.
  54740. */
  54741. function val2lin(val, toIndex) {
  54742. const axis = this, ordinal = axis.ordinal, ordinalPositions = ordinal.positions;
  54743. let slope = ordinal.slope, extendedOrdinalPositions = ordinal.extendedOrdinalPositions;
  54744. if (!ordinalPositions) {
  54745. return val;
  54746. }
  54747. const ordinalLength = ordinalPositions.length;
  54748. let ordinalIndex;
  54749. // If the searched value is inside visible plotArea, ivastigate the
  54750. // value basing on ordinalPositions.
  54751. if (ordinalPositions[0] <= val &&
  54752. ordinalPositions[ordinalLength - 1] >= val) {
  54753. ordinalIndex = getIndexInArray(ordinalPositions, val);
  54754. // final return value is based on ordinalIndex
  54755. }
  54756. else {
  54757. if (!extendedOrdinalPositions) {
  54758. extendedOrdinalPositions =
  54759. ordinal.getExtendedPositions &&
  54760. ordinal.getExtendedPositions();
  54761. ordinal.extendedOrdinalPositions = extendedOrdinalPositions;
  54762. }
  54763. if (!(extendedOrdinalPositions && extendedOrdinalPositions.length)) {
  54764. return val;
  54765. }
  54766. const length = extendedOrdinalPositions.length;
  54767. if (!slope) {
  54768. slope =
  54769. (extendedOrdinalPositions[length - 1] -
  54770. extendedOrdinalPositions[0]) /
  54771. length;
  54772. }
  54773. // OriginalPointReference is equal to the index of
  54774. // first point of ordinalPositions in extendedOrdinalPositions.
  54775. const originalPositionsReference = getIndexInArray(extendedOrdinalPositions, ordinalPositions[0]);
  54776. // If the searched value is outside the visiblePlotArea,
  54777. // check if it is inside extendedOrdinalPositions.
  54778. if (val >= extendedOrdinalPositions[0] &&
  54779. val <=
  54780. extendedOrdinalPositions[length - 1]) {
  54781. // Return Value
  54782. ordinalIndex = getIndexInArray(extendedOrdinalPositions, val) -
  54783. originalPositionsReference;
  54784. }
  54785. else {
  54786. // Since ordinal.slope is the average distance between 2
  54787. // points on visible plotArea, this can be used to calculete
  54788. // the approximate position of the point, which is outside
  54789. // the extededOrdinalPositions.
  54790. if (val < extendedOrdinalPositions[0]) {
  54791. const diff = extendedOrdinalPositions[0] - val, approximateIndexOffset = diff / slope;
  54792. ordinalIndex =
  54793. -originalPositionsReference -
  54794. approximateIndexOffset;
  54795. }
  54796. else {
  54797. const diff = val -
  54798. extendedOrdinalPositions[length - 1], approximateIndexOffset = diff / slope;
  54799. ordinalIndex =
  54800. approximateIndexOffset +
  54801. length -
  54802. originalPositionsReference;
  54803. }
  54804. }
  54805. }
  54806. return toIndex ? ordinalIndex : slope * (ordinalIndex || 0) +
  54807. ordinal.offset;
  54808. }
  54809. /* *
  54810. *
  54811. * Classes
  54812. *
  54813. * */
  54814. /**
  54815. * @private
  54816. */
  54817. class Additions {
  54818. /* *
  54819. *
  54820. * Constructors
  54821. *
  54822. * */
  54823. /**
  54824. * @private
  54825. */
  54826. constructor(axis) {
  54827. this.index = {};
  54828. this.axis = axis;
  54829. }
  54830. /* *
  54831. *
  54832. * Functions
  54833. *
  54834. * */
  54835. /**
  54836. * Calculate the ordinal positions before tick positions are calculated.
  54837. * @private
  54838. */
  54839. beforeSetTickPositions() {
  54840. 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;
  54841. let len, uniqueOrdinalPositions, dist, minIndex, maxIndex, slope, i, ordinalPositions = [], overscrollPointsRange = Number.MAX_VALUE, useOrdinal = false, adjustOrdinalExtremesPoints = false, isBoosted = false;
  54842. // Apply the ordinal logic
  54843. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  54844. let distanceBetweenPoint = 0;
  54845. axis.series.forEach(function (series, i) {
  54846. uniqueOrdinalPositions = [];
  54847. // For an axis with multiple series, check if the distance
  54848. // between points is identical throughout all series.
  54849. if (i > 0 &&
  54850. series.options.id !== 'highcharts-navigator-series' &&
  54851. series.processedXData.length > 1) {
  54852. adjustOrdinalExtremesPoints =
  54853. distanceBetweenPoint !== series.processedXData[1] -
  54854. series.processedXData[0];
  54855. }
  54856. distanceBetweenPoint =
  54857. series.processedXData[1] - series.processedXData[0];
  54858. if (series.boosted) {
  54859. isBoosted = series.boosted;
  54860. }
  54861. if ((!ignoreHiddenSeries || series.visible !== false) &&
  54862. (series
  54863. .takeOrdinalPosition !== false ||
  54864. hasBreaks)) {
  54865. // concatenate the processed X data into the existing
  54866. // positions, or the empty array
  54867. ordinalPositions = ordinalPositions.concat(series.processedXData);
  54868. len = ordinalPositions.length;
  54869. // remove duplicates (#1588)
  54870. ordinalPositions.sort(function (a, b) {
  54871. // without a custom function it is sorted as strings
  54872. return a - b;
  54873. });
  54874. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  54875. // Check for a single-point series:
  54876. series.closestPointRange, overscrollPointsRange));
  54877. if (len) {
  54878. i = 0;
  54879. while (i < len - 1) {
  54880. if (ordinalPositions[i] !==
  54881. ordinalPositions[i + 1]) {
  54882. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  54883. }
  54884. i++;
  54885. }
  54886. // Check first item:
  54887. if (uniqueOrdinalPositions[0] !==
  54888. ordinalPositions[0]) {
  54889. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  54890. }
  54891. ordinalPositions = uniqueOrdinalPositions;
  54892. }
  54893. }
  54894. });
  54895. // If the distance between points is not identical throughout
  54896. // all series, remove the first and last ordinal position to
  54897. // avoid enabling ordinal logic when it is not needed, #17405.
  54898. // Only for boosted series because changes are negligible.
  54899. if (adjustOrdinalExtremesPoints && isBoosted) {
  54900. ordinalPositions.pop();
  54901. ordinalPositions.shift();
  54902. }
  54903. // cache the length
  54904. len = ordinalPositions.length;
  54905. // Check if we really need the overhead of mapping axis data
  54906. // against the ordinal positions. If the series consist of
  54907. // evenly spaced data any way, we don't need any ordinal logic.
  54908. if (len > 2) { // two points have equal distance by default
  54909. dist = ordinalPositions[1] - ordinalPositions[0];
  54910. i = len - 1;
  54911. while (i-- && !useOrdinal) {
  54912. if (ordinalPositions[i + 1] - ordinalPositions[i] !==
  54913. dist) {
  54914. useOrdinal = true;
  54915. }
  54916. }
  54917. // When zooming in on a week, prevent axis padding for
  54918. // weekends even though the data within the week is evenly
  54919. // spaced.
  54920. if (!axis.options.keepOrdinalPadding &&
  54921. (ordinalPositions[0] - min > dist ||
  54922. (max -
  54923. ordinalPositions[ordinalPositions.length - 1]) > dist)) {
  54924. useOrdinal = true;
  54925. }
  54926. }
  54927. else if (axis.options.overscroll) {
  54928. if (len === 2) {
  54929. // Exactly two points, distance for overscroll is fixed:
  54930. overscrollPointsRange =
  54931. ordinalPositions[1] - ordinalPositions[0];
  54932. }
  54933. else if (len === 1) {
  54934. // We have just one point, closest distance is unknown.
  54935. // Assume then it is last point and overscrolled range:
  54936. overscrollPointsRange = axis.options.overscroll;
  54937. ordinalPositions = [
  54938. ordinalPositions[0],
  54939. ordinalPositions[0] + overscrollPointsRange
  54940. ];
  54941. }
  54942. else {
  54943. // In case of zooming in on overscrolled range, stick to
  54944. // the old range:
  54945. overscrollPointsRange = ordinal.overscrollPointsRange;
  54946. }
  54947. }
  54948. // Record the slope and offset to compute the linear values from
  54949. // the array index. Since the ordinal positions may exceed the
  54950. // current range, get the start and end positions within it
  54951. // (#719, #665b)
  54952. if (useOrdinal || axis.forceOrdinal) {
  54953. if (axis.options.overscroll) {
  54954. ordinal.overscrollPointsRange = overscrollPointsRange;
  54955. ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
  54956. }
  54957. // Register
  54958. ordinal.positions = ordinalPositions;
  54959. // This relies on the ordinalPositions being set. Use
  54960. // Math.max and Math.min to prevent padding on either sides
  54961. // of the data.
  54962. minIndex = axis.ordinal2lin(// #5979
  54963. Math.max(min, ordinalPositions[0]), true);
  54964. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  54965. // Set the slope and offset of the values compared to the
  54966. // indices in the ordinal positions
  54967. ordinal.slope = slope = (max - min) / (maxIndex - minIndex);
  54968. ordinal.offset = min - (minIndex * slope);
  54969. }
  54970. else {
  54971. ordinal.overscrollPointsRange = pick(axis.closestPointRange, ordinal.overscrollPointsRange);
  54972. ordinal.positions = axis.ordinal.slope = ordinal.offset =
  54973. void 0;
  54974. }
  54975. }
  54976. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  54977. ordinal.groupIntervalFactor = null; // reset for next run
  54978. }
  54979. /**
  54980. * Faster way of using the Array.indexOf method.
  54981. * Works for sorted arrays only with unique values.
  54982. *
  54983. * @param {Array} sortedArray
  54984. * The sorted array inside which we are looking for.
  54985. * @param {number} key
  54986. * The key to being found.
  54987. * @param {boolean} indirectSearch
  54988. * In case of lack of the point in the array, should return
  54989. * value be equal to -1 or the closest smaller index.
  54990. * @private
  54991. */
  54992. static findIndexOf(sortedArray, key, indirectSearch) {
  54993. let start = 0, end = sortedArray.length - 1, middle;
  54994. while (start < end) {
  54995. middle = Math.ceil((start + end) / 2);
  54996. // Key found as the middle element.
  54997. if (sortedArray[middle] <= key) {
  54998. // Continue searching to the right.
  54999. start = middle;
  55000. }
  55001. else {
  55002. // Continue searching to the left.
  55003. end = middle - 1;
  55004. }
  55005. }
  55006. if (sortedArray[start] === key) {
  55007. return start;
  55008. }
  55009. // Key could not be found.
  55010. return !indirectSearch ? -1 : start;
  55011. }
  55012. /**
  55013. * Get the ordinal positions for the entire data set. This is necessary
  55014. * in chart panning because we need to find out what points or data
  55015. * groups are available outside the visible range. When a panning
  55016. * operation starts, if an index for the given grouping does not exists,
  55017. * it is created and cached. This index is deleted on updated data, so
  55018. * it will be regenerated the next time a panning operation starts.
  55019. * @private
  55020. */
  55021. getExtendedPositions() {
  55022. const ordinal = this, axis = ordinal.axis, axisProto = axis.constructor.prototype, chart = axis.chart, grouping = axis.series[0].currentDataGrouping, key = grouping ?
  55023. grouping.count + grouping.unitName :
  55024. 'raw', overscroll = axis.options.overscroll, extremes = axis.getExtremes();
  55025. let fakeAxis, fakeSeries = void 0, ordinalIndex = ordinal.index;
  55026. // If this is the first time, or the ordinal index is deleted by
  55027. // updatedData,
  55028. // create it.
  55029. if (!ordinalIndex) {
  55030. ordinalIndex = ordinal.index = {};
  55031. }
  55032. if (!ordinalIndex[key]) {
  55033. // Create a fake axis object where the extended ordinal
  55034. // positions are emulated
  55035. fakeAxis = {
  55036. series: [],
  55037. chart: chart,
  55038. forceOrdinal: false,
  55039. getExtremes: function () {
  55040. return {
  55041. min: extremes.dataMin,
  55042. max: extremes.dataMax + overscroll
  55043. };
  55044. },
  55045. getGroupPixelWidth: axisProto.getGroupPixelWidth,
  55046. getTimeTicks: axisProto.getTimeTicks,
  55047. options: {
  55048. ordinal: true
  55049. },
  55050. ordinal: {
  55051. getGroupIntervalFactor: this.getGroupIntervalFactor
  55052. },
  55053. ordinal2lin: axisProto.ordinal2lin,
  55054. getIndexOfPoint: axisProto.getIndexOfPoint,
  55055. val2lin: axisProto.val2lin // #2590
  55056. };
  55057. fakeAxis.ordinal.axis = fakeAxis;
  55058. // Add the fake series to hold the full data, then apply
  55059. // processData to it
  55060. axis.series.forEach(function (series) {
  55061. fakeSeries = {
  55062. xAxis: fakeAxis,
  55063. xData: series.xData.slice(),
  55064. chart: chart,
  55065. destroyGroupedData: H.noop,
  55066. getProcessedData: Series.prototype.getProcessedData,
  55067. applyGrouping: Series.prototype.applyGrouping
  55068. };
  55069. fakeSeries.xData = fakeSeries.xData.concat(ordinal.getOverscrollPositions());
  55070. fakeSeries.options = {
  55071. dataGrouping: grouping ? {
  55072. firstAnchor: 'firstPoint',
  55073. anchor: 'middle',
  55074. lastAnchor: 'lastPoint',
  55075. enabled: true,
  55076. forced: true,
  55077. // doesn't matter which, use the fastest
  55078. approximation: 'open',
  55079. units: [[
  55080. grouping.unitName,
  55081. [grouping.count]
  55082. ]]
  55083. } : {
  55084. enabled: false
  55085. }
  55086. };
  55087. fakeAxis.series.push(fakeSeries);
  55088. series.processData.apply(fakeSeries);
  55089. });
  55090. // Force to use the ordinal when points are evenly spaced (e.g.
  55091. // weeks), #3825.
  55092. if ((fakeSeries.closestPointRange !==
  55093. fakeSeries.basePointRange) &&
  55094. fakeSeries.currentDataGrouping) {
  55095. fakeAxis.forceOrdinal = true;
  55096. }
  55097. // Run beforeSetTickPositions to compute the ordinalPositions
  55098. axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
  55099. // Cache it
  55100. ordinalIndex[key] = fakeAxis.ordinal.positions;
  55101. }
  55102. return ordinalIndex[key];
  55103. }
  55104. /**
  55105. * Find the factor to estimate how wide the plot area would have been if
  55106. * ordinal gaps were included. This value is used to compute an imagined
  55107. * plot width in order to establish the data grouping interval.
  55108. *
  55109. * A real world case is the intraday-candlestick example. Without this
  55110. * logic, it would show the correct data grouping when viewing a range
  55111. * within each day, but once moving the range to include the gap between
  55112. * two days, the interval would include the cut-away night hours and the
  55113. * data grouping would be wrong. So the below method tries to compensate
  55114. * by identifying the most common point interval, in this case days.
  55115. *
  55116. * An opposite case is presented in issue #718. We have a long array of
  55117. * daily data, then one point is appended one hour after the last point.
  55118. * We expect the data grouping not to change.
  55119. *
  55120. * In the future, if we find cases where this estimation doesn't work
  55121. * optimally, we might need to add a second pass to the data grouping
  55122. * logic, where we do another run with a greater interval if the number
  55123. * of data groups is more than a certain fraction of the desired group
  55124. * count.
  55125. * @private
  55126. */
  55127. getGroupIntervalFactor(xMin, xMax, series) {
  55128. const ordinal = this, axis = ordinal.axis, processedXData = series.processedXData, len = processedXData.length, distances = [];
  55129. let median, i, groupIntervalFactor = ordinal.groupIntervalFactor;
  55130. // Only do this computation for the first series, let the other
  55131. // inherit it (#2416)
  55132. if (!groupIntervalFactor) {
  55133. // Register all the distances in an array
  55134. for (i = 0; i < len - 1; i++) {
  55135. distances[i] = (processedXData[i + 1] -
  55136. processedXData[i]);
  55137. }
  55138. // Sort them and find the median
  55139. distances.sort(function (a, b) {
  55140. return a - b;
  55141. });
  55142. median = distances[Math.floor(len / 2)];
  55143. // Compensate for series that don't extend through the entire
  55144. // axis extent. #1675.
  55145. xMin = Math.max(xMin, processedXData[0]);
  55146. xMax = Math.min(xMax, processedXData[len - 1]);
  55147. ordinal.groupIntervalFactor = groupIntervalFactor =
  55148. (len * median) / (xMax - xMin);
  55149. }
  55150. // Return the factor needed for data grouping
  55151. return groupIntervalFactor;
  55152. }
  55153. /**
  55154. * Get index of point inside the ordinal positions array.
  55155. *
  55156. * @private
  55157. * @param {number} val
  55158. * The pixel value of a point.
  55159. *
  55160. * @param {Array<number>} [ordinallArray]
  55161. * An array of all points available on the axis for the given data set.
  55162. * Either ordinalPositions if the value is inside the plotArea or
  55163. * extendedOrdinalPositions if not.
  55164. */
  55165. getIndexOfPoint(val, ordinalArray) {
  55166. const ordinal = this, axis = ordinal.axis, firstPointVal = ordinal.positions ? ordinal.positions[0] : 0;
  55167. // Check whether the series has at least one point inside the chart
  55168. const hasPointsInside = function (series) {
  55169. return series.points.some((point) => !!point.isInside);
  55170. };
  55171. let firstPointX;
  55172. // When more series assign to axis, find the smallest one, #15987.
  55173. axis.series.forEach((series) => {
  55174. var _a;
  55175. const firstPoint = (_a = series.points) === null || _a === void 0 ? void 0 : _a[0];
  55176. if (defined(firstPoint === null || firstPoint === void 0 ? void 0 : firstPoint.plotX) &&
  55177. (firstPoint.plotX < firstPointX ||
  55178. !defined(firstPointX)) &&
  55179. hasPointsInside(series)) {
  55180. firstPointX = firstPoint.plotX;
  55181. }
  55182. });
  55183. // If undefined, give a default value
  55184. firstPointX !== null && firstPointX !== void 0 ? firstPointX : (firstPointX = axis.minPixelPadding);
  55185. // Distance in pixels between two points on the ordinal axis in the
  55186. // current zoom.
  55187. const ordinalPointPixelInterval = axis.translationSlope * (ordinal.slope ||
  55188. axis.closestPointRange ||
  55189. ordinal.overscrollPointsRange),
  55190. // toValue for the first point.
  55191. shiftIndex = correctFloat((val - firstPointX) / ordinalPointPixelInterval);
  55192. return Additions.findIndexOf(ordinalArray, firstPointVal, true) + shiftIndex;
  55193. }
  55194. /**
  55195. * Get ticks for an ordinal axis within a range where points don't
  55196. * exist. It is required when overscroll is enabled. We can't base on
  55197. * points, because we may not have any, so we use approximated
  55198. * pointRange and generate these ticks between Axis.dataMax,
  55199. * Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
  55200. * navigator scrolling.
  55201. * @private
  55202. */
  55203. getOverscrollPositions() {
  55204. const ordinal = this, axis = ordinal.axis, extraRange = axis.options.overscroll, distance = ordinal.overscrollPointsRange, positions = [];
  55205. let max = axis.dataMax;
  55206. if (defined(distance)) {
  55207. // Max + pointRange because we need to scroll to the last
  55208. while (max <= axis.dataMax + extraRange) {
  55209. max += distance;
  55210. positions.push(max);
  55211. }
  55212. }
  55213. return positions;
  55214. }
  55215. /**
  55216. * Make the tick intervals closer because the ordinal gaps make the
  55217. * ticks spread out or cluster.
  55218. * @private
  55219. */
  55220. postProcessTickInterval(tickInterval) {
  55221. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/. This is a case
  55222. // where this algorithm doesn't work optimally. In this case, the
  55223. // tick labels are spread out per week, but all the gaps reside
  55224. // within weeks. So we have a situation where the labels are courser
  55225. // than the ordinal gaps, and thus the tick interval should not be
  55226. // altered.
  55227. const ordinal = this, axis = ordinal.axis, ordinalSlope = ordinal.slope;
  55228. let ret;
  55229. if (ordinalSlope) {
  55230. if (!axis.options.breaks) {
  55231. ret = (tickInterval /
  55232. (ordinalSlope / axis.closestPointRange));
  55233. }
  55234. else {
  55235. ret = axis.closestPointRange || tickInterval; // #7275
  55236. }
  55237. }
  55238. else {
  55239. ret = tickInterval;
  55240. }
  55241. return ret;
  55242. }
  55243. }
  55244. OrdinalAxis.Additions = Additions;
  55245. })(OrdinalAxis || (OrdinalAxis = {}));
  55246. /* *
  55247. *
  55248. * Default Export
  55249. *
  55250. * */
  55251. return OrdinalAxis;
  55252. });
  55253. _registerModule(_modules, 'Series/HLC/HLCPoint.js', [_modules['Core/Series/SeriesRegistry.js']], function (SeriesRegistry) {
  55254. /* *
  55255. *
  55256. * (c) 2010-2021 Pawel Lysy
  55257. *
  55258. * License: www.highcharts.com/license
  55259. *
  55260. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55261. *
  55262. * */
  55263. const { column: { prototype: { pointClass: ColumnPoint } } } = SeriesRegistry.seriesTypes;
  55264. /* *
  55265. *
  55266. * Class
  55267. *
  55268. * */
  55269. class HLCPoint extends ColumnPoint {
  55270. constructor() {
  55271. /* *
  55272. *
  55273. * Properties
  55274. *
  55275. * */
  55276. super(...arguments);
  55277. this.close = void 0;
  55278. this.high = void 0;
  55279. this.low = void 0;
  55280. this.options = void 0;
  55281. this.plotClose = void 0;
  55282. this.series = void 0;
  55283. }
  55284. }
  55285. /* *
  55286. *
  55287. * Default Export
  55288. *
  55289. * */
  55290. return HLCPoint;
  55291. });
  55292. _registerModule(_modules, 'Series/HLC/HLCSeriesDefaults.js', [], function () {
  55293. /* *
  55294. *
  55295. * (c) 2010-2021 Pawel Lysy
  55296. *
  55297. * License: www.highcharts.com/license
  55298. *
  55299. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55300. *
  55301. * */
  55302. /* *
  55303. *
  55304. * API Options
  55305. *
  55306. * */
  55307. /**
  55308. * An HLC chart is a style of financial chart used to describe price
  55309. * movements over time. It displays high, low and close values per
  55310. * data point.
  55311. *
  55312. * @sample stock/demo/hlc/
  55313. * HLC chart
  55314. *
  55315. * @extends plotOptions.column
  55316. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  55317. * stack
  55318. * @product highstock
  55319. * @optionparent plotOptions.hlc
  55320. */
  55321. const HLCSeriesDefaults = {
  55322. /**
  55323. * The approximate pixel width of each group. If for example a series
  55324. * with 30 points is displayed over a 600 pixel wide plot area, no
  55325. * grouping is performed. If however the series contains so many points
  55326. * that the spacing is less than the groupPixelWidth, Highcharts will
  55327. * try to group it into appropriate groups so that each is more or less
  55328. * two pixels wide. Defaults to `5`.
  55329. *
  55330. * @type {number}
  55331. * @default 5
  55332. * @product highstock
  55333. * @apioption plotOptions.hlc.dataGrouping.groupPixelWidth
  55334. */
  55335. /**
  55336. * @type {Highcharts.DataGroupingApproximationValue|Function}
  55337. * @default hlc
  55338. * @product highstock
  55339. * @apioption plotOptions.hlc.dataGrouping.approximation
  55340. */
  55341. /**
  55342. * @default close
  55343. * @apioption plotOptions.hlc.colorKey
  55344. */
  55345. /**
  55346. * The pixel width of the line/border. Defaults to `1`.
  55347. *
  55348. * @sample {highstock} stock/plotoptions/hlc-linewidth/
  55349. * A greater line width
  55350. *
  55351. * @type {number}
  55352. * @default 1
  55353. * @product highstock
  55354. *
  55355. * @public
  55356. */
  55357. lineWidth: 1,
  55358. tooltip: {
  55359. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  55360. '<b> {series.name}</b><br/>' +
  55361. 'High: {point.high}<br/>' +
  55362. 'Low: {point.low}<br/>' +
  55363. 'Close: {point.close}<br/>'
  55364. },
  55365. /**
  55366. * @type {number|null}
  55367. */
  55368. threshold: null,
  55369. states: {
  55370. /**
  55371. * @extends plotOptions.column.states.hover
  55372. * @product highstock
  55373. */
  55374. hover: {
  55375. /**
  55376. * The pixel width of the line representing the HLC point.
  55377. *
  55378. * @type {number}
  55379. * @default 3
  55380. * @product highstock
  55381. */
  55382. lineWidth: 3
  55383. }
  55384. },
  55385. /**
  55386. * Determines which one of `high`, `low`, `close` values should
  55387. * be represented as `point.y`, which is later used to set dataLabel
  55388. * position and [compare](#plotOptions.series.compare).
  55389. *
  55390. * @sample {highstock} stock/plotoptions/hlc-pointvalkey/
  55391. * Possible values
  55392. *
  55393. * @declare Highcharts.OptionsHLCPointValKeyValue
  55394. * @type {string}
  55395. * @default close
  55396. * @validvalue ["high", "low", "close"]
  55397. * @product highstock
  55398. * @apioption plotOptions.hlc.pointValKey
  55399. */
  55400. /**
  55401. * @default close
  55402. * @apioption plotOptions.hlc.colorKey
  55403. */
  55404. stickyTracking: true
  55405. };
  55406. /**
  55407. * A `hlc` series. If the [type](#series.hlc.type) option is not
  55408. * specified, it is inherited from [chart.type](#chart.type).
  55409. *
  55410. * @extends series,plotOptions.hlc
  55411. * @excluding dataParser, dataURL
  55412. * @product highstock
  55413. * @apioption series.hlc
  55414. */
  55415. /**
  55416. * An array of data points for the series. For the `hlc` series type,
  55417. * points can be given in the following ways:
  55418. *
  55419. * 1. An array of arrays with 4 or 3 values. In this case, the values correspond
  55420. * to `x,high,low,close`. If the first value is a string, it is applied
  55421. * as the name of the point, and the `x` value is inferred. The `x` value can
  55422. * also be omitted, in which case the inner arrays should be of length of 3\.
  55423. * Then the `x` value is automatically calculated, either starting at 0 and
  55424. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  55425. * series options.
  55426. * ```js
  55427. * data: [
  55428. * [0, 5, 6, 7],
  55429. * [1, 4, 8, 2],
  55430. * [2, 3, 4, 10]
  55431. * ]
  55432. * ```
  55433. *
  55434. * 2. An array of objects with named values. The following snippet shows only a
  55435. * few settings, see the complete options set below. If the total number of
  55436. * data points exceeds the series'
  55437. * [turboThreshold](#series.hlc.turboThreshold), this option is not
  55438. * available.
  55439. * ```js
  55440. * data: [{
  55441. * x: 1,
  55442. * high: 4,
  55443. * low: 5,
  55444. * close: 2,
  55445. * name: "Point2",
  55446. * color: "#00FF00"
  55447. * }, {
  55448. * x: 1,
  55449. * high: 3,
  55450. * low: 6,
  55451. * close: 7,
  55452. * name: "Point1",
  55453. * color: "#FF00FF"
  55454. * }]
  55455. * ```
  55456. *
  55457. * @type {Array<Array<(number|string),number,number>|Array<(number|string),number,number,number>|*>}
  55458. * @extends series.arearange.data
  55459. * @excluding y, marker
  55460. * @product highstock
  55461. * @apioption series.hlc.data
  55462. */
  55463. /**
  55464. * The closing value of each data point.
  55465. *
  55466. * @type {number}
  55467. * @product highstock
  55468. * @apioption series.hlc.data.close
  55469. */
  55470. (''); // keeps doclets above in JS file
  55471. /* *
  55472. *
  55473. * Default Export
  55474. *
  55475. * */
  55476. return HLCSeriesDefaults;
  55477. });
  55478. _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) {
  55479. /* *
  55480. *
  55481. * (c) 2010-2021 Pawel Lysy
  55482. *
  55483. * License: www.highcharts.com/license
  55484. *
  55485. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55486. *
  55487. * */
  55488. const { column: ColumnSeries } = SeriesRegistry.seriesTypes;
  55489. const { extend, merge } = U;
  55490. /* *
  55491. *
  55492. * Class
  55493. *
  55494. * */
  55495. /**
  55496. * The hlc series type.
  55497. *
  55498. * @private
  55499. * @class
  55500. * @name Highcharts.seriesTypes.hlc
  55501. *
  55502. * @augments Highcharts.Series
  55503. */
  55504. class HLCSeries extends ColumnSeries {
  55505. constructor() {
  55506. /* *
  55507. *
  55508. * Static Properties
  55509. *
  55510. * */
  55511. super(...arguments);
  55512. /* *
  55513. *
  55514. * Properties
  55515. *
  55516. * */
  55517. this.data = void 0;
  55518. this.options = void 0;
  55519. this.points = void 0;
  55520. this.yData = void 0;
  55521. }
  55522. /* *
  55523. *
  55524. * Functions
  55525. *
  55526. * */
  55527. /**
  55528. * Extend the path if close is not between high and low.
  55529. *
  55530. * @param {SVGPath} path the path array of the point
  55531. * @param {number} halfStrokeWidth
  55532. * @param {number} value value of the point to which the stem should be extended
  55533. */
  55534. extendStem(path, halfStrokeWidth, value) {
  55535. const start = path[0];
  55536. const end = path[1];
  55537. // We don't need to worry about crisp - close value
  55538. // is already crisped and halfStrokeWidth should remove it.
  55539. if (typeof start[2] === 'number') {
  55540. start[2] = Math.max(value + halfStrokeWidth, start[2]);
  55541. }
  55542. if (typeof end[2] === 'number') {
  55543. end[2] = Math.min(value - halfStrokeWidth, end[2]);
  55544. }
  55545. }
  55546. /**
  55547. * Function to create SVGPath of the point based on the
  55548. * plot positions of this point.
  55549. * @private
  55550. */
  55551. getPointPath(point, graphic) {
  55552. // crisp vector coordinates
  55553. const strokeWidth = graphic.strokeWidth(), series = point.series, crispCorr = (strokeWidth % 2) / 2,
  55554. // #2596:
  55555. crispX = Math.round(point.plotX) - crispCorr, halfWidth = Math.round(point.shapeArgs.width / 2);
  55556. let plotClose = point.plotClose;
  55557. // the vertical stem
  55558. const path = [
  55559. ['M', crispX, Math.round(point.yBottom)],
  55560. ['L', crispX, Math.round(point.plotHigh)]
  55561. ];
  55562. // close
  55563. if (point.close !== null) {
  55564. plotClose = Math.round(point.plotClose) + crispCorr;
  55565. path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
  55566. series.extendStem(path, strokeWidth / 2, plotClose);
  55567. }
  55568. return path;
  55569. }
  55570. /**
  55571. * Draw single point
  55572. * @private
  55573. */
  55574. drawSinglePoint(point) {
  55575. const series = point.series, chart = series.chart;
  55576. let path, graphic = point.graphic;
  55577. if (typeof point.plotY !== 'undefined') {
  55578. // Create and/or update the graphic
  55579. if (!graphic) {
  55580. point.graphic = graphic = chart.renderer.path()
  55581. .add(series.group);
  55582. }
  55583. if (!chart.styledMode) {
  55584. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  55585. }
  55586. // crisp vector coordinates
  55587. path = series.getPointPath(point, graphic);
  55588. graphic[!graphic ? 'attr' : 'animate']({ d: path })
  55589. .addClass(point.getClassName(), true);
  55590. }
  55591. }
  55592. /**
  55593. * Draw the data points
  55594. * @private
  55595. */
  55596. drawPoints() {
  55597. this.points.forEach(this.drawSinglePoint);
  55598. }
  55599. /**
  55600. * @private
  55601. * @function Highcharts.seriesTypes.hlc#init
  55602. */
  55603. init() {
  55604. super.init.apply(this, arguments);
  55605. this.options.stacking = void 0; // #8817
  55606. }
  55607. /**
  55608. * Postprocess mapping between options and SVG attributes
  55609. * @private
  55610. */
  55611. pointAttribs(point, state) {
  55612. const attribs = super.pointAttribs.call(this, point, state);
  55613. delete attribs.fill;
  55614. return attribs;
  55615. }
  55616. toYData(point) {
  55617. // return a plain array for speedy calculation
  55618. return [point.high, point.low, point.close];
  55619. }
  55620. /**
  55621. * Translate data points from raw values x and y to plotX and plotY
  55622. *
  55623. * @private
  55624. * @function Highcharts.seriesTypes.hlc#translate
  55625. */
  55626. translate() {
  55627. const series = this, yAxis = series.yAxis, names = (this.pointArrayMap && this.pointArrayMap.slice()) || [], translated = names.map((name) => `plot${name.charAt(0).toUpperCase() + name.slice(1)}`);
  55628. translated.push('yBottom');
  55629. names.push('low');
  55630. super.translate.apply(series);
  55631. // Do the translation
  55632. series.points.forEach(function (point) {
  55633. names.forEach(function (name, i) {
  55634. let value = point[name];
  55635. if (value !== null) {
  55636. if (series.dataModify) {
  55637. value = series.dataModify.modifyValue(value);
  55638. }
  55639. point[translated[i]] =
  55640. yAxis.toPixels(value, true);
  55641. }
  55642. });
  55643. // Align the tooltip to the high value to avoid covering the
  55644. // point
  55645. point.tooltipPos[1] =
  55646. point.plotHigh + yAxis.pos - series.chart.plotTop;
  55647. });
  55648. }
  55649. }
  55650. HLCSeries.defaultOptions = merge(ColumnSeries.defaultOptions, HLCSeriesDefaults);
  55651. extend(HLCSeries.prototype, {
  55652. pointClass: HLCPoint,
  55653. animate: null,
  55654. directTouch: false,
  55655. pointArrayMap: ['high', 'low', 'close'],
  55656. pointAttrToOptions: {
  55657. stroke: 'color',
  55658. 'stroke-width': 'lineWidth'
  55659. },
  55660. pointValKey: 'close'
  55661. });
  55662. SeriesRegistry.registerSeriesType('hlc', HLCSeries);
  55663. /* *
  55664. *
  55665. * Default Export
  55666. *
  55667. * */
  55668. return HLCSeries;
  55669. });
  55670. _registerModule(_modules, 'Series/OHLC/OHLCPoint.js', [_modules['Core/Series/SeriesRegistry.js']], function (SeriesRegistry) {
  55671. /* *
  55672. *
  55673. * (c) 2010-2021 Torstein Honsi
  55674. *
  55675. * License: www.highcharts.com/license
  55676. *
  55677. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55678. *
  55679. * */
  55680. const { seriesTypes: { hlc: HLCSeries } } = SeriesRegistry;
  55681. /* *
  55682. *
  55683. * Class
  55684. *
  55685. * */
  55686. class OHLCPoint extends HLCSeries.prototype.pointClass {
  55687. constructor() {
  55688. /* *
  55689. *
  55690. * Properties
  55691. *
  55692. * */
  55693. super(...arguments);
  55694. this.open = void 0;
  55695. this.options = void 0;
  55696. this.plotOpen = void 0;
  55697. this.series = void 0;
  55698. }
  55699. /* *
  55700. *
  55701. * Functions
  55702. *
  55703. * */
  55704. /**
  55705. * Extend the parent method by adding up or down to the class name.
  55706. * @private
  55707. * @function Highcharts.seriesTypes.ohlc#getClassName
  55708. */
  55709. getClassName() {
  55710. return super.getClassName.call(this) +
  55711. (this.open < this.close ?
  55712. ' highcharts-point-up' :
  55713. ' highcharts-point-down');
  55714. }
  55715. /**
  55716. * Save upColor as point color (#14826).
  55717. * @private
  55718. * @function Highcharts.seriesTypes.ohlc#resolveUpColor
  55719. */
  55720. resolveUpColor() {
  55721. if (this.open < this.close &&
  55722. !this.options.color &&
  55723. this.series.options.upColor) {
  55724. this.color = this.series.options.upColor;
  55725. }
  55726. }
  55727. /**
  55728. * Extend the parent method by saving upColor.
  55729. * @private
  55730. * @function Highcharts.seriesTypes.ohlc#resolveColor
  55731. */
  55732. resolveColor() {
  55733. super.resolveColor();
  55734. this.resolveUpColor();
  55735. }
  55736. /**
  55737. * Extend the parent method by saving upColor.
  55738. * @private
  55739. * @function Highcharts.seriesTypes.ohlc#getZone
  55740. *
  55741. * @return {Highcharts.SeriesZonesOptionsObject}
  55742. * The zone item.
  55743. */
  55744. getZone() {
  55745. const zone = super.getZone();
  55746. this.resolveUpColor();
  55747. return zone;
  55748. }
  55749. /**
  55750. * Extend the parent method by resolving up/down colors (#15849)
  55751. * @private
  55752. **/
  55753. applyOptions() {
  55754. super.applyOptions.apply(this, arguments);
  55755. if (this.resolveColor) {
  55756. this.resolveColor();
  55757. }
  55758. return this;
  55759. }
  55760. }
  55761. /* *
  55762. *
  55763. * Default Export
  55764. *
  55765. * */
  55766. return OHLCPoint;
  55767. });
  55768. _registerModule(_modules, 'Series/OHLC/OHLCSeriesDefaults.js', [], function () {
  55769. /* *
  55770. *
  55771. * (c) 2010-2021 Torstein Honsi
  55772. *
  55773. * License: www.highcharts.com/license
  55774. *
  55775. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55776. *
  55777. * */
  55778. /* *
  55779. *
  55780. * API Options
  55781. *
  55782. * */
  55783. /**
  55784. * An OHLC chart is a style of financial chart used to describe price
  55785. * movements over time. It displays open, high, low and close values per
  55786. * data point.
  55787. *
  55788. * @sample stock/demo/ohlc
  55789. * OHLC chart
  55790. *
  55791. * @extends plotOptions.hlc
  55792. * @product highstock
  55793. * @optionparent plotOptions.ohlc
  55794. */
  55795. const OHLCSeriesDefaults = {
  55796. /**
  55797. * @type {Highcharts.DataGroupingApproximationValue|Function}
  55798. * @default ohlc
  55799. * @product highstock
  55800. * @apioption plotOptions.ohlc.dataGrouping.approximation
  55801. */
  55802. /**
  55803. * Determines which one of `open`, `high`, `low`, `close` values should
  55804. * be represented as `point.y`, which is later used to set dataLabel
  55805. * position and [compare](#plotOptions.series.compare).
  55806. *
  55807. * @declare Highcharts.OptionsPointValKeyValue
  55808. * @default close
  55809. * @validvalue ["open", "high", "low", "close"]
  55810. * @product highstock
  55811. * @apioption plotOptions.ohlc.pointValKey
  55812. */
  55813. /**
  55814. * Line color for up points.
  55815. *
  55816. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  55817. * @product highstock
  55818. * @apioption plotOptions.ohlc.upColor
  55819. */
  55820. tooltip: {
  55821. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  55822. '<b> {series.name}</b><br/>' +
  55823. 'Open: {point.open}<br/>' +
  55824. 'High: {point.high}<br/>' +
  55825. 'Low: {point.low}<br/>' +
  55826. 'Close: {point.close}<br/>'
  55827. }
  55828. };
  55829. /**
  55830. * The parameter allows setting line series type and use OHLC indicators.
  55831. * Data in OHLC format is required.
  55832. *
  55833. * @sample {highstock} stock/indicators/use-ohlc-data
  55834. * Use OHLC data format to plot line chart
  55835. *
  55836. * @type {boolean}
  55837. * @product highstock
  55838. * @apioption plotOptions.line.useOhlcData
  55839. */
  55840. /**
  55841. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  55842. * specified, it is inherited from [chart.type](#chart.type).
  55843. *
  55844. * @extends series,plotOptions.ohlc
  55845. * @excluding dataParser, dataURL
  55846. * @product highstock
  55847. * @apioption series.ohlc
  55848. */
  55849. /**
  55850. * An array of data points for the series. For the `ohlc` series type,
  55851. * points can be given in the following ways:
  55852. *
  55853. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  55854. * to `x,open,high,low,close`. If the first value is a string, it is applied
  55855. * as the name of the point, and the `x` value is inferred. The `x` value can
  55856. * also be omitted, in which case the inner arrays should be of length 4\.
  55857. * Then the `x` value is automatically calculated, either starting at 0 and
  55858. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  55859. * series options.
  55860. * ```js
  55861. * data: [
  55862. * [0, 6, 5, 6, 7],
  55863. * [1, 9, 4, 8, 2],
  55864. * [2, 6, 3, 4, 10]
  55865. * ]
  55866. * ```
  55867. *
  55868. * 2. An array of objects with named values. The following snippet shows only a
  55869. * few settings, see the complete options set below. If the total number of
  55870. * data points exceeds the series'
  55871. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  55872. * available.
  55873. * ```js
  55874. * data: [{
  55875. * x: 1,
  55876. * open: 3,
  55877. * high: 4,
  55878. * low: 5,
  55879. * close: 2,
  55880. * name: "Point2",
  55881. * color: "#00FF00"
  55882. * }, {
  55883. * x: 1,
  55884. * open: 4,
  55885. * high: 3,
  55886. * low: 6,
  55887. * close: 7,
  55888. * name: "Point1",
  55889. * color: "#FF00FF"
  55890. * }]
  55891. * ```
  55892. *
  55893. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  55894. * @extends series.arearange.data
  55895. * @excluding y, marker
  55896. * @product highstock
  55897. * @apioption series.ohlc.data
  55898. */
  55899. /**
  55900. * The closing value of each data point.
  55901. *
  55902. * @type {number}
  55903. * @product highstock
  55904. * @apioption series.ohlc.data.close
  55905. */
  55906. /**
  55907. * The opening value of each data point.
  55908. *
  55909. * @type {number}
  55910. * @product highstock
  55911. * @apioption series.ohlc.data.open
  55912. */
  55913. ''; // adds doclets above to transpilat
  55914. /* *
  55915. *
  55916. * Default Export
  55917. *
  55918. * */
  55919. return OHLCSeriesDefaults;
  55920. });
  55921. _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) {
  55922. /* *
  55923. *
  55924. * (c) 2010-2021 Torstein Honsi
  55925. *
  55926. * License: www.highcharts.com/license
  55927. *
  55928. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  55929. *
  55930. * */
  55931. const { seriesTypes: { hlc: HLCSeries } } = SeriesRegistry;
  55932. const { addEvent, extend, merge } = U;
  55933. /* *
  55934. *
  55935. * Constants
  55936. *
  55937. * */
  55938. const composedMembers = [];
  55939. /* *
  55940. *
  55941. * Functions
  55942. *
  55943. * */
  55944. /**
  55945. * @private
  55946. */
  55947. function onSeriesAfterSetOptions(e) {
  55948. const options = e.options, dataGrouping = options.dataGrouping;
  55949. if (dataGrouping &&
  55950. options.useOhlcData &&
  55951. options.id !== 'highcharts-navigator-series') {
  55952. dataGrouping.approximation = 'ohlc';
  55953. }
  55954. }
  55955. /**
  55956. * Add useOhlcData option
  55957. * @private
  55958. */
  55959. function onSeriesInit(eventOptions) {
  55960. // eslint-disable-next-line no-invalid-this
  55961. const series = this, options = eventOptions.options;
  55962. if (options.useOhlcData &&
  55963. options.id !== 'highcharts-navigator-series') {
  55964. extend(series, {
  55965. pointValKey: OHLCSeries.prototype.pointValKey,
  55966. // keys: ohlcProto.keys, // @todo potentially nonsense
  55967. pointArrayMap: OHLCSeries.prototype.pointArrayMap,
  55968. toYData: OHLCSeries.prototype.toYData
  55969. });
  55970. }
  55971. }
  55972. /* *
  55973. *
  55974. * Class
  55975. *
  55976. * */
  55977. /**
  55978. * The ohlc series type.
  55979. *
  55980. * @private
  55981. * @class
  55982. * @name Highcharts.seriesTypes.ohlc
  55983. *
  55984. * @augments Highcharts.Series
  55985. */
  55986. class OHLCSeries extends HLCSeries {
  55987. constructor() {
  55988. /* *
  55989. *
  55990. * Static Properties
  55991. *
  55992. * */
  55993. super(...arguments);
  55994. /* *
  55995. *
  55996. * Properties
  55997. *
  55998. * */
  55999. this.data = void 0;
  56000. this.options = void 0;
  56001. this.points = void 0;
  56002. }
  56003. /* *
  56004. *
  56005. * Static Functions
  56006. *
  56007. * */
  56008. static compose(SeriesClass, ..._args) {
  56009. if (U.pushUnique(composedMembers, SeriesClass)) {
  56010. addEvent(SeriesClass, 'afterSetOptions', onSeriesAfterSetOptions);
  56011. addEvent(SeriesClass, 'init', onSeriesInit);
  56012. }
  56013. }
  56014. /* *
  56015. *
  56016. * Functions
  56017. *
  56018. * */
  56019. getPointPath(point, graphic) {
  56020. 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);
  56021. let plotOpen = point.plotOpen;
  56022. // crisp vector coordinates
  56023. if (point.open !== null) {
  56024. plotOpen = Math.round(point.plotOpen) + crispCorr;
  56025. path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
  56026. super.extendStem(path, strokeWidth / 2, plotOpen);
  56027. }
  56028. return path;
  56029. }
  56030. /**
  56031. * Postprocess mapping between options and SVG attributes
  56032. * @private
  56033. */
  56034. pointAttribs(point, state) {
  56035. const attribs = super.pointAttribs.call(this, point, state), options = this.options;
  56036. delete attribs.fill;
  56037. if (!point.options.color &&
  56038. options.upColor &&
  56039. point.open < point.close) {
  56040. attribs.stroke = options.upColor;
  56041. }
  56042. return attribs;
  56043. }
  56044. toYData(point) {
  56045. // return a plain array for speedy calculation
  56046. return [point.open, point.high, point.low, point.close];
  56047. }
  56048. }
  56049. OHLCSeries.defaultOptions = merge(HLCSeries.defaultOptions, OHLCSeriesDefaults);
  56050. extend(OHLCSeries.prototype, {
  56051. pointClass: OHLCPoint,
  56052. pointArrayMap: ['open', 'high', 'low', 'close']
  56053. });
  56054. SeriesRegistry.registerSeriesType('ohlc', OHLCSeries);
  56055. /* *
  56056. *
  56057. * Default Export
  56058. *
  56059. * */
  56060. return OHLCSeries;
  56061. });
  56062. _registerModule(_modules, 'Series/Candlestick/CandlestickSeriesDefaults.js', [_modules['Core/Defaults.js'], _modules['Core/Utilities.js']], function (D, U) {
  56063. /* *
  56064. *
  56065. * (c) 2010-2021 Torstein Honsi
  56066. *
  56067. * License: www.highcharts.com/license
  56068. *
  56069. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56070. *
  56071. * */
  56072. const { defaultOptions } = D;
  56073. const { merge } = U;
  56074. /* *
  56075. *
  56076. * API Options
  56077. *
  56078. * */
  56079. /**
  56080. * A candlestick chart is a style of financial chart used to describe
  56081. * price movements over time.
  56082. *
  56083. * @sample stock/demo/candlestick/
  56084. * Candlestick chart
  56085. *
  56086. * @extends plotOptions.ohlc
  56087. * @excluding borderColor,borderRadius,borderWidth
  56088. * @product highstock
  56089. * @optionparent plotOptions.candlestick
  56090. */
  56091. const CandlestickSeriesDefaults = {
  56092. /**
  56093. * The specific line color for up candle sticks. The default is to
  56094. * inherit the general `lineColor` setting.
  56095. *
  56096. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  56097. * Candlestick line colors
  56098. *
  56099. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56100. * @since 1.3.6
  56101. * @product highstock
  56102. * @apioption plotOptions.candlestick.upLineColor
  56103. */
  56104. states: {
  56105. /**
  56106. * @extends plotOptions.column.states.hover
  56107. * @product highstock
  56108. */
  56109. hover: {
  56110. /**
  56111. * The pixel width of the line/border around the
  56112. * candlestick.
  56113. *
  56114. * @product highstock
  56115. */
  56116. lineWidth: 2
  56117. }
  56118. },
  56119. /**
  56120. * @type {number|null}
  56121. * @product highstock
  56122. */
  56123. threshold: null,
  56124. /**
  56125. * The color of the line/border of the candlestick.
  56126. *
  56127. * In styled mode, the line stroke can be set with the
  56128. * `.highcharts-candlestick-series .highcahrts-point` rule.
  56129. *
  56130. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  56131. *
  56132. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  56133. * Candlestick line colors
  56134. *
  56135. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56136. * @default #000000
  56137. * @product highstock
  56138. */
  56139. lineColor: "#000000" /* Palette.neutralColor100 */,
  56140. /**
  56141. * The pixel width of the candlestick line/border. Defaults to `1`.
  56142. *
  56143. *
  56144. * In styled mode, the line stroke width can be set with the
  56145. * `.highcharts-candlestick-series .highcahrts-point` rule.
  56146. *
  56147. * @product highstock
  56148. */
  56149. lineWidth: 1,
  56150. /**
  56151. * The fill color of the candlestick when values are rising.
  56152. *
  56153. * In styled mode, the up color can be set with the
  56154. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  56155. *
  56156. * @sample {highstock} stock/plotoptions/candlestick-color/
  56157. * Custom colors
  56158. * @sample {highstock} highcharts/css/candlestick/
  56159. * Colors in styled mode
  56160. *
  56161. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56162. * @default #ffffff
  56163. * @product highstock
  56164. */
  56165. upColor: "#ffffff" /* Palette.backgroundColor */,
  56166. /**
  56167. * @product highstock
  56168. */
  56169. stickyTracking: true
  56170. };
  56171. /**
  56172. * A `candlestick` series. If the [type](#series.candlestick.type)
  56173. * option is not specified, it is inherited from [chart.type](
  56174. * #chart.type).
  56175. *
  56176. * @type {*}
  56177. * @extends series,plotOptions.candlestick
  56178. * @excluding dataParser, dataURL, marker
  56179. * @product highstock
  56180. * @apioption series.candlestick
  56181. */
  56182. /**
  56183. * An array of data points for the series. For the `candlestick` series
  56184. * type, points can be given in the following ways:
  56185. *
  56186. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  56187. * to `x,open,high,low,close`. If the first value is a string, it is applied
  56188. * as the name of the point, and the `x` value is inferred. The `x` value can
  56189. * also be omitted, in which case the inner arrays should be of length 4.
  56190. * Then the `x` value is automatically calculated, either starting at 0 and
  56191. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  56192. * series options.
  56193. * ```js
  56194. * data: [
  56195. * [0, 7, 2, 0, 4],
  56196. * [1, 1, 4, 2, 8],
  56197. * [2, 3, 3, 9, 3]
  56198. * ]
  56199. * ```
  56200. *
  56201. * 2. An array of objects with named values. The following snippet shows only a
  56202. * few settings, see the complete options set below. If the total number of
  56203. * data points exceeds the series'
  56204. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  56205. * available.
  56206. * ```js
  56207. * data: [{
  56208. * x: 1,
  56209. * open: 9,
  56210. * high: 2,
  56211. * low: 4,
  56212. * close: 6,
  56213. * name: "Point2",
  56214. * color: "#00FF00"
  56215. * }, {
  56216. * x: 1,
  56217. * open: 1,
  56218. * high: 4,
  56219. * low: 7,
  56220. * close: 7,
  56221. * name: "Point1",
  56222. * color: "#FF00FF"
  56223. * }]
  56224. * ```
  56225. *
  56226. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  56227. * @extends series.ohlc.data
  56228. * @excluding y
  56229. * @product highstock
  56230. * @apioption series.candlestick.data
  56231. */
  56232. ''; // adds doclets above to transpilat
  56233. /* *
  56234. *
  56235. * Default Export
  56236. *
  56237. * */
  56238. return CandlestickSeriesDefaults;
  56239. });
  56240. _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) {
  56241. /* *
  56242. *
  56243. * (c) 2010-2021 Torstein Honsi
  56244. *
  56245. * License: www.highcharts.com/license
  56246. *
  56247. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56248. *
  56249. * */
  56250. const { defaultOptions } = D;
  56251. const { column: ColumnSeries, ohlc: OHLCSeries } = SeriesRegistry.seriesTypes;
  56252. const { merge } = U;
  56253. /* *
  56254. *
  56255. * Class
  56256. *
  56257. * */
  56258. /**
  56259. * The candlestick series type.
  56260. *
  56261. * @private
  56262. * @class
  56263. * @name Highcharts.seriesTypes.candlestick
  56264. *
  56265. * @augments Highcharts.seriesTypes.ohlc
  56266. */
  56267. class CandlestickSeries extends OHLCSeries {
  56268. constructor() {
  56269. /* *
  56270. *
  56271. * Static Properties
  56272. *
  56273. * */
  56274. super(...arguments);
  56275. /* *
  56276. *
  56277. * Properties
  56278. *
  56279. * */
  56280. this.data = void 0;
  56281. this.options = void 0;
  56282. this.points = void 0;
  56283. }
  56284. /* *
  56285. *
  56286. * Functions
  56287. *
  56288. * */
  56289. /**
  56290. * Postprocess mapping between options and SVG attributes
  56291. *
  56292. * @private
  56293. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  56294. */
  56295. pointAttribs(point, state) {
  56296. 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)
  56297. attribs['stroke-width'] = options.lineWidth;
  56298. attribs.fill = point.options.color ||
  56299. (isUp ? (options.upColor || color) : color);
  56300. attribs.stroke = point.options.lineColor ||
  56301. (isUp ? (options.upLineColor || stroke) : stroke);
  56302. // Select or hover states
  56303. if (state) {
  56304. const stateOptions = options.states[state];
  56305. attribs.fill = stateOptions.color || attribs.fill;
  56306. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  56307. attribs['stroke-width'] =
  56308. stateOptions.lineWidth || attribs['stroke-width'];
  56309. }
  56310. return attribs;
  56311. }
  56312. /**
  56313. * Draw the data points.
  56314. *
  56315. * @private
  56316. * @function Highcharts.seriesTypes.candlestick#drawPoints
  56317. */
  56318. drawPoints() {
  56319. const series = this, points = series.points, chart = series.chart, reversedYAxis = series.yAxis.reversed;
  56320. for (const point of points) {
  56321. let graphic = point.graphic, plotOpen, plotClose, topBox, bottomBox, hasTopWhisker, hasBottomWhisker, crispCorr, crispX, path, halfWidth;
  56322. const isNew = !graphic;
  56323. if (typeof point.plotY !== 'undefined') {
  56324. if (!graphic) {
  56325. point.graphic = graphic = chart.renderer.path()
  56326. .add(series.group);
  56327. }
  56328. if (!series.chart.styledMode) {
  56329. graphic
  56330. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  56331. .shadow(series.options.shadow);
  56332. }
  56333. // Crisp vector coordinates
  56334. crispCorr = (graphic.strokeWidth() % 2) / 2;
  56335. // #2596:
  56336. crispX = Math.round(point.plotX) - crispCorr;
  56337. plotOpen = point.plotOpen;
  56338. plotClose = point.plotClose;
  56339. topBox = Math.min(plotOpen, plotClose);
  56340. bottomBox = Math.max(plotOpen, plotClose);
  56341. halfWidth = Math.round(point.shapeArgs.width / 2);
  56342. hasTopWhisker = reversedYAxis ?
  56343. bottomBox !== point.yBottom :
  56344. Math.round(topBox) !==
  56345. Math.round(point.plotHigh);
  56346. hasBottomWhisker = reversedYAxis ?
  56347. Math.round(topBox) !==
  56348. Math.round(point.plotHigh) :
  56349. bottomBox !== point.yBottom;
  56350. topBox = Math.round(topBox) + crispCorr;
  56351. bottomBox = Math.round(bottomBox) + crispCorr;
  56352. // Create the path. Due to a bug in Chrome 49, the path is
  56353. // first instanciated with no values, then the values
  56354. // pushed. For unknown reasons, instanciating the path array
  56355. // with all the values would lead to a crash when updating
  56356. // frequently (#5193).
  56357. path = [];
  56358. path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
  56359. ['M', crispX, topBox], [
  56360. 'L',
  56361. // #460, #2094
  56362. crispX,
  56363. hasTopWhisker ?
  56364. Math.round(reversedYAxis ?
  56365. point.yBottom :
  56366. point.plotHigh) :
  56367. topBox
  56368. ], ['M', crispX, bottomBox], [
  56369. 'L',
  56370. // #460, #2094
  56371. crispX,
  56372. hasBottomWhisker ?
  56373. Math.round(reversedYAxis ?
  56374. point.plotHigh :
  56375. point.yBottom) :
  56376. bottomBox
  56377. ]);
  56378. graphic[isNew ? 'attr' : 'animate']({ d: path })
  56379. .addClass(point.getClassName(), true);
  56380. }
  56381. }
  56382. }
  56383. }
  56384. CandlestickSeries.defaultOptions = merge(OHLCSeries.defaultOptions, defaultOptions.plotOptions, { tooltip: OHLCSeries.defaultOptions.tooltip }, CandlestickSeriesDefaults);
  56385. SeriesRegistry.registerSeriesType('candlestick', CandlestickSeries);
  56386. /* *
  56387. *
  56388. * Default Export
  56389. *
  56390. * */
  56391. return CandlestickSeries;
  56392. });
  56393. _registerModule(_modules, 'Series/Flags/FlagsPoint.js', [_modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (SeriesRegistry, U) {
  56394. /* *
  56395. *
  56396. * (c) 2010-2021 Torstein Honsi
  56397. *
  56398. * License: www.highcharts.com/license
  56399. *
  56400. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56401. *
  56402. * */
  56403. const { column: { prototype: { pointClass: ColumnPoint } } } = SeriesRegistry.seriesTypes;
  56404. const { isNumber } = U;
  56405. /* *
  56406. *
  56407. * Class
  56408. *
  56409. * */
  56410. class FlagsPoint extends ColumnPoint {
  56411. constructor() {
  56412. /* *
  56413. *
  56414. * Properties
  56415. *
  56416. * */
  56417. super(...arguments);
  56418. this.options = void 0;
  56419. this.series = void 0;
  56420. this.ttBelow = false;
  56421. }
  56422. /* *
  56423. *
  56424. * Functions
  56425. *
  56426. * */
  56427. /**
  56428. * @private
  56429. */
  56430. isValid() {
  56431. // #9233 - Prevent from treating flags as null points (even if
  56432. // they have no y values defined).
  56433. return isNumber(this.y) || typeof this.y === 'undefined';
  56434. }
  56435. /**
  56436. * @private
  56437. */
  56438. hasNewShapeType() {
  56439. const shape = this.options.shape || this.series.options.shape;
  56440. return this.graphic && shape && shape !== this.graphic.symbolKey;
  56441. }
  56442. }
  56443. /* *
  56444. *
  56445. * Default Export
  56446. *
  56447. * */
  56448. return FlagsPoint;
  56449. });
  56450. _registerModule(_modules, 'Series/Flags/FlagsSeriesDefaults.js', [], function () {
  56451. /* *
  56452. *
  56453. * (c) 2010-2021 Torstein Honsi
  56454. *
  56455. * License: www.highcharts.com/license
  56456. *
  56457. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56458. *
  56459. * */
  56460. /* *
  56461. *
  56462. * API Options
  56463. *
  56464. * */
  56465. /**
  56466. * Flags are used to mark events in stock charts. They can be added on the
  56467. * timeline, or attached to a specific series.
  56468. *
  56469. * @sample stock/demo/flags-general/
  56470. * Flags on a line series
  56471. *
  56472. * @extends plotOptions.column
  56473. * @excluding animation, borderColor, borderRadius, borderWidth,
  56474. * colorByPoint, cropThreshold, dataGrouping, pointPadding,
  56475. * pointWidth, turboThreshold
  56476. * @product highstock
  56477. * @optionparent plotOptions.flags
  56478. */
  56479. const FlagsSeriesDefaults = {
  56480. /**
  56481. * In case the flag is placed on a series, on what point key to place
  56482. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  56483. * however, the flag can optionally be placed on the `open`, `high`,
  56484. * `low` or `close` key.
  56485. *
  56486. * @sample {highstock} stock/plotoptions/flags-onkey/
  56487. * Range series, flag on high
  56488. *
  56489. * @type {string}
  56490. * @default y
  56491. * @since 4.2.2
  56492. * @product highstock
  56493. * @validvalue ["y", "open", "high", "low", "close"]
  56494. * @apioption plotOptions.flags.onKey
  56495. */
  56496. /**
  56497. * The id of the series that the flags should be drawn on. If no id
  56498. * is given, the flags are drawn on the x axis.
  56499. *
  56500. * @sample {highstock} stock/plotoptions/flags/
  56501. * Flags on series and on x axis
  56502. *
  56503. * @type {string}
  56504. * @product highstock
  56505. * @apioption plotOptions.flags.onSeries
  56506. */
  56507. pointRange: 0,
  56508. /**
  56509. * Whether the flags are allowed to overlap sideways. If `false`, the
  56510. * flags are moved sideways using an algorithm that seeks to place every
  56511. * flag as close as possible to its original position.
  56512. *
  56513. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  56514. * Allow sideways overlap
  56515. *
  56516. * @since 6.0.4
  56517. */
  56518. allowOverlapX: false,
  56519. /**
  56520. * The shape of the marker. Can be one of "flag", "circlepin",
  56521. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  56522. * Individual shapes can also be set for each point.
  56523. *
  56524. * @sample {highstock} stock/plotoptions/flags/
  56525. * Different shapes
  56526. *
  56527. * @type {Highcharts.FlagsShapeValue}
  56528. * @product highstock
  56529. */
  56530. shape: 'flag',
  56531. /**
  56532. * When multiple flags in the same series fall on the same value, this
  56533. * number determines the vertical offset between them.
  56534. *
  56535. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  56536. * A greater stack distance
  56537. *
  56538. * @product highstock
  56539. */
  56540. stackDistance: 12,
  56541. /**
  56542. * Text alignment for the text inside the flag.
  56543. *
  56544. * @since 5.0.0
  56545. * @product highstock
  56546. * @validvalue ["left", "center", "right"]
  56547. */
  56548. textAlign: 'center',
  56549. /**
  56550. * Specific tooltip options for flag series. Flag series tooltips are
  56551. * different from most other types in that a flag doesn't have a data
  56552. * value, so the tooltip rather displays the `text` option for each
  56553. * point.
  56554. *
  56555. * @extends plotOptions.series.tooltip
  56556. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  56557. * @product highstock
  56558. */
  56559. tooltip: {
  56560. pointFormat: '{point.text}'
  56561. },
  56562. /**
  56563. * @type {number|null}
  56564. */
  56565. threshold: null,
  56566. /**
  56567. * The text to display on each flag. This can be defined on series
  56568. * level, or individually for each point. Defaults to `"A"`.
  56569. *
  56570. * @type {string}
  56571. * @default A
  56572. * @product highstock
  56573. * @apioption plotOptions.flags.title
  56574. */
  56575. /**
  56576. * The y position of the top left corner of the flag relative to either
  56577. * the series (if onSeries is defined), or the x axis. Defaults to
  56578. * `-30`.
  56579. *
  56580. * @product highstock
  56581. */
  56582. y: -30,
  56583. /**
  56584. * Whether to use HTML to render the flag texts. Using HTML allows for
  56585. * advanced formatting, images and reliable bi-directional text
  56586. * rendering. Note that exported images won't respect the HTML, and that
  56587. * HTML won't respect Z-index settings.
  56588. *
  56589. * @type {boolean}
  56590. * @default false
  56591. * @since 1.3
  56592. * @product highstock
  56593. * @apioption plotOptions.flags.useHTML
  56594. */
  56595. /**
  56596. * Fixed width of the flag's shape. By default, width is autocalculated
  56597. * according to the flag's title.
  56598. *
  56599. * @sample {highstock} stock/demo/flags-shapes/
  56600. * Flags with fixed width
  56601. *
  56602. * @type {number}
  56603. * @product highstock
  56604. * @apioption plotOptions.flags.width
  56605. */
  56606. /**
  56607. * Fixed height of the flag's shape. By default, height is
  56608. * autocalculated according to the flag's title.
  56609. *
  56610. * @type {number}
  56611. * @product highstock
  56612. * @apioption plotOptions.flags.height
  56613. */
  56614. /**
  56615. * The fill color for the flags.
  56616. *
  56617. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56618. * @product highstock
  56619. */
  56620. fillColor: "#ffffff" /* Palette.backgroundColor */,
  56621. /**
  56622. * The color of the line/border of the flag.
  56623. *
  56624. * In styled mode, the stroke is set in the
  56625. * `.highcharts-flag-series.highcharts-point` rule.
  56626. *
  56627. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56628. * @default #000000
  56629. * @product highstock
  56630. * @apioption plotOptions.flags.lineColor
  56631. */
  56632. /**
  56633. * The pixel width of the flag's line/border.
  56634. *
  56635. * @product highstock
  56636. */
  56637. lineWidth: 1,
  56638. states: {
  56639. /**
  56640. * @extends plotOptions.column.states.hover
  56641. * @product highstock
  56642. */
  56643. hover: {
  56644. /**
  56645. * The color of the line/border of the flag.
  56646. *
  56647. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56648. * @product highstock
  56649. */
  56650. lineColor: "#000000" /* Palette.neutralColor100 */,
  56651. /**
  56652. * The fill or background color of the flag.
  56653. *
  56654. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56655. * @product highstock
  56656. */
  56657. fillColor: "#ccd3ff" /* Palette.highlightColor20 */
  56658. }
  56659. },
  56660. /**
  56661. * The text styles of the flag.
  56662. *
  56663. * In styled mode, the styles are set in the
  56664. * `.highcharts-flag-series .highcharts-point` rule.
  56665. *
  56666. * @type {Highcharts.CSSObject}
  56667. * @default {"fontSize": "11px", "fontWeight": "bold"}
  56668. * @product highstock
  56669. */
  56670. style: {
  56671. /** @ignore-option */
  56672. fontSize: '0.7em',
  56673. /** @ignore-option */
  56674. fontWeight: 'bold'
  56675. }
  56676. };
  56677. /**
  56678. * A `flags` series. If the [type](#series.flags.type) option is not
  56679. * specified, it is inherited from [chart.type](#chart.type).
  56680. *
  56681. * @extends series,plotOptions.flags
  56682. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  56683. * connectNulls, cropThreshold, dashStyle, dataGrouping, dataParser,
  56684. * dataURL, gapSize, gapUnit, linecap, lineWidth, marker,
  56685. * pointPadding, pointWidth, step, turboThreshold, useOhlcData
  56686. * @product highstock
  56687. * @apioption series.flags
  56688. */
  56689. /**
  56690. * An array of data points for the series. For the `flags` series type,
  56691. * points can be given in the following ways:
  56692. *
  56693. * 1. An array of objects with named values. The following snippet shows only a
  56694. * few settings, see the complete options set below. If the total number of
  56695. * data points exceeds the series'
  56696. * [turboThreshold](#series.flags.turboThreshold), this option is not
  56697. * available.
  56698. * ```js
  56699. * data: [{
  56700. * x: 1,
  56701. * title: "A",
  56702. * text: "First event"
  56703. * }, {
  56704. * x: 1,
  56705. * title: "B",
  56706. * text: "Second event"
  56707. * }]
  56708. * ```
  56709. *
  56710. * @type {Array<*>}
  56711. * @extends series.line.data
  56712. * @excluding dataLabels, marker, name, y
  56713. * @product highstock
  56714. * @apioption series.flags.data
  56715. */
  56716. /**
  56717. * The fill color of an individual flag. By default it inherits from
  56718. * the series color.
  56719. *
  56720. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  56721. * @product highstock
  56722. * @apioption series.flags.data.fillColor
  56723. */
  56724. /**
  56725. * The longer text to be shown in the flag's tooltip.
  56726. *
  56727. * @type {string}
  56728. * @product highstock
  56729. * @apioption series.flags.data.text
  56730. */
  56731. /**
  56732. * The short text to be shown on the flag.
  56733. *
  56734. * @type {string}
  56735. * @product highstock
  56736. * @apioption series.flags.data.title
  56737. */
  56738. ''; // keeps doclets above in transpiled file
  56739. /* *
  56740. *
  56741. * Default Export
  56742. *
  56743. * */
  56744. return FlagsSeriesDefaults;
  56745. });
  56746. _registerModule(_modules, 'Series/Flags/FlagsSymbols.js', [_modules['Core/Renderer/RendererRegistry.js']], function (RendererRegistry) {
  56747. /* *
  56748. *
  56749. * Imports
  56750. *
  56751. * */
  56752. /* *
  56753. *
  56754. * Composition
  56755. *
  56756. * */
  56757. var FlagsSymbols;
  56758. (function (FlagsSymbols) {
  56759. /* *
  56760. *
  56761. * Constants
  56762. *
  56763. * */
  56764. const modifiedMembers = [];
  56765. /* *
  56766. *
  56767. * Functions
  56768. *
  56769. * */
  56770. /* eslint-disable valid-jsdoc */
  56771. /**
  56772. * @private
  56773. */
  56774. function compose(SVGRendererClass) {
  56775. if (modifiedMembers.indexOf(SVGRendererClass) === -1) {
  56776. modifiedMembers.push(SVGRendererClass);
  56777. const symbols = SVGRendererClass.prototype.symbols;
  56778. symbols.flag = flag;
  56779. createPinSymbol(symbols, 'circle');
  56780. createPinSymbol(symbols, 'square');
  56781. }
  56782. const RendererClass = RendererRegistry.getRendererType();
  56783. // The symbol callbacks are generated on the SVGRenderer object in all
  56784. // browsers.
  56785. if (modifiedMembers.indexOf(RendererClass)) {
  56786. modifiedMembers.push(RendererClass);
  56787. }
  56788. }
  56789. FlagsSymbols.compose = compose;
  56790. /**
  56791. * Create the flag icon with anchor.
  56792. * @private
  56793. */
  56794. function flag(x, y, w, h, options) {
  56795. const anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
  56796. // To do: unwanted any cast because symbols.circle has wrong type, it
  56797. // actually returns an SVGPathArray
  56798. const path = this.circle(anchorX - 1, anchorY - 1, 2, 2);
  56799. 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']);
  56800. return path;
  56801. }
  56802. /**
  56803. * Create the circlepin and squarepin icons with anchor.
  56804. * @private
  56805. */
  56806. function createPinSymbol(symbols, shape) {
  56807. symbols[(shape + 'pin')] = function (x, y, w, h, options) {
  56808. const anchorX = options && options.anchorX, anchorY = options && options.anchorY;
  56809. let path;
  56810. // For single-letter flags, make sure circular flags are not taller
  56811. // than their width
  56812. if (shape === 'circle' && h > w) {
  56813. x -= Math.round((h - w) / 2);
  56814. w = h;
  56815. }
  56816. path = (symbols[shape])(x, y, w, h);
  56817. if (anchorX && anchorY) {
  56818. /**
  56819. * If the label is below the anchor, draw the connecting line
  56820. * from the top edge of the label, otherwise start drawing from
  56821. * the bottom edge
  56822. */
  56823. let labelX = anchorX;
  56824. if (shape === 'circle') {
  56825. labelX = x + w / 2;
  56826. }
  56827. else {
  56828. const startSeg = path[0];
  56829. const endSeg = path[1];
  56830. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  56831. labelX = (startSeg[1] + endSeg[1]) / 2;
  56832. }
  56833. }
  56834. const labelY = (y > anchorY) ? y : y + h;
  56835. path.push([
  56836. 'M',
  56837. labelX,
  56838. labelY
  56839. ], [
  56840. 'L',
  56841. anchorX,
  56842. anchorY
  56843. ]);
  56844. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  56845. }
  56846. return path;
  56847. };
  56848. }
  56849. })(FlagsSymbols || (FlagsSymbols = {}));
  56850. /* *
  56851. *
  56852. * Default Export
  56853. *
  56854. * */
  56855. return FlagsSymbols;
  56856. });
  56857. _registerModule(_modules, 'Series/OnSeriesComposition.js', [_modules['Series/Column/ColumnSeries.js'], _modules['Core/Series/Series.js'], _modules['Core/Utilities.js']], function (ColumnSeries, Series, U) {
  56858. /* *
  56859. *
  56860. * (c) 2010-2021 Torstein Honsi
  56861. *
  56862. * License: www.highcharts.com/license
  56863. *
  56864. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  56865. *
  56866. * */
  56867. const { prototype: columnProto } = ColumnSeries;
  56868. const { prototype: seriesProto } = Series;
  56869. const { defined, stableSort } = U;
  56870. /* *
  56871. *
  56872. * Composition
  56873. *
  56874. * */
  56875. var OnSeriesComposition;
  56876. (function (OnSeriesComposition) {
  56877. /* *
  56878. *
  56879. * Declarations
  56880. *
  56881. * */
  56882. /* *
  56883. *
  56884. * Properties
  56885. *
  56886. * */
  56887. const composedMembers = [];
  56888. /* *
  56889. *
  56890. * Functions
  56891. *
  56892. * */
  56893. /* eslint-disable valid-jsdoc */
  56894. /**
  56895. * @private
  56896. */
  56897. function compose(SeriesClass) {
  56898. if (U.pushUnique(composedMembers, SeriesClass)) {
  56899. const seriesProto = SeriesClass.prototype;
  56900. seriesProto.getPlotBox = getPlotBox;
  56901. seriesProto.translate = translate;
  56902. }
  56903. return SeriesClass;
  56904. }
  56905. OnSeriesComposition.compose = compose;
  56906. /**
  56907. * Override getPlotBox. If the onSeries option is valid, return the plot box
  56908. * of the onSeries, otherwise proceed as usual.
  56909. *
  56910. * @private
  56911. */
  56912. function getPlotBox(name) {
  56913. return seriesProto.getPlotBox.call((this.options.onSeries &&
  56914. this.chart.get(this.options.onSeries)) || this, name);
  56915. }
  56916. OnSeriesComposition.getPlotBox = getPlotBox;
  56917. /**
  56918. * Extend the translate method by placing the point on the related series
  56919. *
  56920. * @private
  56921. */
  56922. function translate() {
  56923. columnProto.translate.apply(this);
  56924. const series = this, options = series.options, chart = series.chart, points = series.points, optionsOnSeries = options.onSeries, onSeries = (optionsOnSeries &&
  56925. chart.get(optionsOnSeries)), step = onSeries && onSeries.options.step, onData = (onSeries && onSeries.points), inverted = chart.inverted, xAxis = series.xAxis, yAxis = series.yAxis;
  56926. let cursor = points.length - 1, point, lastPoint, onKey = options.onKey || 'y', i = onData && onData.length, xOffset = 0, leftPoint, lastX, rightPoint, currentDataGrouping, distanceRatio;
  56927. // relate to a master series
  56928. if (onSeries && onSeries.visible && i) {
  56929. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  56930. currentDataGrouping = onSeries.currentDataGrouping;
  56931. lastX = (onData[i - 1].x +
  56932. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  56933. // sort the data points
  56934. stableSort(points, (a, b) => (a.x - b.x));
  56935. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  56936. while (i-- && points[cursor]) {
  56937. leftPoint = onData[i];
  56938. point = points[cursor];
  56939. point.y = leftPoint.y;
  56940. if (leftPoint.x <= point.x &&
  56941. typeof leftPoint[onKey] !== 'undefined') {
  56942. if (point.x <= lastX) { // #803
  56943. point.plotY = leftPoint[onKey];
  56944. // interpolate between points, #666
  56945. if (leftPoint.x < point.x &&
  56946. !step) {
  56947. rightPoint = onData[i + 1];
  56948. if (rightPoint &&
  56949. typeof rightPoint[onKey] !== 'undefined') {
  56950. // the distance ratio, between 0 and 1
  56951. distanceRatio =
  56952. (point.x - leftPoint.x) /
  56953. (rightPoint.x - leftPoint.x);
  56954. point.plotY +=
  56955. distanceRatio *
  56956. // the plotY distance
  56957. (rightPoint[onKey] - leftPoint[onKey]);
  56958. point.y +=
  56959. distanceRatio *
  56960. (rightPoint.y - leftPoint.y);
  56961. }
  56962. }
  56963. }
  56964. cursor--;
  56965. i++; // check again for points in the same x position
  56966. if (cursor < 0) {
  56967. break;
  56968. }
  56969. }
  56970. }
  56971. }
  56972. // Add plotY position and handle stacking
  56973. points.forEach((point, i) => {
  56974. let stackIndex;
  56975. point.plotX += xOffset; // #2049
  56976. // Undefined plotY means the point is either on axis, outside series
  56977. // range or hidden series. If the series is outside the range of the
  56978. // x axis it should fall through with an undefined plotY, but then
  56979. // we must remove the shapeArgs (#847). For inverted charts, we need
  56980. // to calculate position anyway, because series.invertGroups is not
  56981. // defined
  56982. if (typeof point.plotY === 'undefined' || inverted) {
  56983. if (point.plotX >= 0 &&
  56984. point.plotX <= xAxis.len) {
  56985. // We're inside xAxis range
  56986. if (inverted) {
  56987. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  56988. point.plotX = defined(point.y) ?
  56989. yAxis.translate(point.y, 0, 0, 0, 1) :
  56990. 0;
  56991. }
  56992. else {
  56993. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  56994. xAxis.offset; // For the windbarb demo
  56995. }
  56996. }
  56997. else {
  56998. point.shapeArgs = {}; // 847
  56999. }
  57000. }
  57001. // if multiple flags appear at the same x, order them into a stack
  57002. lastPoint = points[i - 1];
  57003. if (lastPoint && lastPoint.plotX === point.plotX) {
  57004. if (typeof lastPoint.stackIndex === 'undefined') {
  57005. lastPoint.stackIndex = 0;
  57006. }
  57007. stackIndex = lastPoint.stackIndex + 1;
  57008. }
  57009. point.stackIndex = stackIndex; // #3639
  57010. });
  57011. this.onSeries = onSeries;
  57012. }
  57013. OnSeriesComposition.translate = translate;
  57014. })(OnSeriesComposition || (OnSeriesComposition = {}));
  57015. /* *
  57016. *
  57017. * Default Export
  57018. *
  57019. * */
  57020. return OnSeriesComposition;
  57021. });
  57022. _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) {
  57023. /* *
  57024. *
  57025. * (c) 2010-2021 Torstein Honsi
  57026. *
  57027. * License: www.highcharts.com/license
  57028. *
  57029. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  57030. *
  57031. * */
  57032. const { noop } = H;
  57033. const { distribute } = R;
  57034. const { series: Series, seriesTypes: { column: ColumnSeries } } = SeriesRegistry;
  57035. const { addEvent, defined, extend, merge, objectEach, wrap } = U;
  57036. /* *
  57037. *
  57038. * Classes
  57039. *
  57040. * */
  57041. /**
  57042. * The Flags series.
  57043. *
  57044. * @private
  57045. * @class
  57046. * @name Highcharts.seriesTypes.flags
  57047. *
  57048. * @augments Highcharts.Series
  57049. */
  57050. class FlagsSeries extends ColumnSeries {
  57051. constructor() {
  57052. /* *
  57053. *
  57054. * Static Properties
  57055. *
  57056. * */
  57057. super(...arguments);
  57058. /* *
  57059. *
  57060. * Properties
  57061. *
  57062. * */
  57063. this.data = void 0;
  57064. this.options = void 0;
  57065. this.points = void 0;
  57066. }
  57067. /* *
  57068. *
  57069. * Functions
  57070. *
  57071. * */
  57072. /**
  57073. * Disable animation, but keep clipping (#8546).
  57074. * @private
  57075. */
  57076. animate(init) {
  57077. if (init) {
  57078. this.setClip();
  57079. }
  57080. }
  57081. /**
  57082. * Draw the markers.
  57083. * @private
  57084. */
  57085. drawPoints() {
  57086. 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 = [];
  57087. let plotX, plotY, shape, i, point, graphic, stackIndex, anchorY, attribs, outsideRight, centered;
  57088. i = points.length;
  57089. while (i--) {
  57090. point = points[i];
  57091. outsideRight =
  57092. (inverted ? point.plotY : point.plotX) >
  57093. series.xAxis.len;
  57094. plotX = point.plotX;
  57095. stackIndex = point.stackIndex;
  57096. shape = point.options.shape || options.shape;
  57097. plotY = point.plotY;
  57098. if (typeof plotY !== 'undefined') {
  57099. plotY = point.plotY + optionsY -
  57100. (typeof stackIndex !== 'undefined' &&
  57101. (stackIndex * options.stackDistance));
  57102. }
  57103. // skip connectors for higher level stacked points
  57104. point.anchorX = stackIndex ? void 0 : point.plotX;
  57105. anchorY = stackIndex ? void 0 : point.plotY;
  57106. centered = shape !== 'flag';
  57107. graphic = point.graphic;
  57108. // Only draw the point if y is defined and the flag is within
  57109. // the visible area
  57110. if (typeof plotY !== 'undefined' &&
  57111. plotX >= 0 &&
  57112. !outsideRight) {
  57113. // #15384
  57114. if (graphic && point.hasNewShapeType()) {
  57115. graphic = graphic.destroy();
  57116. }
  57117. // Create the flag
  57118. if (!graphic) {
  57119. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML)
  57120. .addClass('highcharts-point')
  57121. .add(series.markerGroup);
  57122. // Add reference to the point for tracker (#6303)
  57123. if (point.graphic.div) {
  57124. point.graphic.div.point = point;
  57125. }
  57126. graphic.isNew = true;
  57127. }
  57128. graphic.attr({
  57129. align: centered ? 'center' : 'left',
  57130. width: options.width,
  57131. height: options.height,
  57132. 'text-align': options.textAlign
  57133. });
  57134. if (!chart.styledMode) {
  57135. graphic
  57136. .attr(series.pointAttribs(point))
  57137. .css(merge(options.style, point.style))
  57138. .shadow(options.shadow);
  57139. }
  57140. if (plotX > 0) { // #3119
  57141. plotX -= graphic.strokeWidth() % 2; // #4285
  57142. }
  57143. // Plant the flag
  57144. attribs = {
  57145. y: plotY,
  57146. anchorY: anchorY
  57147. };
  57148. if (options.allowOverlapX) {
  57149. attribs.x = plotX;
  57150. attribs.anchorX = point.anchorX;
  57151. }
  57152. graphic.attr({
  57153. text: point.options.title || options.title || 'A'
  57154. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  57155. // Rig for the distribute function
  57156. if (!options.allowOverlapX) {
  57157. if (!boxesMap[point.plotX]) {
  57158. boxesMap[point.plotX] = {
  57159. align: centered ? 0.5 : 0,
  57160. size: graphic.width,
  57161. target: plotX,
  57162. anchorX: plotX
  57163. };
  57164. }
  57165. else {
  57166. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  57167. }
  57168. }
  57169. // Set the tooltip anchor position
  57170. point.tooltipPos = [
  57171. plotX,
  57172. plotY + yAxis.pos - chart.plotTop
  57173. ]; // #6327
  57174. }
  57175. else if (graphic) {
  57176. point.graphic = graphic.destroy();
  57177. }
  57178. }
  57179. // Handle X-dimension overlapping
  57180. if (!options.allowOverlapX) {
  57181. let maxDistance = 100;
  57182. objectEach(boxesMap, function (box) {
  57183. box.plotX = box.anchorX;
  57184. boxes.push(box);
  57185. maxDistance = Math.max(box.size, maxDistance);
  57186. });
  57187. // If necessary (for overlapping or long labels) distribute it
  57188. // depending on the label width or a hardcoded value, #16041.
  57189. distribute(boxes, inverted ? yAxis.len : this.xAxis.len, maxDistance);
  57190. for (const point of points) {
  57191. const plotX = point.plotX, graphic = point.graphic, box = graphic && boxesMap[plotX];
  57192. if (box && graphic) {
  57193. // Hide flag when its box position is not specified
  57194. // (#8573, #9299)
  57195. if (!defined(box.pos)) {
  57196. graphic.hide().isNew = true;
  57197. }
  57198. else {
  57199. graphic[graphic.isNew ? 'attr' : 'animate']({
  57200. x: box.pos + (box.align || 0) * box.size,
  57201. anchorX: point.anchorX
  57202. }).show().isNew = false;
  57203. }
  57204. }
  57205. }
  57206. }
  57207. // Can be a mix of SVG and HTML and we need events for both (#6303)
  57208. if (options.useHTML && series.markerGroup) {
  57209. wrap(series.markerGroup, 'on', function (proceed) {
  57210. return SVGElement.prototype.on.apply(
  57211. // for HTML
  57212. // eslint-disable-next-line no-invalid-this
  57213. proceed.apply(this, [].slice.call(arguments, 1)),
  57214. // and for SVG
  57215. [].slice.call(arguments, 1));
  57216. });
  57217. }
  57218. }
  57219. /**
  57220. * Extend the column trackers with listeners to expand and contract
  57221. * stacks.
  57222. * @private
  57223. */
  57224. drawTracker() {
  57225. const series = this, points = series.points;
  57226. super.drawTracker();
  57227. /* *
  57228. * Bring each stacked flag up on mouse over, this allows readability
  57229. * of vertically stacked elements as well as tight points on the x
  57230. * axis. #1924.
  57231. */
  57232. for (const point of points) {
  57233. const graphic = point.graphic;
  57234. if (graphic) {
  57235. if (point.unbindMouseOver) {
  57236. point.unbindMouseOver();
  57237. }
  57238. point.unbindMouseOver = addEvent(graphic.element, 'mouseover', function () {
  57239. // Raise this point
  57240. if (point.stackIndex > 0 &&
  57241. !point.raised) {
  57242. point._y = graphic.y;
  57243. graphic.attr({
  57244. y: point._y - 8
  57245. });
  57246. point.raised = true;
  57247. }
  57248. // Revert other raised points
  57249. for (const otherPoint of points) {
  57250. if (otherPoint !== point &&
  57251. otherPoint.raised &&
  57252. otherPoint.graphic) {
  57253. otherPoint.graphic.attr({
  57254. y: otherPoint._y
  57255. });
  57256. otherPoint.raised = false;
  57257. }
  57258. }
  57259. });
  57260. }
  57261. }
  57262. }
  57263. /**
  57264. * Get presentational attributes
  57265. * @private
  57266. */
  57267. pointAttribs(point, state) {
  57268. const options = this.options, color = (point && point.color) || this.color;
  57269. let lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
  57270. if (state) {
  57271. fill = options.states[state].fillColor;
  57272. lineColor = options.states[state].lineColor;
  57273. lineWidth = options.states[state].lineWidth;
  57274. }
  57275. return {
  57276. fill: fill || color,
  57277. stroke: lineColor || color,
  57278. 'stroke-width': lineWidth || options.lineWidth || 0
  57279. };
  57280. }
  57281. /**
  57282. * @private
  57283. */
  57284. setClip() {
  57285. Series.prototype.setClip.apply(this, arguments);
  57286. if (this.options.clip !== false &&
  57287. this.sharedClipKey &&
  57288. this.markerGroup) {
  57289. this.markerGroup.clip(this.chart.sharedClips[this.sharedClipKey]);
  57290. }
  57291. }
  57292. }
  57293. FlagsSeries.compose = FlagsSymbols.compose;
  57294. FlagsSeries.defaultOptions = merge(ColumnSeries.defaultOptions, FlagsSeriesDefaults);
  57295. OnSeriesComposition.compose(FlagsSeries);
  57296. extend(FlagsSeries.prototype, {
  57297. allowDG: false,
  57298. forceCrop: true,
  57299. invertible: false,
  57300. noSharedTooltip: true,
  57301. pointClass: FlagsPoint,
  57302. sorted: false,
  57303. takeOrdinalPosition: false,
  57304. trackerGroups: ['markerGroup'],
  57305. buildKDTree: noop,
  57306. /**
  57307. * Inherit the initialization from base Series.
  57308. * @private
  57309. */
  57310. init: Series.prototype.init
  57311. });
  57312. SeriesRegistry.registerSeriesType('flags', FlagsSeries);
  57313. /* *
  57314. *
  57315. * Default Export
  57316. *
  57317. * */
  57318. /* *
  57319. *
  57320. * API Declarations
  57321. *
  57322. * */
  57323. /**
  57324. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  57325. */
  57326. ''; // detach doclets above
  57327. return FlagsSeries;
  57328. });
  57329. _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) {
  57330. /* *
  57331. *
  57332. * (c) 2010-2021 Torstein Honsi
  57333. *
  57334. * License: www.highcharts.com/license
  57335. *
  57336. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  57337. *
  57338. * */
  57339. const { format } = F;
  57340. const { getOptions } = D;
  57341. const { addEvent, clamp, defined, extend, find, isNumber, isString, merge, pick, splat } = U;
  57342. /* *
  57343. *
  57344. * Class
  57345. *
  57346. * */
  57347. /**
  57348. * Stock-optimized chart. Use {@link Highcharts.Chart|Chart} for common charts.
  57349. *
  57350. * @requires modules/stock
  57351. *
  57352. * @class
  57353. * @name Highcharts.StockChart
  57354. * @extends Highcharts.Chart
  57355. */
  57356. class StockChart extends Chart {
  57357. /**
  57358. * Initializes the chart. The constructor's arguments are passed on
  57359. * directly.
  57360. *
  57361. * @function Highcharts.StockChart#init
  57362. *
  57363. * @param {Highcharts.Options} userOptions
  57364. * Custom options.
  57365. *
  57366. * @param {Function} [callback]
  57367. * Function to run when the chart has loaded and and all external
  57368. * images are loaded.
  57369. *
  57370. *
  57371. * @emits Highcharts.StockChart#event:init
  57372. * @emits Highcharts.StockChart#event:afterInit
  57373. */
  57374. init(userOptions, callback) {
  57375. const defaultOptions = getOptions(), xAxisOptions = userOptions.xAxis, yAxisOptions = userOptions.yAxis,
  57376. // Always disable startOnTick:true on the main axis when the
  57377. // navigator is enabled (#1090)
  57378. navigatorEnabled = pick(userOptions.navigator && userOptions.navigator.enabled, NavigatorDefaults.enabled, true);
  57379. // Avoid doing these twice
  57380. userOptions.xAxis = userOptions.yAxis = void 0;
  57381. const options = merge({
  57382. chart: {
  57383. panning: {
  57384. enabled: true,
  57385. type: 'x'
  57386. },
  57387. zooming: {
  57388. pinchType: 'x'
  57389. }
  57390. },
  57391. navigator: {
  57392. enabled: navigatorEnabled
  57393. },
  57394. scrollbar: {
  57395. // #4988 - check if setOptions was called
  57396. enabled: pick(ScrollbarDefaults.enabled, true)
  57397. },
  57398. rangeSelector: {
  57399. // #4988 - check if setOptions was called
  57400. enabled: pick(RangeSelectorDefaults.rangeSelector.enabled, true)
  57401. },
  57402. title: {
  57403. text: null
  57404. },
  57405. tooltip: {
  57406. split: pick(defaultOptions.tooltip.split, true),
  57407. crosshairs: true
  57408. },
  57409. legend: {
  57410. enabled: false
  57411. }
  57412. }, userOptions, // user's options
  57413. {
  57414. isStock: true // internal flag
  57415. });
  57416. userOptions.xAxis = xAxisOptions;
  57417. userOptions.yAxis = yAxisOptions;
  57418. // apply X axis options to both single and multi y axes
  57419. options.xAxis = splat(userOptions.xAxis || {}).map(function (xAxisOptions, i) {
  57420. return merge(getDefaultAxisOptions('xAxis', xAxisOptions), defaultOptions.xAxis, // #3802
  57421. // #7690
  57422. defaultOptions.xAxis && defaultOptions.xAxis[i], xAxisOptions, // user options
  57423. getForcedAxisOptions('xAxis', userOptions));
  57424. });
  57425. // apply Y axis options to both single and multi y axes
  57426. options.yAxis = splat(userOptions.yAxis || {}).map(function (yAxisOptions, i) {
  57427. return merge(getDefaultAxisOptions('yAxis', yAxisOptions), defaultOptions.yAxis, // #3802
  57428. // #7690
  57429. defaultOptions.yAxis && defaultOptions.yAxis[i], yAxisOptions // user options
  57430. );
  57431. });
  57432. super.init(options, callback);
  57433. }
  57434. /**
  57435. * Factory for creating different axis types.
  57436. * Extended to add stock defaults.
  57437. *
  57438. * @private
  57439. * @function Highcharts.StockChart#createAxis
  57440. * @param {string} coll
  57441. * An axis type.
  57442. * @param {Chart.CreateAxisOptionsObject} options
  57443. * The axis creation options.
  57444. */
  57445. createAxis(coll, options) {
  57446. options.axis = merge(getDefaultAxisOptions(coll, options.axis), options.axis, getForcedAxisOptions(coll, this.userOptions));
  57447. return super.createAxis(coll, options);
  57448. }
  57449. }
  57450. /* eslint-disable no-invalid-this, valid-jsdoc */
  57451. (function (StockChart) {
  57452. /**
  57453. * Factory function for creating new stock charts. Creates a new
  57454. * {@link Highcharts.StockChart|StockChart} object with different default
  57455. * options than the basic Chart.
  57456. *
  57457. * @example
  57458. * let chart = Highcharts.stockChart('container', {
  57459. * series: [{
  57460. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  57461. * pointInterval: 24 * 60 * 60 * 1000
  57462. * }]
  57463. * });
  57464. *
  57465. * @function Highcharts.stockChart
  57466. *
  57467. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  57468. * The DOM element to render to, or its id.
  57469. *
  57470. * @param {Highcharts.Options} options
  57471. * The chart options structure as described in the
  57472. * [options reference](https://api.highcharts.com/highstock).
  57473. *
  57474. * @param {Highcharts.ChartCallbackFunction} [callback]
  57475. * A function to execute when the chart object is finished
  57476. * rendering and all external image files (`chart.backgroundImage`,
  57477. * `chart.plotBackgroundImage` etc) are loaded. Defining a
  57478. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  57479. * handler is equivalent.
  57480. *
  57481. * @return {Highcharts.StockChart}
  57482. * The chart object.
  57483. */
  57484. function stockChart(a, b, c) {
  57485. return new StockChart(a, b, c);
  57486. }
  57487. StockChart.stockChart = stockChart;
  57488. })(StockChart || (StockChart = {}));
  57489. /**
  57490. * Get stock-specific default axis options.
  57491. *
  57492. * @private
  57493. * @function getDefaultAxisOptions
  57494. */
  57495. function getDefaultAxisOptions(type, options) {
  57496. if (type === 'xAxis') {
  57497. return {
  57498. minPadding: 0,
  57499. maxPadding: 0,
  57500. overscroll: 0,
  57501. ordinal: true,
  57502. title: {
  57503. text: null
  57504. },
  57505. labels: {
  57506. overflow: 'justify'
  57507. },
  57508. showLastLabel: true
  57509. };
  57510. }
  57511. if (type === 'yAxis') {
  57512. return {
  57513. labels: {
  57514. y: -2
  57515. },
  57516. opposite: pick(options.opposite, true),
  57517. showLastLabel: !!(
  57518. // #6104, show last label by default for category axes
  57519. options.categories ||
  57520. options.type === 'category'),
  57521. title: {
  57522. text: null
  57523. }
  57524. };
  57525. }
  57526. return {};
  57527. }
  57528. /**
  57529. * Get stock-specific forced axis options.
  57530. *
  57531. * @private
  57532. * @function getForcedAxisOptions
  57533. */
  57534. function getForcedAxisOptions(type, chartOptions) {
  57535. if (type === 'xAxis') {
  57536. // Always disable startOnTick:true on the main axis when the navigator
  57537. // is enabled (#1090)
  57538. const navigatorEnabled = pick(chartOptions.navigator && chartOptions.navigator.enabled, NavigatorDefaults.enabled, true);
  57539. const axisOptions = {
  57540. type: 'datetime',
  57541. categories: void 0
  57542. };
  57543. if (navigatorEnabled) {
  57544. axisOptions.startOnTick = false;
  57545. axisOptions.endOnTick = false;
  57546. }
  57547. return axisOptions;
  57548. }
  57549. return {};
  57550. }
  57551. /* *
  57552. *
  57553. * Compositions
  57554. *
  57555. * */
  57556. // Handle som Stock-specific series defaults, override the plotOptions before
  57557. // series options are handled.
  57558. addEvent(Series, 'setOptions', function (e) {
  57559. let overrides;
  57560. if (this.chart.options.isStock) {
  57561. if (this.is('column') || this.is('columnrange')) {
  57562. overrides = {
  57563. borderWidth: 0,
  57564. shadow: false
  57565. };
  57566. }
  57567. else if (!this.is('scatter') && !this.is('sma')) {
  57568. overrides = {
  57569. marker: {
  57570. enabled: false,
  57571. radius: 2
  57572. }
  57573. };
  57574. }
  57575. if (overrides) {
  57576. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  57577. }
  57578. }
  57579. });
  57580. // Override the automatic label alignment so that the first Y axis' labels are
  57581. // drawn on top of the grid line, and subsequent axes are drawn outside
  57582. addEvent(Axis, 'autoLabelAlign', function (e) {
  57583. const { chart, options } = this, panes = chart._labelPanes = chart._labelPanes || {}, labelOptions = options.labels;
  57584. if (chart.options.isStock && this.coll === 'yAxis') {
  57585. const key = options.top + ',' + options.height;
  57586. // Do it only for the first Y axis of each pane
  57587. if (!panes[key] && labelOptions.enabled) {
  57588. if (labelOptions.distance === 15 && // default
  57589. this.side === 1) {
  57590. labelOptions.distance = 0;
  57591. }
  57592. if (typeof labelOptions.align === 'undefined') {
  57593. labelOptions.align = 'right';
  57594. }
  57595. panes[key] = this;
  57596. e.align = 'right';
  57597. e.preventDefault();
  57598. }
  57599. }
  57600. });
  57601. // Clear axis from label panes (#6071)
  57602. addEvent(Axis, 'destroy', function () {
  57603. const chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  57604. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  57605. delete chart._labelPanes[key];
  57606. }
  57607. });
  57608. // Override getPlotLinePath to allow for multipane charts
  57609. addEvent(Axis, 'getPlotLinePath', function (e) {
  57610. let axis = this, series = (this.isLinked && !this.series ?
  57611. this.linkedParent.series :
  57612. 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
  57613. axes2, uniqueAxes, translatedValue = e.translatedValue, value = e.value, force = e.force, transVal;
  57614. /**
  57615. * Return the other axis based on either the axis option or on related
  57616. * series.
  57617. * @private
  57618. */
  57619. function getAxis(coll) {
  57620. const otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis', opt = axis.options[otherColl];
  57621. // Other axis indexed by number
  57622. if (isNumber(opt)) {
  57623. return [chart[otherColl][opt]];
  57624. }
  57625. // Other axis indexed by id (like navigator)
  57626. if (isString(opt)) {
  57627. return [chart.get(opt)];
  57628. }
  57629. // Auto detect based on existing series
  57630. return series.map(function (s) {
  57631. return s[otherColl];
  57632. });
  57633. }
  57634. if ( // For stock chart, by default render paths across the panes
  57635. // except the case when `acrossPanes` is disabled by user (#6644)
  57636. (chart.options.isStock && e.acrossPanes !== false) &&
  57637. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  57638. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  57639. e.preventDefault();
  57640. // Get the related axes based on series
  57641. axes = getAxis(axis.coll);
  57642. // Get the related axes based options.*Axis setting #2810
  57643. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  57644. axes2.forEach(function (A) {
  57645. if (defined(A.options.id) ?
  57646. A.options.id.indexOf('navigator') === -1 :
  57647. true) {
  57648. const a = (A.isXAxis ? 'yAxis' : 'xAxis'), rax = (defined(A.options[a]) ?
  57649. chart[a][A.options[a]] :
  57650. chart[a][0]);
  57651. if (axis === rax) {
  57652. axes.push(A);
  57653. }
  57654. }
  57655. });
  57656. // Remove duplicates in the axes array. If there are no axes in the axes
  57657. // array, we are adding an axis without data, so we need to populate
  57658. // this with grid lines (#2796).
  57659. uniqueAxes = axes.length ?
  57660. [] :
  57661. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  57662. axes.forEach(function (axis2) {
  57663. if (uniqueAxes.indexOf(axis2) === -1 &&
  57664. // Do not draw on axis which overlap completely. #5424
  57665. !find(uniqueAxes, function (unique) {
  57666. return unique.pos === axis2.pos && unique.len === axis2.len;
  57667. })) {
  57668. uniqueAxes.push(axis2);
  57669. }
  57670. });
  57671. transVal = pick(translatedValue, axis.translate(value, void 0, void 0, e.old));
  57672. if (isNumber(transVal)) {
  57673. if (axis.horiz) {
  57674. uniqueAxes.forEach(function (axis2) {
  57675. let skip;
  57676. y1 = axis2.pos;
  57677. y2 = y1 + axis2.len;
  57678. x1 = x2 = Math.round(transVal + axis.transB);
  57679. // outside plot area
  57680. if (force !== 'pass' &&
  57681. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  57682. if (force) {
  57683. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  57684. }
  57685. else {
  57686. skip = true;
  57687. }
  57688. }
  57689. if (!skip) {
  57690. result.push(['M', x1, y1], ['L', x2, y2]);
  57691. }
  57692. });
  57693. }
  57694. else {
  57695. uniqueAxes.forEach(function (axis2) {
  57696. let skip;
  57697. x1 = axis2.pos;
  57698. x2 = x1 + axis2.len;
  57699. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  57700. // outside plot area
  57701. if (force !== 'pass' &&
  57702. (y1 < axisTop || y1 > axisTop + axis.height)) {
  57703. if (force) {
  57704. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  57705. }
  57706. else {
  57707. skip = true;
  57708. }
  57709. }
  57710. if (!skip) {
  57711. result.push(['M', x1, y1], ['L', x2, y2]);
  57712. }
  57713. });
  57714. }
  57715. }
  57716. e.path = result.length > 0 ?
  57717. renderer.crispPolyLine(result, e.lineWidth || 1) :
  57718. // #3557 getPlotLinePath in regular Highcharts also returns null
  57719. null;
  57720. }
  57721. });
  57722. /**
  57723. * Function to crisp a line with multiple segments
  57724. *
  57725. * @private
  57726. * @function Highcharts.SVGRenderer#crispPolyLine
  57727. */
  57728. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  57729. // points format: [['M', 0, 0], ['L', 100, 0]]
  57730. // normalize to a crisp line
  57731. for (let i = 0; i < points.length; i = i + 2) {
  57732. const start = points[i], end = points[i + 1];
  57733. if (start[1] === end[1]) {
  57734. // Substract due to #1129. Now bottom and left axis gridlines behave
  57735. // the same.
  57736. start[1] = end[1] =
  57737. Math.round(start[1]) - (width % 2 / 2);
  57738. }
  57739. if (start[2] === end[2]) {
  57740. start[2] = end[2] =
  57741. Math.round(start[2]) + (width % 2 / 2);
  57742. }
  57743. }
  57744. return points;
  57745. };
  57746. // Wrapper to hide the label
  57747. addEvent(Axis, 'afterHideCrosshair', function () {
  57748. if (this.crossLabel) {
  57749. this.crossLabel = this.crossLabel.hide();
  57750. }
  57751. });
  57752. // Extend crosshairs to also draw the label
  57753. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  57754. // Check if the label has to be drawn
  57755. if (!this.crosshair ||
  57756. !this.crosshair.label ||
  57757. !this.crosshair.label.enabled ||
  57758. !this.cross ||
  57759. !isNumber(this.min) ||
  57760. !isNumber(this.max)) {
  57761. return;
  57762. }
  57763. let chart = this.chart, log = this.logarithmic, options = this.crosshair.label, // the label's options
  57764. horiz = this.horiz, // axis orientation
  57765. opposite = this.opposite, // axis position
  57766. left = this.left, // left position
  57767. top = this.top, // top position
  57768. width = this.width, crossLabel = this.crossLabel, // the svgElement
  57769. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, offset = 0,
  57770. // Use last available event (#5287)
  57771. e = event.e || (this.cross && this.cross.e), point = event.point, min = this.min, max = this.max;
  57772. if (log) {
  57773. min = log.lin2log(min);
  57774. max = log.lin2log(max);
  57775. }
  57776. align = (horiz ? 'center' : opposite ?
  57777. (this.labelAlign === 'right' ? 'right' : 'left') :
  57778. (this.labelAlign === 'left' ? 'left' : 'center'));
  57779. // If the label does not exist yet, create it.
  57780. if (!crossLabel) {
  57781. crossLabel = this.crossLabel = chart.renderer
  57782. .label('', 0, void 0, options.shape || 'callout')
  57783. .addClass('highcharts-crosshair-label highcharts-color-' + (point && point.series ?
  57784. point.series.colorIndex :
  57785. this.series[0] && this.series[0].colorIndex))
  57786. .attr({
  57787. align: options.align || align,
  57788. padding: pick(options.padding, 8),
  57789. r: pick(options.borderRadius, 3),
  57790. zIndex: 2
  57791. })
  57792. .add(this.labelGroup);
  57793. // Presentational
  57794. if (!chart.styledMode) {
  57795. crossLabel
  57796. .attr({
  57797. fill: options.backgroundColor ||
  57798. point && point.series && point.series.color || // #14888
  57799. "#666666" /* Palette.neutralColor60 */,
  57800. stroke: options.borderColor || '',
  57801. 'stroke-width': options.borderWidth || 0
  57802. })
  57803. .css(extend({
  57804. color: "#ffffff" /* Palette.backgroundColor */,
  57805. fontWeight: 'normal',
  57806. fontSize: '0.7em',
  57807. textAlign: 'center'
  57808. }, options.style || {}));
  57809. }
  57810. }
  57811. if (horiz) {
  57812. posx = snap ? (point.plotX || 0) + left : e.chartX;
  57813. posy = top + (opposite ? 0 : this.height);
  57814. }
  57815. else {
  57816. posx = left + this.offset + (opposite ? width : 0);
  57817. posy = snap ? (point.plotY || 0) + top : e.chartY;
  57818. }
  57819. if (!formatOption && !options.formatter) {
  57820. if (this.dateTime) {
  57821. formatFormat = '%b %d, %Y';
  57822. }
  57823. formatOption =
  57824. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  57825. }
  57826. // Show the label
  57827. const value = snap ?
  57828. (this.isXAxis ? point.x : point.y) :
  57829. this.toValue(horiz ? e.chartX : e.chartY);
  57830. // Crosshair should be rendered within Axis range (#7219). Also, the point
  57831. // of currentPriceIndicator should be inside the plot area, #14879.
  57832. const isInside = point && point.series ?
  57833. point.series.isPointInside(point) :
  57834. (isNumber(value) && value > min && value < max);
  57835. let text = '';
  57836. if (formatOption) {
  57837. text = format(formatOption, { value }, chart);
  57838. }
  57839. else if (options.formatter && isNumber(value)) {
  57840. text = options.formatter.call(this, value);
  57841. }
  57842. crossLabel.attr({
  57843. text,
  57844. x: posx,
  57845. y: posy,
  57846. visibility: isInside ? 'inherit' : 'hidden'
  57847. });
  57848. crossBox = crossLabel.getBBox();
  57849. // now it is placed we can correct its position
  57850. if (isNumber(crossLabel.x) && !horiz && !opposite) {
  57851. posx = crossLabel.x - (crossBox.width / 2);
  57852. }
  57853. if (isNumber(crossLabel.y)) {
  57854. if (horiz) {
  57855. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  57856. posy = crossLabel.y - crossBox.height;
  57857. }
  57858. }
  57859. else {
  57860. posy = crossLabel.y - (crossBox.height / 2);
  57861. }
  57862. }
  57863. // check the edges
  57864. if (horiz) {
  57865. limit = {
  57866. left: left - crossBox.x,
  57867. right: left + this.width - crossBox.x
  57868. };
  57869. }
  57870. else {
  57871. limit = {
  57872. left: this.labelAlign === 'left' ? left : 0,
  57873. right: this.labelAlign === 'right' ?
  57874. left + this.width :
  57875. chart.chartWidth
  57876. };
  57877. }
  57878. // left edge
  57879. if (crossLabel.translateX < limit.left) {
  57880. offset = limit.left - crossLabel.translateX;
  57881. }
  57882. // right edge
  57883. if (crossLabel.translateX + crossBox.width >= limit.right) {
  57884. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  57885. }
  57886. // show the crosslabel
  57887. crossLabel.attr({
  57888. x: posx + offset,
  57889. y: posy,
  57890. // First set x and y, then anchorX and anchorY, when box is actually
  57891. // calculated, #5702
  57892. anchorX: horiz ?
  57893. posx :
  57894. (this.opposite ? 0 : chart.chartWidth),
  57895. anchorY: horiz ?
  57896. (this.opposite ? chart.chartHeight : 0) :
  57897. posy + crossBox.height / 2
  57898. });
  57899. });
  57900. /**
  57901. * Based on the data grouping options decides whether
  57902. * the data should be cropped while processing.
  57903. *
  57904. * @ignore
  57905. * @function Highcharts.Series#forceCropping
  57906. */
  57907. Series.prototype.forceCropping = function () {
  57908. const chart = this.chart, options = this.options, dataGroupingOptions = options.dataGrouping, groupingEnabled = this.allowDG !== false && dataGroupingOptions &&
  57909. pick(dataGroupingOptions.enabled, chart.options.isStock);
  57910. return groupingEnabled;
  57911. };
  57912. addEvent(Chart, 'update', function (e) {
  57913. const options = e.options;
  57914. // Use case: enabling scrollbar from a disabled state.
  57915. // Scrollbar needs to be initialized from a controller, Navigator in this
  57916. // case (#6615)
  57917. if ('scrollbar' in options && this.navigator) {
  57918. merge(true, this.options.scrollbar, options.scrollbar);
  57919. this.navigator.update({});
  57920. delete options.scrollbar;
  57921. }
  57922. });
  57923. /* *
  57924. *
  57925. * Default Export
  57926. *
  57927. * */
  57928. return StockChart;
  57929. });
  57930. _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) {
  57931. const G = Highcharts;
  57932. // Classes
  57933. G.Navigator = Navigator;
  57934. G.RangeSelector = RangeSelector;
  57935. G.Scrollbar = Scrollbar;
  57936. G.StockChart = G.stockChart = StockChart.stockChart;
  57937. // Compositions
  57938. DataModifyComposition.compose(G.Series, G.Axis, G.Point);
  57939. FlagsSeries.compose(G.Renderer);
  57940. Navigator.compose(G.Axis, G.Chart, G.Series);
  57941. OHLCSeries.compose(G.Series);
  57942. OrdinalAxis.compose(G.Axis, G.Series, G.Chart);
  57943. RangeSelector.compose(G.Axis, G.Chart);
  57944. Scrollbar.compose(G.Axis);
  57945. });
  57946. _registerModule(_modules, 'masters/highstock.src.js', [_modules['masters/highcharts.src.js']], function (Highcharts) {
  57947. Highcharts.product = 'Highstock';
  57948. return Highcharts;
  57949. });
  57950. _modules['masters/highstock.src.js']._modules = _modules;
  57951. return _modules['masters/highstock.src.js'];
  57952. }));