highstock.src.js 2.3 MB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733447344473544736447374473844739447404474144742447434474444745447464474744748447494475044751447524475344754447554475644757447584475944760447614476244763447644476544766447674476844769447704477144772447734477444775447764477744778447794478044781447824478344784447854478644787447884478944790447914479244793447944479544796447974479844799448004480144802448034480444805448064480744808448094481044811448124481344814448154481644817448184481944820448214482244823448244482544826448274482844829448304483144832448334483444835448364483744838448394484044841448424484344844448454484644847448484484944850448514485244853448544485544856448574485844859448604486144862448634486444865448664486744868448694487044871448724487344874448754487644877448784487944880448814488244883448844488544886448874488844889448904489144892448934489444895448964489744898448994490044901449024490344904449054490644907449084490944910449114491244913449144491544916449174491844919449204492144922449234492444925449264492744928449294493044931449324493344934449354493644937449384493944940449414494244943449444494544946449474494844949449504495144952449534495444955449564495744958449594496044961449624496344964449654496644967449684496944970449714497244973449744497544976449774497844979449804498144982449834498444985449864498744988449894499044991449924499344994449954499644997449984499945000450014500245003450044500545006450074500845009450104501145012450134501445015450164501745018450194502045021450224502345024450254502645027450284502945030450314503245033450344503545036450374503845039450404504145042450434504445045450464504745048450494505045051450524505345054450554505645057450584505945060450614506245063450644506545066450674506845069450704507145072450734507445075450764507745078450794508045081450824508345084450854508645087450884508945090450914509245093450944509545096450974509845099451004510145102451034510445105451064510745108451094511045111451124511345114451154511645117451184511945120451214512245123451244512545126451274512845129451304513145132451334513445135451364513745138451394514045141451424514345144451454514645147451484514945150451514515245153451544515545156451574515845159451604516145162451634516445165451664516745168451694517045171451724517345174451754517645177451784517945180451814518245183451844518545186451874518845189451904519145192451934519445195451964519745198451994520045201452024520345204452054520645207452084520945210452114521245213452144521545216452174521845219452204522145222452234522445225452264522745228452294523045231452324523345234452354523645237452384523945240452414524245243452444524545246452474524845249452504525145252452534525445255452564525745258452594526045261452624526345264452654526645267452684526945270452714527245273452744527545276452774527845279452804528145282452834528445285452864528745288452894529045291452924529345294452954529645297452984529945300453014530245303453044530545306453074530845309453104531145312453134531445315453164531745318453194532045321453224532345324453254532645327453284532945330453314533245333453344533545336453374533845339453404534145342453434534445345453464534745348453494535045351453524535345354453554535645357453584535945360453614536245363453644536545366453674536845369453704537145372453734537445375453764537745378453794538045381453824538345384453854538645387453884538945390453914539245393453944539545396453974539845399454004540145402454034540445405454064540745408454094541045411454124541345414454154541645417454184541945420454214542245423454244542545426454274542845429454304543145432454334543445435454364543745438454394544045441454424544345444454454544645447454484544945450454514545245453454544545545456454574545845459454604546145462454634546445465454664546745468454694547045471454724547345474454754547645477454784547945480454814548245483454844548545486454874548845489454904549145492454934549445495454964549745498454994550045501455024550345504455054550645507455084550945510455114551245513455144551545516455174551845519455204552145522455234552445525455264552745528455294553045531455324553345534455354553645537455384553945540455414554245543455444554545546455474554845549455504555145552455534555445555455564555745558455594556045561455624556345564455654556645567455684556945570455714557245573455744557545576455774557845579455804558145582455834558445585455864558745588455894559045591455924559345594455954559645597455984559945600456014560245603456044560545606456074560845609456104561145612456134561445615456164561745618456194562045621456224562345624456254562645627456284562945630456314563245633456344563545636456374563845639456404564145642456434564445645456464564745648456494565045651456524565345654456554565645657456584565945660456614566245663456644566545666456674566845669456704567145672456734567445675456764567745678456794568045681456824568345684456854568645687456884568945690456914569245693456944569545696456974569845699457004570145702457034570445705457064570745708457094571045711457124571345714457154571645717457184571945720457214572245723457244572545726457274572845729457304573145732457334573445735457364573745738457394574045741457424574345744457454574645747457484574945750457514575245753457544575545756457574575845759457604576145762457634576445765457664576745768457694577045771457724577345774457754577645777457784577945780457814578245783457844578545786457874578845789457904579145792457934579445795457964579745798457994580045801458024580345804458054580645807458084580945810458114581245813458144581545816458174581845819458204582145822458234582445825458264582745828458294583045831458324583345834458354583645837458384583945840458414584245843458444584545846458474584845849458504585145852458534585445855458564585745858458594586045861458624586345864458654586645867458684586945870458714587245873458744587545876458774587845879458804588145882458834588445885458864588745888458894589045891458924589345894458954589645897458984589945900459014590245903459044590545906459074590845909459104591145912459134591445915459164591745918459194592045921459224592345924459254592645927459284592945930459314593245933459344593545936459374593845939459404594145942459434594445945459464594745948459494595045951459524595345954459554595645957459584595945960459614596245963459644596545966459674596845969459704597145972459734597445975459764597745978459794598045981459824598345984459854598645987459884598945990459914599245993459944599545996459974599845999460004600146002460034600446005460064600746008460094601046011460124601346014460154601646017460184601946020460214602246023460244602546026460274602846029460304603146032460334603446035460364603746038460394604046041460424604346044460454604646047460484604946050460514605246053460544605546056460574605846059460604606146062460634606446065460664606746068460694607046071460724607346074460754607646077460784607946080460814608246083460844608546086460874608846089460904609146092460934609446095460964609746098460994610046101461024610346104461054610646107461084610946110461114611246113461144611546116461174611846119461204612146122461234612446125461264612746128461294613046131461324613346134461354613646137461384613946140461414614246143461444614546146461474614846149461504615146152461534615446155461564615746158461594616046161461624616346164461654616646167461684616946170461714617246173461744617546176461774617846179461804618146182461834618446185461864618746188461894619046191461924619346194461954619646197461984619946200462014620246203462044620546206462074620846209462104621146212462134621446215462164621746218462194622046221462224622346224462254622646227462284622946230462314623246233462344623546236462374623846239462404624146242462434624446245462464624746248462494625046251462524625346254462554625646257462584625946260462614626246263462644626546266462674626846269462704627146272462734627446275462764627746278462794628046281462824628346284462854628646287462884628946290462914629246293462944629546296462974629846299463004630146302463034630446305463064630746308463094631046311463124631346314463154631646317463184631946320463214632246323463244632546326463274632846329463304633146332463334633446335463364633746338463394634046341463424634346344463454634646347463484634946350463514635246353463544635546356463574635846359463604636146362463634636446365463664636746368463694637046371463724637346374463754637646377463784637946380463814638246383463844638546386463874638846389463904639146392463934639446395463964639746398463994640046401464024640346404464054640646407464084640946410464114641246413464144641546416464174641846419464204642146422464234642446425464264642746428464294643046431464324643346434464354643646437464384643946440464414644246443464444644546446464474644846449464504645146452464534645446455464564645746458464594646046461464624646346464464654646646467464684646946470464714647246473464744647546476464774647846479464804648146482464834648446485464864648746488464894649046491464924649346494464954649646497464984649946500465014650246503465044650546506465074650846509465104651146512465134651446515465164651746518465194652046521465224652346524465254652646527465284652946530465314653246533465344653546536465374653846539465404654146542465434654446545465464654746548465494655046551465524655346554465554655646557465584655946560465614656246563465644656546566465674656846569465704657146572465734657446575465764657746578465794658046581465824658346584465854658646587465884658946590465914659246593465944659546596465974659846599466004660146602466034660446605466064660746608466094661046611466124661346614466154661646617466184661946620466214662246623466244662546626466274662846629466304663146632466334663446635466364663746638466394664046641466424664346644466454664646647466484664946650466514665246653466544665546656466574665846659466604666146662466634666446665466664666746668466694667046671466724667346674466754667646677466784667946680466814668246683466844668546686466874668846689466904669146692466934669446695466964669746698466994670046701467024670346704467054670646707467084670946710467114671246713467144671546716467174671846719467204672146722467234672446725467264672746728467294673046731467324673346734467354673646737467384673946740467414674246743467444674546746467474674846749467504675146752467534675446755467564675746758467594676046761467624676346764467654676646767467684676946770467714677246773467744677546776467774677846779467804678146782467834678446785467864678746788467894679046791467924679346794467954679646797467984679946800468014680246803468044680546806468074680846809468104681146812468134681446815468164681746818468194682046821468224682346824468254682646827468284682946830468314683246833468344683546836468374683846839468404684146842468434684446845468464684746848468494685046851468524685346854468554685646857468584685946860468614686246863468644686546866468674686846869468704687146872468734687446875468764687746878468794688046881468824688346884468854688646887468884688946890468914689246893468944689546896468974689846899469004690146902469034690446905469064690746908469094691046911469124691346914469154691646917469184691946920469214692246923469244692546926469274692846929469304693146932469334693446935469364693746938469394694046941469424694346944469454694646947469484694946950469514695246953469544695546956469574695846959469604696146962469634696446965469664696746968469694697046971469724697346974469754697646977469784697946980469814698246983469844698546986469874698846989469904699146992469934699446995469964699746998469994700047001470024700347004470054700647007470084700947010470114701247013470144701547016470174701847019470204702147022470234702447025470264702747028470294703047031470324703347034470354703647037470384703947040470414704247043470444704547046470474704847049470504705147052470534705447055470564705747058470594706047061470624706347064470654706647067470684706947070470714707247073470744707547076470774707847079470804708147082470834708447085470864708747088470894709047091470924709347094470954709647097470984709947100471014710247103471044710547106471074710847109471104711147112471134711447115471164711747118471194712047121471224712347124471254712647127471284712947130471314713247133471344713547136471374713847139471404714147142471434714447145471464714747148471494715047151471524715347154471554715647157471584715947160471614716247163471644716547166471674716847169471704717147172471734717447175471764717747178471794718047181471824718347184471854718647187471884718947190471914719247193471944719547196471974719847199472004720147202472034720447205472064720747208472094721047211472124721347214472154721647217472184721947220472214722247223472244722547226472274722847229472304723147232472334723447235472364723747238472394724047241472424724347244472454724647247472484724947250472514725247253472544725547256472574725847259472604726147262472634726447265472664726747268472694727047271472724727347274472754727647277472784727947280472814728247283472844728547286472874728847289472904729147292472934729447295472964729747298472994730047301473024730347304473054730647307473084730947310473114731247313473144731547316473174731847319473204732147322473234732447325473264732747328473294733047331473324733347334473354733647337473384733947340473414734247343473444734547346473474734847349473504735147352473534735447355473564735747358473594736047361473624736347364473654736647367473684736947370473714737247373473744737547376473774737847379473804738147382473834738447385473864738747388473894739047391473924739347394473954739647397473984739947400474014740247403474044740547406474074740847409474104741147412474134741447415474164741747418474194742047421474224742347424474254742647427474284742947430474314743247433474344743547436474374743847439474404744147442474434744447445474464744747448474494745047451474524745347454474554745647457474584745947460474614746247463474644746547466474674746847469474704747147472474734747447475474764747747478474794748047481474824748347484474854748647487474884748947490474914749247493474944749547496474974749847499475004750147502475034750447505475064750747508475094751047511475124751347514475154751647517475184751947520475214752247523475244752547526475274752847529475304753147532475334753447535475364753747538475394754047541475424754347544475454754647547475484754947550475514755247553475544755547556475574755847559475604756147562475634756447565475664756747568475694757047571475724757347574475754757647577475784757947580475814758247583475844758547586475874758847589475904759147592475934759447595475964759747598475994760047601476024760347604476054760647607476084760947610476114761247613476144761547616476174761847619476204762147622476234762447625476264762747628476294763047631476324763347634476354763647637476384763947640476414764247643476444764547646476474764847649476504765147652476534765447655476564765747658476594766047661476624766347664476654766647667476684766947670476714767247673476744767547676476774767847679476804768147682476834768447685476864768747688476894769047691476924769347694476954769647697476984769947700477014770247703477044770547706477074770847709477104771147712477134771447715477164771747718477194772047721477224772347724477254772647727477284772947730477314773247733477344773547736477374773847739477404774147742477434774447745477464774747748477494775047751477524775347754477554775647757477584775947760477614776247763477644776547766477674776847769477704777147772477734777447775477764777747778477794778047781477824778347784477854778647787477884778947790477914779247793477944779547796477974779847799478004780147802478034780447805478064780747808478094781047811478124781347814478154781647817478184781947820478214782247823478244782547826478274782847829478304783147832478334783447835478364783747838478394784047841478424784347844478454784647847478484784947850478514785247853478544785547856478574785847859478604786147862478634786447865478664786747868478694787047871478724787347874478754787647877478784787947880478814788247883478844788547886478874788847889478904789147892478934789447895478964789747898478994790047901479024790347904479054790647907479084790947910479114791247913479144791547916479174791847919479204792147922479234792447925479264792747928479294793047931479324793347934479354793647937479384793947940479414794247943479444794547946479474794847949479504795147952479534795447955479564795747958479594796047961479624796347964479654796647967479684796947970479714797247973479744797547976479774797847979479804798147982479834798447985479864798747988479894799047991479924799347994479954799647997479984799948000480014800248003480044800548006480074800848009480104801148012480134801448015480164801748018480194802048021480224802348024480254802648027480284802948030480314803248033480344803548036480374803848039480404804148042480434804448045480464804748048480494805048051480524805348054480554805648057480584805948060480614806248063480644806548066480674806848069480704807148072480734807448075480764807748078480794808048081480824808348084480854808648087480884808948090480914809248093480944809548096480974809848099481004810148102481034810448105481064810748108481094811048111481124811348114481154811648117481184811948120481214812248123481244812548126481274812848129481304813148132481334813448135481364813748138481394814048141481424814348144481454814648147481484814948150481514815248153481544815548156481574815848159481604816148162481634816448165481664816748168481694817048171481724817348174481754817648177481784817948180481814818248183481844818548186481874818848189481904819148192481934819448195481964819748198481994820048201482024820348204482054820648207482084820948210482114821248213482144821548216482174821848219482204822148222482234822448225482264822748228482294823048231482324823348234482354823648237482384823948240482414824248243482444824548246482474824848249482504825148252482534825448255482564825748258482594826048261482624826348264482654826648267482684826948270482714827248273482744827548276482774827848279482804828148282482834828448285482864828748288482894829048291482924829348294482954829648297482984829948300483014830248303483044830548306483074830848309483104831148312483134831448315483164831748318483194832048321483224832348324483254832648327483284832948330483314833248333483344833548336483374833848339483404834148342483434834448345483464834748348483494835048351483524835348354483554835648357483584835948360483614836248363483644836548366483674836848369483704837148372483734837448375483764837748378483794838048381483824838348384483854838648387483884838948390483914839248393483944839548396483974839848399484004840148402484034840448405484064840748408484094841048411484124841348414484154841648417484184841948420484214842248423484244842548426484274842848429484304843148432484334843448435484364843748438484394844048441484424844348444484454844648447484484844948450484514845248453484544845548456484574845848459484604846148462484634846448465484664846748468484694847048471484724847348474484754847648477484784847948480484814848248483484844848548486484874848848489484904849148492484934849448495484964849748498484994850048501485024850348504485054850648507485084850948510485114851248513485144851548516485174851848519485204852148522485234852448525485264852748528485294853048531485324853348534485354853648537485384853948540485414854248543485444854548546485474854848549485504855148552485534855448555485564855748558485594856048561485624856348564485654856648567485684856948570485714857248573485744857548576485774857848579485804858148582485834858448585485864858748588485894859048591485924859348594485954859648597485984859948600486014860248603486044860548606486074860848609486104861148612486134861448615486164861748618486194862048621486224862348624486254862648627486284862948630486314863248633486344863548636486374863848639486404864148642486434864448645486464864748648486494865048651486524865348654486554865648657486584865948660486614866248663486644866548666486674866848669486704867148672486734867448675486764867748678486794868048681486824868348684486854868648687486884868948690486914869248693486944869548696486974869848699487004870148702487034870448705487064870748708487094871048711487124871348714487154871648717487184871948720487214872248723487244872548726487274872848729487304873148732487334873448735487364873748738487394874048741487424874348744487454874648747487484874948750487514875248753487544875548756487574875848759487604876148762487634876448765487664876748768487694877048771487724877348774487754877648777487784877948780487814878248783487844878548786487874878848789487904879148792487934879448795487964879748798487994880048801488024880348804488054880648807488084880948810488114881248813488144881548816488174881848819488204882148822488234882448825488264882748828488294883048831488324883348834488354883648837488384883948840488414884248843488444884548846488474884848849488504885148852488534885448855488564885748858488594886048861488624886348864488654886648867488684886948870488714887248873488744887548876488774887848879488804888148882488834888448885488864888748888488894889048891488924889348894488954889648897488984889948900489014890248903489044890548906489074890848909489104891148912489134891448915489164891748918489194892048921489224892348924489254892648927489284892948930489314893248933489344893548936489374893848939489404894148942489434894448945489464894748948489494895048951489524895348954489554895648957489584895948960489614896248963489644896548966489674896848969489704897148972489734897448975489764897748978489794898048981489824898348984489854898648987489884898948990489914899248993489944899548996489974899848999490004900149002490034900449005490064900749008490094901049011490124901349014490154901649017490184901949020490214902249023490244902549026490274902849029490304903149032490334903449035490364903749038490394904049041490424904349044490454904649047490484904949050490514905249053490544905549056490574905849059490604906149062490634906449065490664906749068490694907049071490724907349074490754907649077490784907949080490814908249083490844908549086490874908849089490904909149092490934909449095490964909749098490994910049101491024910349104491054910649107491084910949110491114911249113491144911549116491174911849119491204912149122491234912449125491264912749128491294913049131491324913349134491354913649137491384913949140491414914249143491444914549146491474914849149491504915149152491534915449155491564915749158491594916049161491624916349164491654916649167491684916949170491714917249173491744917549176491774917849179491804918149182491834918449185491864918749188491894919049191491924919349194491954919649197491984919949200492014920249203492044920549206492074920849209492104921149212492134921449215492164921749218492194922049221492224922349224492254922649227492284922949230492314923249233492344923549236492374923849239492404924149242492434924449245492464924749248492494925049251492524925349254492554925649257492584925949260492614926249263492644926549266492674926849269492704927149272492734927449275492764927749278492794928049281492824928349284492854928649287492884928949290492914929249293492944929549296492974929849299493004930149302493034930449305493064930749308493094931049311493124931349314493154931649317493184931949320493214932249323493244932549326493274932849329493304933149332493334933449335493364933749338493394934049341493424934349344493454934649347493484934949350493514935249353493544935549356493574935849359493604936149362493634936449365493664936749368493694937049371493724937349374493754937649377493784937949380493814938249383493844938549386493874938849389493904939149392493934939449395493964939749398493994940049401494024940349404494054940649407494084940949410494114941249413494144941549416494174941849419494204942149422494234942449425494264942749428494294943049431494324943349434494354943649437494384943949440494414944249443494444944549446494474944849449494504945149452494534945449455494564945749458494594946049461494624946349464494654946649467494684946949470494714947249473494744947549476494774947849479494804948149482494834948449485494864948749488494894949049491494924949349494494954949649497494984949949500495014950249503495044950549506495074950849509495104951149512495134951449515495164951749518495194952049521495224952349524495254952649527495284952949530495314953249533495344953549536495374953849539495404954149542495434954449545495464954749548495494955049551495524955349554495554955649557495584955949560495614956249563495644956549566495674956849569495704957149572495734957449575495764957749578495794958049581495824958349584495854958649587495884958949590495914959249593495944959549596495974959849599496004960149602496034960449605496064960749608496094961049611496124961349614496154961649617496184961949620496214962249623496244962549626496274962849629496304963149632496334963449635496364963749638496394964049641496424964349644496454964649647496484964949650496514965249653496544965549656496574965849659496604966149662496634966449665496664966749668496694967049671496724967349674496754967649677496784967949680496814968249683496844968549686496874968849689496904969149692496934969449695496964969749698496994970049701497024970349704497054970649707497084970949710497114971249713497144971549716497174971849719497204972149722497234972449725497264972749728497294973049731497324973349734497354973649737497384973949740497414974249743497444974549746497474974849749497504975149752497534975449755497564975749758497594976049761497624976349764497654976649767497684976949770497714977249773497744977549776497774977849779497804978149782497834978449785497864978749788497894979049791497924979349794497954979649797497984979949800498014980249803498044980549806498074980849809498104981149812498134981449815498164981749818498194982049821498224982349824498254982649827498284982949830498314983249833498344983549836498374983849839498404984149842498434984449845498464984749848498494985049851498524985349854498554985649857498584985949860498614986249863498644986549866498674986849869498704987149872498734987449875498764987749878498794988049881498824988349884498854988649887498884988949890498914989249893498944989549896498974989849899499004990149902499034990449905499064990749908499094991049911499124991349914499154991649917499184991949920499214992249923499244992549926499274992849929499304993149932499334993449935499364993749938499394994049941499424994349944499454994649947499484994949950499514995249953499544995549956499574995849959499604996149962499634996449965499664996749968499694997049971499724997349974499754997649977499784997949980499814998249983499844998549986499874998849989499904999149992499934999449995499964999749998499995000050001500025000350004500055000650007500085000950010500115001250013500145001550016500175001850019500205002150022500235002450025500265002750028500295003050031500325003350034500355003650037500385003950040500415004250043500445004550046500475004850049500505005150052500535005450055500565005750058500595006050061500625006350064500655006650067500685006950070500715007250073500745007550076500775007850079500805008150082500835008450085500865008750088500895009050091500925009350094500955009650097500985009950100501015010250103501045010550106501075010850109501105011150112501135011450115501165011750118501195012050121501225012350124501255012650127501285012950130501315013250133501345013550136501375013850139501405014150142501435014450145501465014750148501495015050151501525015350154501555015650157501585015950160501615016250163501645016550166501675016850169501705017150172501735017450175501765017750178501795018050181501825018350184501855018650187501885018950190501915019250193501945019550196501975019850199502005020150202502035020450205502065020750208502095021050211502125021350214502155021650217502185021950220502215022250223502245022550226502275022850229502305023150232502335023450235502365023750238502395024050241502425024350244502455024650247502485024950250502515025250253502545025550256502575025850259502605026150262502635026450265502665026750268502695027050271502725027350274502755027650277502785027950280502815028250283502845028550286502875028850289502905029150292502935029450295502965029750298502995030050301503025030350304503055030650307503085030950310503115031250313503145031550316503175031850319503205032150322503235032450325503265032750328503295033050331503325033350334503355033650337503385033950340503415034250343503445034550346503475034850349503505035150352503535035450355503565035750358503595036050361503625036350364503655036650367503685036950370503715037250373503745037550376503775037850379503805038150382503835038450385503865038750388503895039050391503925039350394503955039650397503985039950400504015040250403504045040550406504075040850409504105041150412504135041450415504165041750418504195042050421504225042350424504255042650427504285042950430504315043250433504345043550436504375043850439504405044150442504435044450445504465044750448504495045050451504525045350454504555045650457504585045950460504615046250463504645046550466504675046850469504705047150472504735047450475504765047750478504795048050481504825048350484504855048650487504885048950490504915049250493504945049550496504975049850499505005050150502505035050450505505065050750508505095051050511505125051350514505155051650517505185051950520505215052250523505245052550526505275052850529505305053150532505335053450535505365053750538505395054050541505425054350544505455054650547505485054950550505515055250553505545055550556505575055850559505605056150562505635056450565505665056750568505695057050571505725057350574505755057650577505785057950580505815058250583505845058550586505875058850589505905059150592505935059450595505965059750598505995060050601506025060350604506055060650607506085060950610506115061250613506145061550616506175061850619506205062150622506235062450625506265062750628506295063050631506325063350634506355063650637506385063950640506415064250643506445064550646506475064850649506505065150652506535065450655506565065750658506595066050661506625066350664506655066650667506685066950670506715067250673506745067550676506775067850679506805068150682506835068450685506865068750688506895069050691506925069350694506955069650697506985069950700507015070250703507045070550706507075070850709507105071150712507135071450715507165071750718507195072050721507225072350724507255072650727507285072950730507315073250733507345073550736507375073850739507405074150742507435074450745507465074750748507495075050751507525075350754507555075650757507585075950760507615076250763507645076550766507675076850769507705077150772507735077450775507765077750778507795078050781507825078350784507855078650787507885078950790507915079250793507945079550796507975079850799508005080150802508035080450805508065080750808508095081050811508125081350814508155081650817508185081950820508215082250823508245082550826508275082850829508305083150832508335083450835508365083750838508395084050841508425084350844508455084650847508485084950850508515085250853508545085550856508575085850859508605086150862508635086450865508665086750868508695087050871508725087350874508755087650877508785087950880508815088250883508845088550886508875088850889508905089150892508935089450895508965089750898508995090050901509025090350904509055090650907509085090950910509115091250913509145091550916509175091850919509205092150922509235092450925509265092750928509295093050931509325093350934509355093650937509385093950940509415094250943509445094550946509475094850949509505095150952509535095450955509565095750958509595096050961509625096350964509655096650967509685096950970509715097250973509745097550976509775097850979509805098150982509835098450985509865098750988509895099050991509925099350994509955099650997509985099951000510015100251003510045100551006510075100851009510105101151012510135101451015510165101751018510195102051021510225102351024510255102651027510285102951030510315103251033510345103551036510375103851039510405104151042510435104451045510465104751048510495105051051510525105351054510555105651057510585105951060510615106251063510645106551066510675106851069510705107151072510735107451075510765107751078510795108051081510825108351084510855108651087510885108951090510915109251093510945109551096510975109851099511005110151102511035110451105511065110751108511095111051111511125111351114511155111651117511185111951120511215112251123511245112551126511275112851129511305113151132511335113451135511365113751138511395114051141511425114351144511455114651147511485114951150511515115251153511545115551156511575115851159511605116151162511635116451165511665116751168
  1. /**
  2. * @license Highstock JS v8.1.2 (2020-06-16)
  3. *
  4. * (c) 2009-2018 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. (function (root, factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. factory['default'] = factory;
  12. module.exports = root.document ?
  13. factory(root) :
  14. factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/highstock', function () {
  17. return factory(root);
  18. });
  19. } else {
  20. if (root.Highcharts) {
  21. root.Highcharts.error(16, true);
  22. }
  23. root.Highcharts = factory(root);
  24. }
  25. }(typeof window !== 'undefined' ? window : this, function (win) {
  26. var _modules = {};
  27. function _registerModule(obj, path, args, fn) {
  28. if (!obj.hasOwnProperty(path)) {
  29. obj[path] = fn.apply(null, args);
  30. }
  31. }
  32. _registerModule(_modules, 'parts/Globals.js', [], function () {
  33. /* *
  34. *
  35. * (c) 2010-2020 Torstein Honsi
  36. *
  37. * License: www.highcharts.com/license
  38. *
  39. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40. *
  41. * */
  42. /* globals Image, window */
  43. /**
  44. * Reference to the global SVGElement class as a workaround for a name conflict
  45. * in the Highcharts namespace.
  46. *
  47. * @global
  48. * @typedef {global.SVGElement} GlobalSVGElement
  49. *
  50. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  51. */
  52. // glob is a temporary fix to allow our es-modules to work.
  53. var glob = ( // @todo UMD variable named `window`, and glob named `win`
  54. typeof win !== 'undefined' ?
  55. win :
  56. typeof window !== 'undefined' ?
  57. window :
  58. {}), doc = glob.document, SVG_NS = 'http://www.w3.org/2000/svg', userAgent = (glob.navigator && glob.navigator.userAgent) || '', svg = (doc &&
  59. doc.createElementNS &&
  60. !!doc.createElementNS(SVG_NS, 'svg').createSVGRect), isMS = /(edge|msie|trident)/i.test(userAgent) && !glob.opera, isFirefox = userAgent.indexOf('Firefox') !== -1, isChrome = userAgent.indexOf('Chrome') !== -1, hasBidiBug = (isFirefox &&
  61. parseInt(userAgent.split('Firefox/')[1], 10) < 4 // issue #38
  62. );
  63. var H = {
  64. product: 'Highcharts',
  65. version: '8.1.2',
  66. deg2rad: Math.PI * 2 / 360,
  67. doc: doc,
  68. hasBidiBug: hasBidiBug,
  69. hasTouch: !!glob.TouchEvent,
  70. isMS: isMS,
  71. isWebKit: userAgent.indexOf('AppleWebKit') !== -1,
  72. isFirefox: isFirefox,
  73. isChrome: isChrome,
  74. isSafari: !isChrome && userAgent.indexOf('Safari') !== -1,
  75. isTouchDevice: /(Mobile|Android|Windows Phone)/.test(userAgent),
  76. SVG_NS: SVG_NS,
  77. chartCount: 0,
  78. seriesTypes: {},
  79. symbolSizes: {},
  80. svg: svg,
  81. win: glob,
  82. marginNames: ['plotTop', 'marginRight', 'marginBottom', 'plotLeft'],
  83. noop: function () { },
  84. /**
  85. * An array containing the current chart objects in the page. A chart's
  86. * position in the array is preserved throughout the page's lifetime. When
  87. * a chart is destroyed, the array item becomes `undefined`.
  88. *
  89. * @name Highcharts.charts
  90. * @type {Array<Highcharts.Chart|undefined>}
  91. */
  92. charts: [],
  93. /**
  94. * A hook for defining additional date format specifiers. New
  95. * specifiers are defined as key-value pairs by using the
  96. * specifier as key, and a function which takes the timestamp as
  97. * value. This function returns the formatted portion of the
  98. * date.
  99. *
  100. * @sample highcharts/global/dateformats/
  101. * Adding support for week number
  102. *
  103. * @name Highcharts.dateFormats
  104. * @type {Highcharts.Dictionary<Highcharts.TimeFormatCallbackFunction>}
  105. */
  106. dateFormats: {}
  107. };
  108. return H;
  109. });
  110. _registerModule(_modules, 'parts/Utilities.js', [_modules['parts/Globals.js']], function (H) {
  111. /* *
  112. *
  113. * (c) 2010-2020 Torstein Honsi
  114. *
  115. * License: www.highcharts.com/license
  116. *
  117. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  118. *
  119. * */
  120. /**
  121. * An animation configuration. Animation configurations can also be defined as
  122. * booleans, where `false` turns off animation and `true` defaults to a duration
  123. * of 500ms.
  124. *
  125. * @interface Highcharts.AnimationOptionsObject
  126. */ /**
  127. * A callback function to exectute when the animation finishes.
  128. * @name Highcharts.AnimationOptionsObject#complete
  129. * @type {Function|undefined}
  130. */ /**
  131. * The animation duration in milliseconds.
  132. * @name Highcharts.AnimationOptionsObject#duration
  133. * @type {number|undefined}
  134. */ /**
  135. * The name of an easing function as defined on the `Math` object.
  136. * @name Highcharts.AnimationOptionsObject#easing
  137. * @type {string|Function|undefined}
  138. */ /**
  139. * A callback function to execute on each step of each attribute or CSS property
  140. * that's being animated. The first argument contains information about the
  141. * animation and progress.
  142. * @name Highcharts.AnimationOptionsObject#step
  143. * @type {Function|undefined}
  144. */
  145. /**
  146. * Creates a frame for the animated SVG element.
  147. *
  148. * @callback Highcharts.AnimationStepCallbackFunction
  149. *
  150. * @param {Highcharts.SVGElement} this
  151. * The SVG element to animate.
  152. *
  153. * @return {void}
  154. */
  155. /**
  156. * Interface description for a class.
  157. *
  158. * @interface Highcharts.Class<T>
  159. * @extends Function
  160. */ /**
  161. * Class costructor.
  162. * @function Highcharts.Class<T>#new
  163. * @param {...Array<*>} args
  164. * Constructor arguments.
  165. * @return {T}
  166. * Class instance.
  167. */
  168. /**
  169. * A style object with camel case property names to define visual appearance of
  170. * a SVG element or HTML element. The properties can be whatever styles are
  171. * supported on the given SVG or HTML element.
  172. *
  173. * @example
  174. * {
  175. * fontFamily: 'monospace',
  176. * fontSize: '1.2em'
  177. * }
  178. *
  179. * @interface Highcharts.CSSObject
  180. */ /**
  181. * @name Highcharts.CSSObject#[key:string]
  182. * @type {boolean|number|string|undefined}
  183. */ /**
  184. * Background style for the element.
  185. * @name Highcharts.CSSObject#background
  186. * @type {string|undefined}
  187. */ /**
  188. * Background color of the element.
  189. * @name Highcharts.CSSObject#backgroundColor
  190. * @type {Highcharts.ColorString|undefined}
  191. */ /**
  192. * Border style for the element.
  193. * @name Highcharts.CSSObject#border
  194. * @type {string|undefined}
  195. */ /**
  196. * Radius of the element border.
  197. * @name Highcharts.CSSObject#borderRadius
  198. * @type {number|undefined}
  199. */ /**
  200. * Color used in the element. The 'contrast' option is a Highcharts custom
  201. * property that results in black or white, depending on the background of the
  202. * element.
  203. * @name Highcharts.CSSObject#color
  204. * @type {'contrast'|Highcharts.ColorString|undefined}
  205. */ /**
  206. * Style of the mouse cursor when resting over the element.
  207. * @name Highcharts.CSSObject#cursor
  208. * @type {Highcharts.CursorValue|undefined}
  209. */ /**
  210. * Font family of the element text. Multiple values have to be in decreasing
  211. * preference order and separated by comma.
  212. * @name Highcharts.CSSObject#fontFamily
  213. * @type {string|undefined}
  214. */ /**
  215. * Font size of the element text.
  216. * @name Highcharts.CSSObject#fontSize
  217. * @type {string|undefined}
  218. */ /**
  219. * Font weight of the element text.
  220. * @name Highcharts.CSSObject#fontWeight
  221. * @type {string|undefined}
  222. */ /**
  223. * Height of the element.
  224. * @name Highcharts.CSSObject#height
  225. * @type {number|undefined}
  226. */ /**
  227. * Width of the element border.
  228. * @name Highcharts.CSSObject#lineWidth
  229. * @type {number|undefined}
  230. */ /**
  231. * Opacity of the element.
  232. * @name Highcharts.CSSObject#opacity
  233. * @type {number|undefined}
  234. */ /**
  235. * Space around the element content.
  236. * @name Highcharts.CSSObject#padding
  237. * @type {string|undefined}
  238. */ /**
  239. * Behaviour of the element when the mouse cursor rests over it.
  240. * @name Highcharts.CSSObject#pointerEvents
  241. * @type {string|undefined}
  242. */ /**
  243. * Positioning of the element.
  244. * @name Highcharts.CSSObject#position
  245. * @type {string|undefined}
  246. */ /**
  247. * Alignment of the element text.
  248. * @name Highcharts.CSSObject#textAlign
  249. * @type {string|undefined}
  250. */ /**
  251. * Additional decoration of the element text.
  252. * @name Highcharts.CSSObject#textDecoration
  253. * @type {string|undefined}
  254. */ /**
  255. * Outline style of the element text.
  256. * @name Highcharts.CSSObject#textOutline
  257. * @type {string|undefined}
  258. */ /**
  259. * Line break style of the element text. Highcharts SVG elements support
  260. * `ellipsis` when a `width` is set.
  261. * @name Highcharts.CSSObject#textOverflow
  262. * @type {string|undefined}
  263. */ /**
  264. * Top spacing of the element relative to the parent element.
  265. * @name Highcharts.CSSObject#top
  266. * @type {string|undefined}
  267. */ /**
  268. * Animated transition of selected element properties.
  269. * @name Highcharts.CSSObject#transition
  270. * @type {string|undefined}
  271. */ /**
  272. * Line break style of the element text.
  273. * @name Highcharts.CSSObject#whiteSpace
  274. * @type {string|undefined}
  275. */ /**
  276. * Width of the element.
  277. * @name Highcharts.CSSObject#width
  278. * @type {number|undefined}
  279. */
  280. /**
  281. * All possible cursor styles.
  282. *
  283. * @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
  284. */
  285. /**
  286. * All possible dash styles.
  287. *
  288. * @typedef {'Dash'|'DashDot'|'Dot'|'LongDash'|'LongDashDot'|'LongDashDotDot'|'ShortDash'|'ShortDashDot'|'ShortDashDotDot'|'ShortDot'|'Solid'} Highcharts.DashStyleValue
  289. */
  290. /**
  291. * Generic dictionary in TypeScript notation.
  292. * Use the native `Record<string, any>` instead.
  293. *
  294. * @deprecated
  295. * @interface Highcharts.Dictionary<T>
  296. */ /**
  297. * @name Highcharts.Dictionary<T>#[key:string]
  298. * @type {T}
  299. */
  300. /**
  301. * The function callback to execute when the event is fired. The `this` context
  302. * contains the instance, that fired the event.
  303. *
  304. * @callback Highcharts.EventCallbackFunction<T>
  305. *
  306. * @param {T} this
  307. *
  308. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  309. * Event arguments.
  310. *
  311. * @return {boolean|void}
  312. */
  313. /**
  314. * The event options for adding function callback.
  315. *
  316. * @interface Highcharts.EventOptionsObject
  317. */ /**
  318. * The order the event handler should be called. This opens for having one
  319. * handler be called before another, independent of in which order they were
  320. * added.
  321. * @name Highcharts.EventOptionsObject#order
  322. * @type {number}
  323. */
  324. /**
  325. * Formats data as a string. Usually the data is accessible throught the `this`
  326. * keyword.
  327. *
  328. * @callback Highcharts.FormatterCallbackFunction<T>
  329. *
  330. * @param {T} this
  331. * Context to format
  332. *
  333. * @return {string}
  334. * Formatted text
  335. */
  336. /**
  337. * An object of key-value pairs for HTML attributes.
  338. *
  339. * @typedef {Highcharts.Dictionary<boolean|number|string|Function>} Highcharts.HTMLAttributes
  340. */
  341. /**
  342. * An HTML DOM element. The type is a reference to the regular HTMLElement in
  343. * the global scope.
  344. *
  345. * @typedef {global.HTMLElement} Highcharts.HTMLDOMElement
  346. *
  347. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
  348. */
  349. /**
  350. * The iterator callback.
  351. *
  352. * @callback Highcharts.ObjectEachCallbackFunction<T>
  353. *
  354. * @param {T} this
  355. * The context.
  356. *
  357. * @param {*} value
  358. * The property value.
  359. *
  360. * @param {string} key
  361. * The property key.
  362. *
  363. * @param {*} obj
  364. * The object that objectEach is being applied to.
  365. */
  366. /**
  367. * An object containing `left` and `top` properties for the position in the
  368. * page.
  369. *
  370. * @interface Highcharts.OffsetObject
  371. */ /**
  372. * Left distance to the page border.
  373. * @name Highcharts.OffsetObject#left
  374. * @type {number}
  375. */ /**
  376. * Top distance to the page border.
  377. * @name Highcharts.OffsetObject#top
  378. * @type {number}
  379. */
  380. /**
  381. * Describes a range.
  382. *
  383. * @interface Highcharts.RangeObject
  384. */ /**
  385. * Maximum number of the range.
  386. * @name Highcharts.RangeObject#max
  387. * @type {number}
  388. */ /**
  389. * Minimum number of the range.
  390. * @name Highcharts.RangeObject#min
  391. * @type {number}
  392. */
  393. /**
  394. * If a number is given, it defines the pixel length. If a percentage string is
  395. * given, like for example `'50%'`, the setting defines a length relative to a
  396. * base size, for example the size of a container.
  397. *
  398. * @typedef {number|string} Highcharts.RelativeSize
  399. */
  400. /**
  401. * Proceed function to call original (wrapped) function.
  402. *
  403. * @callback Highcharts.WrapProceedFunction
  404. *
  405. * @param {*} [arg1]
  406. * Optional argument. Without any arguments defaults to first argument of
  407. * the wrapping function.
  408. *
  409. * @param {*} [arg2]
  410. * Optional argument. Without any arguments defaults to second argument
  411. * of the wrapping function.
  412. *
  413. * @param {*} [arg3]
  414. * Optional argument. Without any arguments defaults to third argument of
  415. * the wrapping function.
  416. *
  417. * @return {*}
  418. * Return value of the original function.
  419. */
  420. /**
  421. * The Highcharts object is the placeholder for all other members, and various
  422. * utility functions. The most important member of the namespace would be the
  423. * chart constructor.
  424. *
  425. * @example
  426. * var chart = Highcharts.chart('container', { ... });
  427. *
  428. * @namespace Highcharts
  429. */
  430. H.timers = [];
  431. var charts = H.charts, doc = H.doc, win = H.win;
  432. /**
  433. * Provide error messages for debugging, with links to online explanation. This
  434. * function can be overridden to provide custom error handling.
  435. *
  436. * @sample highcharts/chart/highcharts-error/
  437. * Custom error handler
  438. *
  439. * @function Highcharts.error
  440. *
  441. * @param {number|string} code
  442. * The error code. See
  443. * [errors.xml](https://github.com/highcharts/highcharts/blob/master/errors/errors.xml)
  444. * for available codes. If it is a string, the error message is printed
  445. * directly in the console.
  446. *
  447. * @param {boolean} [stop=false]
  448. * Whether to throw an error or just log a warning in the console.
  449. *
  450. * @param {Highcharts.Chart} [chart]
  451. * Reference to the chart that causes the error. Used in 'debugger'
  452. * module to display errors directly on the chart.
  453. * Important note: This argument is undefined for errors that lack
  454. * access to the Chart instance.
  455. *
  456. * @param {Highcharts.Dictionary<string>} [params]
  457. * Additional parameters for the generated message.
  458. *
  459. * @return {void}
  460. */
  461. function error(code, stop, chart, params) {
  462. var severity = stop ? 'Highcharts error' : 'Highcharts warning';
  463. if (code === 32) {
  464. code = severity + ": Deprecated member";
  465. }
  466. var isCode = isNumber(code), message = isCode ?
  467. severity + " #" + code + ": www.highcharts.com/errors/" + code + "/" :
  468. code.toString(), defaultHandler = function () {
  469. if (stop) {
  470. throw new Error(message);
  471. }
  472. // else ...
  473. if (win.console &&
  474. error.messages.indexOf(message) === -1 // prevent console flooting
  475. ) {
  476. console.log(message); // eslint-disable-line no-console
  477. }
  478. };
  479. if (typeof params !== 'undefined') {
  480. var additionalMessages_1 = '';
  481. if (isCode) {
  482. message += '?';
  483. }
  484. objectEach(params, function (value, key) {
  485. additionalMessages_1 += "\n - " + key + ": " + value;
  486. if (isCode) {
  487. message += encodeURI(key) + '=' + encodeURI(value);
  488. }
  489. });
  490. message += additionalMessages_1;
  491. }
  492. if (chart) {
  493. fireEvent(chart, 'displayError', { code: code, message: message, params: params }, defaultHandler);
  494. }
  495. else {
  496. defaultHandler();
  497. }
  498. error.messages.push(message);
  499. }
  500. (function (error) {
  501. error.messages = [];
  502. })(error || (error = {}));
  503. H.error = error;
  504. /* eslint-disable no-invalid-this, valid-jsdoc */
  505. /**
  506. * An animator object used internally. One instance applies to one property
  507. * (attribute or style prop) on one element. Animation is always initiated
  508. * through {@link SVGElement#animate}.
  509. *
  510. * @example
  511. * var rect = renderer.rect(0, 0, 10, 10).add();
  512. * rect.animate({ width: 100 });
  513. *
  514. * @private
  515. * @class
  516. * @name Highcharts.Fx
  517. */
  518. var Fx = /** @class */ (function () {
  519. /* *
  520. *
  521. * Constructors
  522. *
  523. * */
  524. /**
  525. *
  526. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} elem
  527. * The element to animate.
  528. *
  529. * @param {Highcharts.AnimationOptionsObject} options
  530. * Animation options.
  531. *
  532. * @param {string} prop
  533. * The single attribute or CSS property to animate.
  534. */
  535. function Fx(elem, options, prop) {
  536. this.options = options;
  537. this.elem = elem;
  538. this.prop = prop;
  539. }
  540. /* *
  541. *
  542. * Functions
  543. *
  544. * */
  545. /**
  546. * Set the current step of a path definition on SVGElement.
  547. *
  548. * @function Highcharts.Fx#dSetter
  549. *
  550. * @return {void}
  551. */
  552. Fx.prototype.dSetter = function () {
  553. var paths = this.paths, start = paths && paths[0], end = paths && paths[1], path = [], now = this.now || 0;
  554. // Land on the final path without adjustment points appended in the ends
  555. if (now === 1 || !start || !end) {
  556. path = this.toD || [];
  557. }
  558. else if (start.length === end.length && now < 1) {
  559. for (var i = 0; i < end.length; i++) {
  560. // Tween between the start segment and the end segment. Start
  561. // with a copy of the end segment and tween the appropriate
  562. // numerics
  563. var startSeg = start[i];
  564. var endSeg = end[i];
  565. var tweenSeg = [];
  566. for (var j = 0; j < endSeg.length; j++) {
  567. var startItem = startSeg[j];
  568. var endItem = endSeg[j];
  569. // Tween numbers
  570. if (typeof startItem === 'number' &&
  571. typeof endItem === 'number' &&
  572. // Arc boolean flags
  573. !(endSeg[0] === 'A' && (j === 4 || j === 5))) {
  574. tweenSeg[j] = startItem + now * (endItem - startItem);
  575. // Strings, take directly from the end segment
  576. }
  577. else {
  578. tweenSeg[j] = endItem;
  579. }
  580. }
  581. path.push(tweenSeg);
  582. }
  583. // If animation is finished or length not matching, land on right value
  584. }
  585. else {
  586. path = end;
  587. }
  588. this.elem.attr('d', path, void 0, true);
  589. };
  590. /**
  591. * Update the element with the current animation step.
  592. *
  593. * @function Highcharts.Fx#update
  594. *
  595. * @return {void}
  596. */
  597. Fx.prototype.update = function () {
  598. var elem = this.elem, prop = this.prop, // if destroyed, it is null
  599. now = this.now, step = this.options.step;
  600. // Animation setter defined from outside
  601. if (this[prop + 'Setter']) {
  602. this[prop + 'Setter']();
  603. // Other animations on SVGElement
  604. }
  605. else if (elem.attr) {
  606. if (elem.element) {
  607. elem.attr(prop, now, null, true);
  608. }
  609. // HTML styles, raw HTML content like container size
  610. }
  611. else {
  612. elem.style[prop] = now + this.unit;
  613. }
  614. if (step) {
  615. step.call(elem, now, this);
  616. }
  617. };
  618. /**
  619. * Run an animation.
  620. *
  621. * @function Highcharts.Fx#run
  622. *
  623. * @param {number} from
  624. * The current value, value to start from.
  625. *
  626. * @param {number} to
  627. * The end value, value to land on.
  628. *
  629. * @param {string} unit
  630. * The property unit, for example `px`.
  631. *
  632. * @return {void}
  633. */
  634. Fx.prototype.run = function (from, to, unit) {
  635. var self = this, options = self.options, timer = function (gotoEnd) {
  636. return timer.stopped ? false : self.step(gotoEnd);
  637. }, requestAnimationFrame = win.requestAnimationFrame ||
  638. function (step) {
  639. setTimeout(step, 13);
  640. }, step = function () {
  641. for (var i = 0; i < H.timers.length; i++) {
  642. if (!H.timers[i]()) {
  643. H.timers.splice(i--, 1);
  644. }
  645. }
  646. if (H.timers.length) {
  647. requestAnimationFrame(step);
  648. }
  649. };
  650. if (from === to && !this.elem['forceAnimate:' + this.prop]) {
  651. delete options.curAnim[this.prop];
  652. if (options.complete && Object.keys(options.curAnim).length === 0) {
  653. options.complete.call(this.elem);
  654. }
  655. }
  656. else { // #7166
  657. this.startTime = +new Date();
  658. this.start = from;
  659. this.end = to;
  660. this.unit = unit;
  661. this.now = this.start;
  662. this.pos = 0;
  663. timer.elem = this.elem;
  664. timer.prop = this.prop;
  665. if (timer() && H.timers.push(timer) === 1) {
  666. requestAnimationFrame(step);
  667. }
  668. }
  669. };
  670. /**
  671. * Run a single step in the animation.
  672. *
  673. * @function Highcharts.Fx#step
  674. *
  675. * @param {boolean} [gotoEnd]
  676. * Whether to go to the endpoint of the animation after abort.
  677. *
  678. * @return {boolean}
  679. * Returns `true` if animation continues.
  680. */
  681. Fx.prototype.step = function (gotoEnd) {
  682. var t = +new Date(), ret, done, options = this.options, elem = this.elem, complete = options.complete, duration = options.duration, curAnim = options.curAnim;
  683. if (elem.attr && !elem.element) { // #2616, element is destroyed
  684. ret = false;
  685. }
  686. else if (gotoEnd || t >= duration + this.startTime) {
  687. this.now = this.end;
  688. this.pos = 1;
  689. this.update();
  690. curAnim[this.prop] = true;
  691. done = true;
  692. objectEach(curAnim, function (val) {
  693. if (val !== true) {
  694. done = false;
  695. }
  696. });
  697. if (done && complete) {
  698. complete.call(elem);
  699. }
  700. ret = false;
  701. }
  702. else {
  703. this.pos = options.easing((t - this.startTime) / duration);
  704. this.now = this.start + ((this.end - this.start) * this.pos);
  705. this.update();
  706. ret = true;
  707. }
  708. return ret;
  709. };
  710. /**
  711. * Prepare start and end values so that the path can be animated one to one.
  712. *
  713. * @function Highcharts.Fx#initPath
  714. *
  715. * @param {Highcharts.SVGElement} elem
  716. * The SVGElement item.
  717. *
  718. * @param {Highcharts.SVGPathArray|undefined} fromD
  719. * Starting path definition.
  720. *
  721. * @param {Highcharts.SVGPathArray} toD
  722. * Ending path definition.
  723. *
  724. * @return {Array<Highcharts.SVGPathArray,Highcharts.SVGPathArray>}
  725. * An array containing start and end paths in array form so that
  726. * they can be animated in parallel.
  727. */
  728. Fx.prototype.initPath = function (elem, fromD, toD) {
  729. var shift, startX = elem.startX, endX = elem.endX, fullLength, i, start = fromD && fromD.slice(), // copy
  730. end = toD.slice(), // copy
  731. isArea = elem.isArea, positionFactor = isArea ? 2 : 1, reverse;
  732. if (!start) {
  733. return [end, end];
  734. }
  735. /**
  736. * If shifting points, prepend a dummy point to the end path.
  737. * @private
  738. * @param {Highcharts.SVGPathArray} arr - array
  739. * @param {Highcharts.SVGPathArray} other - array
  740. * @return {void}
  741. */
  742. function prepend(arr, other) {
  743. while (arr.length < fullLength) {
  744. // Move to, line to or curve to?
  745. var moveSegment = arr[0], otherSegment = other[fullLength - arr.length];
  746. if (otherSegment && moveSegment[0] === 'M') {
  747. if (otherSegment[0] === 'C') {
  748. arr[0] = [
  749. 'C',
  750. moveSegment[1],
  751. moveSegment[2],
  752. moveSegment[1],
  753. moveSegment[2],
  754. moveSegment[1],
  755. moveSegment[2]
  756. ];
  757. }
  758. else {
  759. arr[0] = ['L', moveSegment[1], moveSegment[2]];
  760. }
  761. }
  762. // Prepend a copy of the first point
  763. arr.unshift(moveSegment);
  764. // For areas, the bottom path goes back again to the left, so we
  765. // need to append a copy of the last point.
  766. if (isArea) {
  767. arr.push(arr[arr.length - 1]);
  768. }
  769. }
  770. }
  771. /**
  772. * Copy and append last point until the length matches the end length.
  773. * @private
  774. * @param {Highcharts.SVGPathArray} arr - array
  775. * @param {Highcharts.SVGPathArray} other - array
  776. * @return {void}
  777. */
  778. function append(arr, other) {
  779. while (arr.length < fullLength) {
  780. // Pull out the slice that is going to be appended or inserted.
  781. // In a line graph, the positionFactor is 1, and the last point
  782. // is sliced out. In an area graph, the positionFactor is 2,
  783. // causing the middle two points to be sliced out, since an area
  784. // path starts at left, follows the upper path then turns and
  785. // follows the bottom back.
  786. var segmentToAdd = arr[arr.length / positionFactor - 1].slice();
  787. // Disable the first control point of curve segments
  788. if (segmentToAdd[0] === 'C') {
  789. segmentToAdd[1] = segmentToAdd[5];
  790. segmentToAdd[2] = segmentToAdd[6];
  791. }
  792. if (!isArea) {
  793. arr.push(segmentToAdd);
  794. }
  795. else {
  796. var lowerSegmentToAdd = arr[arr.length / positionFactor].slice();
  797. arr.splice(arr.length / 2, 0, segmentToAdd, lowerSegmentToAdd);
  798. }
  799. }
  800. }
  801. // For sideways animation, find out how much we need to shift to get the
  802. // start path Xs to match the end path Xs.
  803. if (startX && endX) {
  804. for (i = 0; i < startX.length; i++) {
  805. // Moving left, new points coming in on right
  806. if (startX[i] === endX[0]) {
  807. shift = i;
  808. break;
  809. // Moving right
  810. }
  811. else if (startX[0] ===
  812. endX[endX.length - startX.length + i]) {
  813. shift = i;
  814. reverse = true;
  815. break;
  816. // Fixed from the right side, "scaling" left
  817. }
  818. else if (startX[startX.length - 1] ===
  819. endX[endX.length - startX.length + i]) {
  820. shift = startX.length - i;
  821. break;
  822. }
  823. }
  824. if (typeof shift === 'undefined') {
  825. start = [];
  826. }
  827. }
  828. if (start.length && isNumber(shift)) {
  829. // The common target length for the start and end array, where both
  830. // arrays are padded in opposite ends
  831. fullLength = end.length + shift * positionFactor;
  832. if (!reverse) {
  833. prepend(end, start);
  834. append(start, end);
  835. }
  836. else {
  837. prepend(start, end);
  838. append(end, start);
  839. }
  840. }
  841. return [start, end];
  842. };
  843. /**
  844. * Handle animation of the color attributes directly.
  845. *
  846. * @function Highcharts.Fx#fillSetter
  847. *
  848. * @return {void}
  849. */
  850. Fx.prototype.fillSetter = function () {
  851. Fx.prototype.strokeSetter.apply(this, arguments);
  852. };
  853. /**
  854. * Handle animation of the color attributes directly.
  855. *
  856. * @function Highcharts.Fx#strokeSetter
  857. *
  858. * @return {void}
  859. */
  860. Fx.prototype.strokeSetter = function () {
  861. this.elem.attr(this.prop, H.color(this.start).tweenTo(H.color(this.end), this.pos), null, true);
  862. };
  863. return Fx;
  864. }());
  865. H.Fx = Fx;
  866. /* eslint-disable valid-jsdoc */
  867. /**
  868. * Utility function to deep merge two or more objects and return a third object.
  869. * If the first argument is true, the contents of the second object is copied
  870. * into the first object. The merge function can also be used with a single
  871. * object argument to create a deep copy of an object.
  872. *
  873. * @function Highcharts.merge<T>
  874. *
  875. * @param {boolean} extend
  876. * Whether to extend the left-side object (a) or return a whole new
  877. * object.
  878. *
  879. * @param {T|undefined} a
  880. * The first object to extend. When only this is given, the function
  881. * returns a deep copy.
  882. *
  883. * @param {...Array<object|undefined>} [n]
  884. * An object to merge into the previous one.
  885. *
  886. * @return {T}
  887. * The merged object. If the first argument is true, the return is the
  888. * same as the second argument.
  889. */ /**
  890. * Utility function to deep merge two or more objects and return a third object.
  891. * The merge function can also be used with a single object argument to create a
  892. * deep copy of an object.
  893. *
  894. * @function Highcharts.merge<T>
  895. *
  896. * @param {T|undefined} a
  897. * The first object to extend. When only this is given, the function
  898. * returns a deep copy.
  899. *
  900. * @param {...Array<object|undefined>} [n]
  901. * An object to merge into the previous one.
  902. *
  903. * @return {T}
  904. * The merged object. If the first argument is true, the return is the
  905. * same as the second argument.
  906. */
  907. function merge() {
  908. /* eslint-enable valid-jsdoc */
  909. var i, args = arguments, len, ret = {}, doCopy = function (copy, original) {
  910. // An object is replacing a primitive
  911. if (typeof copy !== 'object') {
  912. copy = {};
  913. }
  914. objectEach(original, function (value, key) {
  915. // Copy the contents of objects, but not arrays or DOM nodes
  916. if (isObject(value, true) &&
  917. !isClass(value) &&
  918. !isDOMElement(value)) {
  919. copy[key] = doCopy(copy[key] || {}, value);
  920. // Primitives and arrays are copied over directly
  921. }
  922. else {
  923. copy[key] = original[key];
  924. }
  925. });
  926. return copy;
  927. };
  928. // If first argument is true, copy into the existing object. Used in
  929. // setOptions.
  930. if (args[0] === true) {
  931. ret = args[1];
  932. args = Array.prototype.slice.call(args, 2);
  933. }
  934. // For each argument, extend the return
  935. len = args.length;
  936. for (i = 0; i < len; i++) {
  937. ret = doCopy(ret, args[i]);
  938. }
  939. return ret;
  940. }
  941. H.merge = merge;
  942. /**
  943. * Constrain a value to within a lower and upper threshold.
  944. *
  945. * @private
  946. * @param {number} value The initial value
  947. * @param {number} min The lower threshold
  948. * @param {number} max The upper threshold
  949. * @return {number} Returns a number value within min and max.
  950. */
  951. function clamp(value, min, max) {
  952. return value > min ? value < max ? value : max : min;
  953. }
  954. /**
  955. * Shortcut for parseInt
  956. *
  957. * @private
  958. * @function Highcharts.pInt
  959. *
  960. * @param {*} s
  961. * any
  962. *
  963. * @param {number} [mag]
  964. * Magnitude
  965. *
  966. * @return {number}
  967. * number
  968. */
  969. var pInt = H.pInt = function pInt(s, mag) {
  970. return parseInt(s, mag || 10);
  971. };
  972. /**
  973. * Utility function to check for string type.
  974. *
  975. * @function Highcharts.isString
  976. *
  977. * @param {*} s
  978. * The item to check.
  979. *
  980. * @return {boolean}
  981. * True if the argument is a string.
  982. */
  983. var isString = H.isString = function isString(s) {
  984. return typeof s === 'string';
  985. };
  986. /**
  987. * Utility function to check if an item is an array.
  988. *
  989. * @function Highcharts.isArray
  990. *
  991. * @param {*} obj
  992. * The item to check.
  993. *
  994. * @return {boolean}
  995. * True if the argument is an array.
  996. */
  997. var isArray = H.isArray = function isArray(obj) {
  998. var str = Object.prototype.toString.call(obj);
  999. return str === '[object Array]' || str === '[object Array Iterator]';
  1000. };
  1001. /**
  1002. * Utility function to check if an item is of type object.
  1003. *
  1004. * @function Highcharts.isObject
  1005. *
  1006. * @param {*} obj
  1007. * The item to check.
  1008. *
  1009. * @param {boolean} [strict=false]
  1010. * Also checks that the object is not an array.
  1011. *
  1012. * @return {boolean}
  1013. * True if the argument is an object.
  1014. */
  1015. function isObject(obj, strict) {
  1016. return (!!obj &&
  1017. typeof obj === 'object' &&
  1018. (!strict || !isArray(obj))); // eslint-disable-line @typescript-eslint/no-explicit-any
  1019. }
  1020. H.isObject = isObject;
  1021. /**
  1022. * Utility function to check if an Object is a HTML Element.
  1023. *
  1024. * @function Highcharts.isDOMElement
  1025. *
  1026. * @param {*} obj
  1027. * The item to check.
  1028. *
  1029. * @return {boolean}
  1030. * True if the argument is a HTML Element.
  1031. */
  1032. var isDOMElement = H.isDOMElement = function isDOMElement(obj) {
  1033. return isObject(obj) && typeof obj.nodeType === 'number';
  1034. };
  1035. /**
  1036. * Utility function to check if an Object is a class.
  1037. *
  1038. * @function Highcharts.isClass
  1039. *
  1040. * @param {object|undefined} obj
  1041. * The item to check.
  1042. *
  1043. * @return {boolean}
  1044. * True if the argument is a class.
  1045. */
  1046. var isClass = H.isClass = function isClass(obj) {
  1047. var c = obj && obj.constructor;
  1048. return !!(isObject(obj, true) &&
  1049. !isDOMElement(obj) &&
  1050. (c && c.name && c.name !== 'Object'));
  1051. };
  1052. /**
  1053. * Utility function to check if an item is a number and it is finite (not NaN,
  1054. * Infinity or -Infinity).
  1055. *
  1056. * @function Highcharts.isNumber
  1057. *
  1058. * @param {*} n
  1059. * The item to check.
  1060. *
  1061. * @return {boolean}
  1062. * True if the item is a finite number
  1063. */
  1064. var isNumber = H.isNumber = function isNumber(n) {
  1065. return typeof n === 'number' && !isNaN(n) && n < Infinity && n > -Infinity;
  1066. };
  1067. /**
  1068. * Remove the last occurence of an item from an array.
  1069. *
  1070. * @function Highcharts.erase
  1071. *
  1072. * @param {Array<*>} arr
  1073. * The array.
  1074. *
  1075. * @param {*} item
  1076. * The item to remove.
  1077. *
  1078. * @return {void}
  1079. */
  1080. var erase = H.erase = function erase(arr, item) {
  1081. var i = arr.length;
  1082. while (i--) {
  1083. if (arr[i] === item) {
  1084. arr.splice(i, 1);
  1085. break;
  1086. }
  1087. }
  1088. };
  1089. /**
  1090. * Check if an object is null or undefined.
  1091. *
  1092. * @function Highcharts.defined
  1093. *
  1094. * @param {*} obj
  1095. * The object to check.
  1096. *
  1097. * @return {boolean}
  1098. * False if the object is null or undefined, otherwise true.
  1099. */
  1100. var defined = H.defined = function defined(obj) {
  1101. return typeof obj !== 'undefined' && obj !== null;
  1102. };
  1103. /**
  1104. * Set or get an attribute or an object of attributes. To use as a setter, pass
  1105. * a key and a value, or let the second argument be a collection of keys and
  1106. * values. To use as a getter, pass only a string as the second argument.
  1107. *
  1108. * @function Highcharts.attr
  1109. *
  1110. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} elem
  1111. * The DOM element to receive the attribute(s).
  1112. *
  1113. * @param {string|Highcharts.HTMLAttributes|Highcharts.SVGAttributes} [prop]
  1114. * The property or an object of key-value pairs.
  1115. *
  1116. * @param {number|string} [value]
  1117. * The value if a single property is set.
  1118. *
  1119. * @return {string|null|undefined}
  1120. * When used as a getter, return the value.
  1121. */
  1122. function attr(elem, prop, value) {
  1123. var ret;
  1124. // if the prop is a string
  1125. if (isString(prop)) {
  1126. // set the value
  1127. if (defined(value)) {
  1128. elem.setAttribute(prop, value);
  1129. // get the value
  1130. }
  1131. else if (elem && elem.getAttribute) {
  1132. ret = elem.getAttribute(prop);
  1133. // IE7 and below cannot get class through getAttribute (#7850)
  1134. if (!ret && prop === 'class') {
  1135. ret = elem.getAttribute(prop + 'Name');
  1136. }
  1137. }
  1138. // else if prop is defined, it is a hash of key/value pairs
  1139. }
  1140. else {
  1141. objectEach(prop, function (val, key) {
  1142. elem.setAttribute(key, val);
  1143. });
  1144. }
  1145. return ret;
  1146. }
  1147. H.attr = attr;
  1148. /**
  1149. * Check if an element is an array, and if not, make it into an array.
  1150. *
  1151. * @function Highcharts.splat
  1152. *
  1153. * @param {*} obj
  1154. * The object to splat.
  1155. *
  1156. * @return {Array}
  1157. * The produced or original array.
  1158. */
  1159. var splat = H.splat = function splat(obj) {
  1160. return isArray(obj) ? obj : [obj];
  1161. };
  1162. /**
  1163. * Set a timeout if the delay is given, otherwise perform the function
  1164. * synchronously.
  1165. *
  1166. * @function Highcharts.syncTimeout
  1167. *
  1168. * @param {Function} fn
  1169. * The function callback.
  1170. *
  1171. * @param {number} delay
  1172. * Delay in milliseconds.
  1173. *
  1174. * @param {*} [context]
  1175. * An optional context to send to the function callback.
  1176. *
  1177. * @return {number}
  1178. * An identifier for the timeout that can later be cleared with
  1179. * Highcharts.clearTimeout. Returns -1 if there is no timeout.
  1180. */
  1181. var syncTimeout = H.syncTimeout = function syncTimeout(fn, delay, context) {
  1182. if (delay > 0) {
  1183. return setTimeout(fn, delay, context);
  1184. }
  1185. fn.call(0, context);
  1186. return -1;
  1187. };
  1188. /**
  1189. * Internal clear timeout. The function checks that the `id` was not removed
  1190. * (e.g. by `chart.destroy()`). For the details see
  1191. * [issue #7901](https://github.com/highcharts/highcharts/issues/7901).
  1192. *
  1193. * @function Highcharts.clearTimeout
  1194. *
  1195. * @param {number} id
  1196. * Id of a timeout.
  1197. *
  1198. * @return {void}
  1199. */
  1200. var internalClearTimeout = H.clearTimeout = function (id) {
  1201. if (defined(id)) {
  1202. clearTimeout(id);
  1203. }
  1204. };
  1205. /* eslint-disable valid-jsdoc */
  1206. /**
  1207. * Utility function to extend an object with the members of another.
  1208. *
  1209. * @function Highcharts.extend<T>
  1210. *
  1211. * @param {T|undefined} a
  1212. * The object to be extended.
  1213. *
  1214. * @param {object} b
  1215. * The object to add to the first one.
  1216. *
  1217. * @return {T}
  1218. * Object a, the original object.
  1219. */
  1220. var extend = H.extend = function extend(a, b) {
  1221. /* eslint-enable valid-jsdoc */
  1222. var n;
  1223. if (!a) {
  1224. a = {};
  1225. }
  1226. for (n in b) { // eslint-disable-line guard-for-in
  1227. a[n] = b[n];
  1228. }
  1229. return a;
  1230. };
  1231. /* eslint-disable valid-jsdoc */
  1232. /**
  1233. * Return the first value that is not null or undefined.
  1234. *
  1235. * @function Highcharts.pick<T>
  1236. *
  1237. * @param {...Array<T|null|undefined>} items
  1238. * Variable number of arguments to inspect.
  1239. *
  1240. * @return {T}
  1241. * The value of the first argument that is not null or undefined.
  1242. */
  1243. function pick() {
  1244. var args = arguments;
  1245. var length = args.length;
  1246. for (var i = 0; i < length; i++) {
  1247. var arg = args[i];
  1248. if (typeof arg !== 'undefined' && arg !== null) {
  1249. return arg;
  1250. }
  1251. }
  1252. }
  1253. H.pick = pick;
  1254. /**
  1255. * Set CSS on a given element.
  1256. *
  1257. * @function Highcharts.css
  1258. *
  1259. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGDOMElement} el
  1260. * An HTML DOM element.
  1261. *
  1262. * @param {Highcharts.CSSObject} styles
  1263. * Style object with camel case property names.
  1264. *
  1265. * @return {void}
  1266. */
  1267. var css = H.css = function css(el, styles) {
  1268. if (H.isMS && !H.svg) { // #2686
  1269. if (styles && typeof styles.opacity !== 'undefined') {
  1270. styles.filter =
  1271. 'alpha(opacity=' + (styles.opacity * 100) + ')';
  1272. }
  1273. }
  1274. extend(el.style, styles);
  1275. };
  1276. /**
  1277. * Utility function to create an HTML element with attributes and styles.
  1278. *
  1279. * @function Highcharts.createElement
  1280. *
  1281. * @param {string} tag
  1282. * The HTML tag.
  1283. *
  1284. * @param {Highcharts.HTMLAttributes} [attribs]
  1285. * Attributes as an object of key-value pairs.
  1286. *
  1287. * @param {Highcharts.CSSObject} [styles]
  1288. * Styles as an object of key-value pairs.
  1289. *
  1290. * @param {Highcharts.HTMLDOMElement} [parent]
  1291. * The parent HTML object.
  1292. *
  1293. * @param {boolean} [nopad=false]
  1294. * If true, remove all padding, border and margin.
  1295. *
  1296. * @return {Highcharts.HTMLDOMElement}
  1297. * The created DOM element.
  1298. */
  1299. var createElement = H.createElement = function createElement(tag, attribs, styles, parent, nopad) {
  1300. var el = doc.createElement(tag);
  1301. if (attribs) {
  1302. extend(el, attribs);
  1303. }
  1304. if (nopad) {
  1305. css(el, { padding: '0', border: 'none', margin: '0' });
  1306. }
  1307. if (styles) {
  1308. css(el, styles);
  1309. }
  1310. if (parent) {
  1311. parent.appendChild(el);
  1312. }
  1313. return el;
  1314. };
  1315. // eslint-disable-next-line valid-jsdoc
  1316. /**
  1317. * Extend a prototyped class by new members.
  1318. *
  1319. * @function Highcharts.extendClass<T>
  1320. *
  1321. * @param {Highcharts.Class<T>} parent
  1322. * The parent prototype to inherit.
  1323. *
  1324. * @param {Highcharts.Dictionary<*>} members
  1325. * A collection of prototype members to add or override compared to the
  1326. * parent prototype.
  1327. *
  1328. * @return {Highcharts.Class<T>}
  1329. * A new prototype.
  1330. */
  1331. var extendClass = H.extendClass = function extendClass(parent, members) {
  1332. var obj = (function () { });
  1333. obj.prototype = new parent(); // eslint-disable-line new-cap
  1334. extend(obj.prototype, members);
  1335. return obj;
  1336. };
  1337. /**
  1338. * Left-pad a string to a given length by adding a character repetetively.
  1339. *
  1340. * @function Highcharts.pad
  1341. *
  1342. * @param {number} number
  1343. * The input string or number.
  1344. *
  1345. * @param {number} [length]
  1346. * The desired string length.
  1347. *
  1348. * @param {string} [padder=0]
  1349. * The character to pad with.
  1350. *
  1351. * @return {string}
  1352. * The padded string.
  1353. */
  1354. var pad = H.pad = function pad(number, length, padder) {
  1355. return new Array((length || 2) +
  1356. 1 -
  1357. String(number)
  1358. .replace('-', '')
  1359. .length).join(padder || '0') + number;
  1360. };
  1361. /**
  1362. * Return a length based on either the integer value, or a percentage of a base.
  1363. *
  1364. * @function Highcharts.relativeLength
  1365. *
  1366. * @param {Highcharts.RelativeSize} value
  1367. * A percentage string or a number.
  1368. *
  1369. * @param {number} base
  1370. * The full length that represents 100%.
  1371. *
  1372. * @param {number} [offset=0]
  1373. * A pixel offset to apply for percentage values. Used internally in
  1374. * axis positioning.
  1375. *
  1376. * @return {number}
  1377. * The computed length.
  1378. */
  1379. var relativeLength = H.relativeLength = function relativeLength(value, base, offset) {
  1380. return (/%$/).test(value) ?
  1381. (base * parseFloat(value) / 100) + (offset || 0) :
  1382. parseFloat(value);
  1383. };
  1384. /**
  1385. * Wrap a method with extended functionality, preserving the original function.
  1386. *
  1387. * @function Highcharts.wrap
  1388. *
  1389. * @param {*} obj
  1390. * The context object that the method belongs to. In real cases, this is
  1391. * often a prototype.
  1392. *
  1393. * @param {string} method
  1394. * The name of the method to extend.
  1395. *
  1396. * @param {Highcharts.WrapProceedFunction} func
  1397. * A wrapper function callback. This function is called with the same
  1398. * arguments as the original function, except that the original function
  1399. * is unshifted and passed as the first argument.
  1400. */
  1401. var wrap = H.wrap = function wrap(obj, method, func) {
  1402. var proceed = obj[method];
  1403. obj[method] = function () {
  1404. var args = Array.prototype.slice.call(arguments), outerArgs = arguments, ctx = this, ret;
  1405. ctx.proceed = function () {
  1406. proceed.apply(ctx, arguments.length ? arguments : outerArgs);
  1407. };
  1408. args.unshift(proceed);
  1409. ret = func.apply(this, args);
  1410. ctx.proceed = null;
  1411. return ret;
  1412. };
  1413. };
  1414. /**
  1415. * Format a string according to a subset of the rules of Python's String.format
  1416. * method.
  1417. *
  1418. * @example
  1419. * var s = Highcharts.format(
  1420. * 'The {color} fox was {len:.2f} feet long',
  1421. * { color: 'red', len: Math.PI }
  1422. * );
  1423. * // => The red fox was 3.14 feet long
  1424. *
  1425. * @function Highcharts.format
  1426. *
  1427. * @param {string} str
  1428. * The string to format.
  1429. *
  1430. * @param {Record<string, *>} ctx
  1431. * The context, a collection of key-value pairs where each key is
  1432. * replaced by its value.
  1433. *
  1434. * @param {Highcharts.Chart} [chart]
  1435. * A `Chart` instance used to get numberFormatter and time.
  1436. *
  1437. * @return {string}
  1438. * The formatted string.
  1439. */
  1440. var format = H.format = function (str, ctx, chart) {
  1441. var splitter = '{', isInside = false, segment, valueAndFormat, ret = [], val, index;
  1442. var floatRegex = /f$/;
  1443. var decRegex = /\.([0-9])/;
  1444. var lang = H.defaultOptions.lang;
  1445. var time = chart && chart.time || H.time;
  1446. var numberFormatter = chart && chart.numberFormatter || numberFormat;
  1447. while (str) {
  1448. index = str.indexOf(splitter);
  1449. if (index === -1) {
  1450. break;
  1451. }
  1452. segment = str.slice(0, index);
  1453. if (isInside) { // we're on the closing bracket looking back
  1454. valueAndFormat = segment.split(':');
  1455. val = getNestedProperty(valueAndFormat.shift() || '', ctx);
  1456. // Format the replacement
  1457. if (valueAndFormat.length && typeof val === 'number') {
  1458. segment = valueAndFormat.join(':');
  1459. if (floatRegex.test(segment)) { // float
  1460. var decimals = parseInt((segment.match(decRegex) || ['', '-1'])[1], 10);
  1461. if (val !== null) {
  1462. val = numberFormatter(val, decimals, lang.decimalPoint, segment.indexOf(',') > -1 ? lang.thousandsSep : '');
  1463. }
  1464. }
  1465. else {
  1466. val = time.dateFormat(segment, val);
  1467. }
  1468. }
  1469. // Push the result and advance the cursor
  1470. ret.push(val);
  1471. }
  1472. else {
  1473. ret.push(segment);
  1474. }
  1475. str = str.slice(index + 1); // the rest
  1476. isInside = !isInside; // toggle
  1477. splitter = isInside ? '}' : '{'; // now look for next matching bracket
  1478. }
  1479. ret.push(str);
  1480. return ret.join('');
  1481. };
  1482. /**
  1483. * Get the magnitude of a number.
  1484. *
  1485. * @function Highcharts.getMagnitude
  1486. *
  1487. * @param {number} num
  1488. * The number.
  1489. *
  1490. * @return {number}
  1491. * The magnitude, where 1-9 are magnitude 1, 10-99 magnitude 2 etc.
  1492. */
  1493. var getMagnitude = H.getMagnitude = function (num) {
  1494. return Math.pow(10, Math.floor(Math.log(num) / Math.LN10));
  1495. };
  1496. /**
  1497. * Take an interval and normalize it to multiples of round numbers.
  1498. *
  1499. * @deprecated
  1500. * @function Highcharts.normalizeTickInterval
  1501. *
  1502. * @param {number} interval
  1503. * The raw, un-rounded interval.
  1504. *
  1505. * @param {Array<*>} [multiples]
  1506. * Allowed multiples.
  1507. *
  1508. * @param {number} [magnitude]
  1509. * The magnitude of the number.
  1510. *
  1511. * @param {boolean} [allowDecimals]
  1512. * Whether to allow decimals.
  1513. *
  1514. * @param {boolean} [hasTickAmount]
  1515. * If it has tickAmount, avoid landing on tick intervals lower than
  1516. * original.
  1517. *
  1518. * @return {number}
  1519. * The normalized interval.
  1520. *
  1521. * @todo
  1522. * Move this function to the Axis prototype. It is here only for historical
  1523. * reasons.
  1524. */
  1525. var normalizeTickInterval = H.normalizeTickInterval = function (interval, multiples, magnitude, allowDecimals, hasTickAmount) {
  1526. var normalized, i, retInterval = interval;
  1527. // round to a tenfold of 1, 2, 2.5 or 5
  1528. magnitude = pick(magnitude, 1);
  1529. normalized = interval / magnitude;
  1530. // multiples for a linear scale
  1531. if (!multiples) {
  1532. multiples = hasTickAmount ?
  1533. // Finer grained ticks when the tick amount is hard set, including
  1534. // when alignTicks is true on multiple axes (#4580).
  1535. [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10] :
  1536. // Else, let ticks fall on rounder numbers
  1537. [1, 2, 2.5, 5, 10];
  1538. // the allowDecimals option
  1539. if (allowDecimals === false) {
  1540. if (magnitude === 1) {
  1541. multiples = multiples.filter(function (num) {
  1542. return num % 1 === 0;
  1543. });
  1544. }
  1545. else if (magnitude <= 0.1) {
  1546. multiples = [1 / magnitude];
  1547. }
  1548. }
  1549. }
  1550. // normalize the interval to the nearest multiple
  1551. for (i = 0; i < multiples.length; i++) {
  1552. retInterval = multiples[i];
  1553. // only allow tick amounts smaller than natural
  1554. if ((hasTickAmount &&
  1555. retInterval * magnitude >= interval) ||
  1556. (!hasTickAmount &&
  1557. (normalized <=
  1558. (multiples[i] +
  1559. (multiples[i + 1] || multiples[i])) / 2))) {
  1560. break;
  1561. }
  1562. }
  1563. // Multiply back to the correct magnitude. Correct floats to appropriate
  1564. // precision (#6085).
  1565. retInterval = correctFloat(retInterval * magnitude, -Math.round(Math.log(0.001) / Math.LN10));
  1566. return retInterval;
  1567. };
  1568. /**
  1569. * Sort an object array and keep the order of equal items. The ECMAScript
  1570. * standard does not specify the behaviour when items are equal.
  1571. *
  1572. * @function Highcharts.stableSort
  1573. *
  1574. * @param {Array<*>} arr
  1575. * The array to sort.
  1576. *
  1577. * @param {Function} sortFunction
  1578. * The function to sort it with, like with regular Array.prototype.sort.
  1579. *
  1580. * @return {void}
  1581. */
  1582. var stableSort = H.stableSort = function stableSort(arr, sortFunction) {
  1583. // @todo It seems like Chrome since v70 sorts in a stable way internally,
  1584. // plus all other browsers do it, so over time we may be able to remove this
  1585. // function
  1586. var length = arr.length, sortValue, i;
  1587. // Add index to each item
  1588. for (i = 0; i < length; i++) {
  1589. arr[i].safeI = i; // stable sort index
  1590. }
  1591. arr.sort(function (a, b) {
  1592. sortValue = sortFunction(a, b);
  1593. return sortValue === 0 ? a.safeI - b.safeI : sortValue;
  1594. });
  1595. // Remove index from items
  1596. for (i = 0; i < length; i++) {
  1597. delete arr[i].safeI; // stable sort index
  1598. }
  1599. };
  1600. /**
  1601. * Non-recursive method to find the lowest member of an array. `Math.min` raises
  1602. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1603. * than 150.000 points. This method is slightly slower, but safe.
  1604. *
  1605. * @function Highcharts.arrayMin
  1606. *
  1607. * @param {Array<*>} data
  1608. * An array of numbers.
  1609. *
  1610. * @return {number}
  1611. * The lowest number.
  1612. */
  1613. var arrayMin = H.arrayMin = function arrayMin(data) {
  1614. var i = data.length, min = data[0];
  1615. while (i--) {
  1616. if (data[i] < min) {
  1617. min = data[i];
  1618. }
  1619. }
  1620. return min;
  1621. };
  1622. /**
  1623. * Non-recursive method to find the lowest member of an array. `Math.max` raises
  1624. * a maximum call stack size exceeded error in Chrome when trying to apply more
  1625. * than 150.000 points. This method is slightly slower, but safe.
  1626. *
  1627. * @function Highcharts.arrayMax
  1628. *
  1629. * @param {Array<*>} data
  1630. * An array of numbers.
  1631. *
  1632. * @return {number}
  1633. * The highest number.
  1634. */
  1635. var arrayMax = H.arrayMax = function arrayMax(data) {
  1636. var i = data.length, max = data[0];
  1637. while (i--) {
  1638. if (data[i] > max) {
  1639. max = data[i];
  1640. }
  1641. }
  1642. return max;
  1643. };
  1644. /**
  1645. * Utility method that destroys any SVGElement instances that are properties on
  1646. * the given object. It loops all properties and invokes destroy if there is a
  1647. * destroy method. The property is then delete.
  1648. *
  1649. * @function Highcharts.destroyObjectProperties
  1650. *
  1651. * @param {*} obj
  1652. * The object to destroy properties on.
  1653. *
  1654. * @param {*} [except]
  1655. * Exception, do not destroy this property, only delete it.
  1656. *
  1657. * @return {void}
  1658. */
  1659. var destroyObjectProperties = H.destroyObjectProperties =
  1660. function destroyObjectProperties(obj, except) {
  1661. objectEach(obj, function (val, n) {
  1662. // If the object is non-null and destroy is defined
  1663. if (val && val !== except && val.destroy) {
  1664. // Invoke the destroy
  1665. val.destroy();
  1666. }
  1667. // Delete the property from the object.
  1668. delete obj[n];
  1669. });
  1670. };
  1671. /**
  1672. * Discard a HTML element by moving it to the bin and delete.
  1673. *
  1674. * @function Highcharts.discardElement
  1675. *
  1676. * @param {Highcharts.HTMLDOMElement} element
  1677. * The HTML node to discard.
  1678. *
  1679. * @return {void}
  1680. */
  1681. var discardElement = H.discardElement = function discardElement(element) {
  1682. var garbageBin = H.garbageBin;
  1683. // create a garbage bin element, not part of the DOM
  1684. if (!garbageBin) {
  1685. garbageBin = createElement('div');
  1686. }
  1687. // move the node and empty bin
  1688. if (element) {
  1689. garbageBin.appendChild(element);
  1690. }
  1691. garbageBin.innerHTML = '';
  1692. };
  1693. /**
  1694. * Fix JS round off float errors.
  1695. *
  1696. * @function Highcharts.correctFloat
  1697. *
  1698. * @param {number} num
  1699. * A float number to fix.
  1700. *
  1701. * @param {number} [prec=14]
  1702. * The precision.
  1703. *
  1704. * @return {number}
  1705. * The corrected float number.
  1706. */
  1707. var correctFloat = H.correctFloat = function correctFloat(num, prec) {
  1708. return parseFloat(num.toPrecision(prec || 14));
  1709. };
  1710. /**
  1711. * Set the global animation to either a given value, or fall back to the given
  1712. * chart's animation option.
  1713. *
  1714. * @function Highcharts.setAnimation
  1715. *
  1716. * @param {boolean|Highcharts.AnimationOptionsObject|undefined} animation
  1717. * The animation object.
  1718. *
  1719. * @param {Highcharts.Chart} chart
  1720. * The chart instance.
  1721. *
  1722. * @return {void}
  1723. *
  1724. * @todo
  1725. * This function always relates to a chart, and sets a property on the renderer,
  1726. * so it should be moved to the SVGRenderer.
  1727. */
  1728. var setAnimation = H.setAnimation = function setAnimation(animation, chart) {
  1729. chart.renderer.globalAnimation = pick(animation, chart.options.chart.animation, true);
  1730. };
  1731. /**
  1732. * Get the animation in object form, where a disabled animation is always
  1733. * returned as `{ duration: 0 }`.
  1734. *
  1735. * @function Highcharts.animObject
  1736. *
  1737. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=0]
  1738. * An animation setting. Can be an object with duration, complete and
  1739. * easing properties, or a boolean to enable or disable.
  1740. *
  1741. * @return {Highcharts.AnimationOptionsObject}
  1742. * An object with at least a duration property.
  1743. */
  1744. var animObject = H.animObject = function animObject(animation) {
  1745. return isObject(animation) ?
  1746. merge(animation) :
  1747. { duration: animation ? 500 : 0 };
  1748. };
  1749. /**
  1750. * The time unit lookup
  1751. *
  1752. * @ignore
  1753. */
  1754. var timeUnits = H.timeUnits = {
  1755. millisecond: 1,
  1756. second: 1000,
  1757. minute: 60000,
  1758. hour: 3600000,
  1759. day: 24 * 3600000,
  1760. week: 7 * 24 * 3600000,
  1761. month: 28 * 24 * 3600000,
  1762. year: 364 * 24 * 3600000
  1763. };
  1764. /**
  1765. * Format a number and return a string based on input settings.
  1766. *
  1767. * @sample highcharts/members/highcharts-numberformat/
  1768. * Custom number format
  1769. *
  1770. * @function Highcharts.numberFormat
  1771. *
  1772. * @param {number} number
  1773. * The input number to format.
  1774. *
  1775. * @param {number} decimals
  1776. * The amount of decimals. A value of -1 preserves the amount in the
  1777. * input number.
  1778. *
  1779. * @param {string} [decimalPoint]
  1780. * The decimal point, defaults to the one given in the lang options, or
  1781. * a dot.
  1782. *
  1783. * @param {string} [thousandsSep]
  1784. * The thousands separator, defaults to the one given in the lang
  1785. * options, or a space character.
  1786. *
  1787. * @return {string}
  1788. * The formatted number.
  1789. */
  1790. var numberFormat = H.numberFormat = function numberFormat(number, decimals, decimalPoint, thousandsSep) {
  1791. number = +number || 0;
  1792. decimals = +decimals;
  1793. var lang = H.defaultOptions.lang, origDec = (number.toString().split('.')[1] || '').split('e')[0].length, strinteger, thousands, ret, roundedNumber, exponent = number.toString().split('e'), fractionDigits;
  1794. if (decimals === -1) {
  1795. // Preserve decimals. Not huge numbers (#3793).
  1796. decimals = Math.min(origDec, 20);
  1797. }
  1798. else if (!isNumber(decimals)) {
  1799. decimals = 2;
  1800. }
  1801. else if (decimals && exponent[1] && exponent[1] < 0) {
  1802. // Expose decimals from exponential notation (#7042)
  1803. fractionDigits = decimals + +exponent[1];
  1804. if (fractionDigits >= 0) {
  1805. // remove too small part of the number while keeping the notation
  1806. exponent[0] = (+exponent[0]).toExponential(fractionDigits)
  1807. .split('e')[0];
  1808. decimals = fractionDigits;
  1809. }
  1810. else {
  1811. // fractionDigits < 0
  1812. exponent[0] = exponent[0].split('.')[0] || 0;
  1813. if (decimals < 20) {
  1814. // use number instead of exponential notation (#7405)
  1815. number = (exponent[0] * Math.pow(10, exponent[1]))
  1816. .toFixed(decimals);
  1817. }
  1818. else {
  1819. // or zero
  1820. number = 0;
  1821. }
  1822. exponent[1] = 0;
  1823. }
  1824. }
  1825. // Add another decimal to avoid rounding errors of float numbers. (#4573)
  1826. // Then use toFixed to handle rounding.
  1827. roundedNumber = (Math.abs(exponent[1] ? exponent[0] : number) +
  1828. Math.pow(10, -Math.max(decimals, origDec) - 1)).toFixed(decimals);
  1829. // A string containing the positive integer component of the number
  1830. strinteger = String(pInt(roundedNumber));
  1831. // Leftover after grouping into thousands. Can be 0, 1 or 2.
  1832. thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;
  1833. // Language
  1834. decimalPoint = pick(decimalPoint, lang.decimalPoint);
  1835. thousandsSep = pick(thousandsSep, lang.thousandsSep);
  1836. // Start building the return
  1837. ret = number < 0 ? '-' : '';
  1838. // Add the leftover after grouping into thousands. For example, in the
  1839. // number 42 000 000, this line adds 42.
  1840. ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';
  1841. // Add the remaining thousands groups, joined by the thousands separator
  1842. ret += strinteger
  1843. .substr(thousands)
  1844. .replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep);
  1845. // Add the decimal point and the decimal component
  1846. if (decimals) {
  1847. // Get the decimal component
  1848. ret += decimalPoint + roundedNumber.slice(-decimals);
  1849. }
  1850. if (exponent[1] && +ret !== 0) {
  1851. ret += 'e' + exponent[1];
  1852. }
  1853. return ret;
  1854. };
  1855. /**
  1856. * Easing definition
  1857. *
  1858. * @private
  1859. * @function Math.easeInOutSine
  1860. *
  1861. * @param {number} pos
  1862. * Current position, ranging from 0 to 1.
  1863. *
  1864. * @return {number}
  1865. * Ease result
  1866. */
  1867. Math.easeInOutSine = function (pos) {
  1868. return -0.5 * (Math.cos(Math.PI * pos) - 1);
  1869. };
  1870. /**
  1871. * Returns the value of a property path on a given object.
  1872. *
  1873. * @private
  1874. * @function getNestedProperty
  1875. *
  1876. * @param {string} path
  1877. * Path to the property, for example `custom.myValue`.
  1878. *
  1879. * @param {unknown} obj
  1880. * Instance containing the property on the specific path.
  1881. *
  1882. * @return {unknown}
  1883. * The unknown property value.
  1884. */
  1885. function getNestedProperty(path, obj) {
  1886. if (!path) {
  1887. return obj;
  1888. }
  1889. var pathElements = path.split('.').reverse();
  1890. var subProperty = obj;
  1891. if (pathElements.length === 1) {
  1892. return subProperty[path];
  1893. }
  1894. var pathElement = pathElements.pop();
  1895. while (typeof pathElement !== 'undefined' &&
  1896. typeof subProperty !== 'undefined' &&
  1897. subProperty !== null) {
  1898. subProperty = subProperty[pathElement];
  1899. pathElement = pathElements.pop();
  1900. }
  1901. return subProperty;
  1902. }
  1903. /**
  1904. * Get the computed CSS value for given element and property, only for numerical
  1905. * properties. For width and height, the dimension of the inner box (excluding
  1906. * padding) is returned. Used for fitting the chart within the container.
  1907. *
  1908. * @function Highcharts.getStyle
  1909. *
  1910. * @param {Highcharts.HTMLDOMElement} el
  1911. * An HTML element.
  1912. *
  1913. * @param {string} prop
  1914. * The property name.
  1915. *
  1916. * @param {boolean} [toInt=true]
  1917. * Parse to integer.
  1918. *
  1919. * @return {number|string}
  1920. * The numeric value.
  1921. */
  1922. var getStyle = H.getStyle = function (el, prop, toInt) {
  1923. var style;
  1924. // For width and height, return the actual inner pixel size (#4913)
  1925. if (prop === 'width') {
  1926. var offsetWidth = Math.min(el.offsetWidth, el.scrollWidth);
  1927. // In flex boxes, we need to use getBoundingClientRect and floor it,
  1928. // because scrollWidth doesn't support subpixel precision (#6427) ...
  1929. var boundingClientRectWidth = el.getBoundingClientRect &&
  1930. el.getBoundingClientRect().width;
  1931. // ...unless if the containing div or its parents are transform-scaled
  1932. // down, in which case the boundingClientRect can't be used as it is
  1933. // also scaled down (#9871, #10498).
  1934. if (boundingClientRectWidth < offsetWidth &&
  1935. boundingClientRectWidth >= offsetWidth - 1) {
  1936. offsetWidth = Math.floor(boundingClientRectWidth);
  1937. }
  1938. return Math.max(0, // #8377
  1939. (offsetWidth -
  1940. H.getStyle(el, 'padding-left') -
  1941. H.getStyle(el, 'padding-right')));
  1942. }
  1943. if (prop === 'height') {
  1944. return Math.max(0, // #8377
  1945. Math.min(el.offsetHeight, el.scrollHeight) -
  1946. H.getStyle(el, 'padding-top') -
  1947. H.getStyle(el, 'padding-bottom'));
  1948. }
  1949. if (!win.getComputedStyle) {
  1950. // SVG not supported, forgot to load oldie.js?
  1951. error(27, true);
  1952. }
  1953. // Otherwise, get the computed style
  1954. style = win.getComputedStyle(el, undefined); // eslint-disable-line no-undefined
  1955. if (style) {
  1956. style = style.getPropertyValue(prop);
  1957. if (pick(toInt, prop !== 'opacity')) {
  1958. style = pInt(style);
  1959. }
  1960. }
  1961. return style;
  1962. };
  1963. /**
  1964. * Search for an item in an array.
  1965. *
  1966. * @function Highcharts.inArray
  1967. *
  1968. * @deprecated
  1969. *
  1970. * @param {*} item
  1971. * The item to search for.
  1972. *
  1973. * @param {Array<*>} arr
  1974. * The array or node collection to search in.
  1975. *
  1976. * @param {number} [fromIndex=0]
  1977. * The index to start searching from.
  1978. *
  1979. * @return {number}
  1980. * The index within the array, or -1 if not found.
  1981. */
  1982. var inArray = H.inArray = function (item, arr, fromIndex) {
  1983. error(32, false, void 0, { 'Highcharts.inArray': 'use Array.indexOf' });
  1984. return arr.indexOf(item, fromIndex);
  1985. };
  1986. /* eslint-disable valid-jsdoc */
  1987. /**
  1988. * Return the value of the first element in the array that satisfies the
  1989. * provided testing function.
  1990. *
  1991. * @function Highcharts.find<T>
  1992. *
  1993. * @param {Array<T>} arr
  1994. * The array to test.
  1995. *
  1996. * @param {Function} callback
  1997. * The callback function. The function receives the item as the first
  1998. * argument. Return `true` if this item satisfies the condition.
  1999. *
  2000. * @return {T|undefined}
  2001. * The value of the element.
  2002. */
  2003. var find = H.find = Array.prototype.find ?
  2004. /* eslint-enable valid-jsdoc */
  2005. function (arr, callback) {
  2006. return arr.find(callback);
  2007. } :
  2008. // Legacy implementation. PhantomJS, IE <= 11 etc. #7223.
  2009. function (arr, callback) {
  2010. var i, length = arr.length;
  2011. for (i = 0; i < length; i++) {
  2012. if (callback(arr[i], i)) { // eslint-disable-line callback-return
  2013. return arr[i];
  2014. }
  2015. }
  2016. };
  2017. /**
  2018. * Returns an array of a given object's own properties.
  2019. *
  2020. * @function Highcharts.keys
  2021. * @deprecated
  2022. *
  2023. * @param {*} obj
  2024. * The object of which the properties are to be returned.
  2025. *
  2026. * @return {Array<string>}
  2027. * An array of strings that represents all the properties.
  2028. */
  2029. H.keys = function (obj) {
  2030. error(32, false, void 0, { 'Highcharts.keys': 'use Object.keys' });
  2031. return Object.keys(obj);
  2032. };
  2033. /**
  2034. * Get the element's offset position, corrected for `overflow: auto`.
  2035. *
  2036. * @function Highcharts.offset
  2037. *
  2038. * @param {global.Element} el
  2039. * The DOM element.
  2040. *
  2041. * @return {Highcharts.OffsetObject}
  2042. * An object containing `left` and `top` properties for the position in
  2043. * the page.
  2044. */
  2045. var offset = H.offset = function offset(el) {
  2046. var docElem = doc.documentElement, box = (el.parentElement || el.parentNode) ?
  2047. el.getBoundingClientRect() :
  2048. { top: 0, left: 0 };
  2049. return {
  2050. top: box.top + (win.pageYOffset || docElem.scrollTop) -
  2051. (docElem.clientTop || 0),
  2052. left: box.left + (win.pageXOffset || docElem.scrollLeft) -
  2053. (docElem.clientLeft || 0)
  2054. };
  2055. };
  2056. /**
  2057. * Stop running animation.
  2058. *
  2059. * @function Highcharts.stop
  2060. *
  2061. * @param {Highcharts.SVGElement} el
  2062. * The SVGElement to stop animation on.
  2063. *
  2064. * @param {string} [prop]
  2065. * The property to stop animating. If given, the stop method will stop a
  2066. * single property from animating, while others continue.
  2067. *
  2068. * @return {void}
  2069. *
  2070. * @todo
  2071. * A possible extension to this would be to stop a single property, when
  2072. * we want to continue animating others. Then assign the prop to the timer
  2073. * in the Fx.run method, and check for the prop here. This would be an
  2074. * improvement in all cases where we stop the animation from .attr. Instead of
  2075. * stopping everything, we can just stop the actual attributes we're setting.
  2076. */
  2077. var stop = H.stop = function (el, prop) {
  2078. var i = H.timers.length;
  2079. // Remove timers related to this element (#4519)
  2080. while (i--) {
  2081. if (H.timers[i].elem === el && (!prop || prop === H.timers[i].prop)) {
  2082. H.timers[i].stopped = true; // #4667
  2083. }
  2084. }
  2085. };
  2086. /* eslint-disable valid-jsdoc */
  2087. /**
  2088. * Iterate over object key pairs in an object.
  2089. *
  2090. * @function Highcharts.objectEach<T>
  2091. *
  2092. * @param {*} obj
  2093. * The object to iterate over.
  2094. *
  2095. * @param {Highcharts.ObjectEachCallbackFunction<T>} fn
  2096. * The iterator callback. It passes three arguments:
  2097. * * value - The property value.
  2098. * * key - The property key.
  2099. * * obj - The object that objectEach is being applied to.
  2100. *
  2101. * @param {T} [ctx]
  2102. * The context.
  2103. *
  2104. * @return {void}
  2105. */
  2106. var objectEach = H.objectEach = function objectEach(obj, fn, ctx) {
  2107. /* eslint-enable valid-jsdoc */
  2108. for (var key in obj) {
  2109. if (Object.hasOwnProperty.call(obj, key)) {
  2110. fn.call(ctx || obj[key], obj[key], key, obj);
  2111. }
  2112. }
  2113. };
  2114. /**
  2115. * Iterate over an array.
  2116. *
  2117. * @deprecated
  2118. * @function Highcharts.each
  2119. *
  2120. * @param {Array<*>} arr
  2121. * The array to iterate over.
  2122. *
  2123. * @param {Function} fn
  2124. * The iterator callback. It passes three arguments:
  2125. * - `item`: The array item.
  2126. * - `index`: The item's index in the array.
  2127. * - `arr`: The array that each is being applied to.
  2128. *
  2129. * @param {*} [ctx]
  2130. * The context.
  2131. *
  2132. * @return {void}
  2133. */
  2134. /**
  2135. * Filter an array by a callback.
  2136. *
  2137. * @deprecated
  2138. * @function Highcharts.grep
  2139. *
  2140. * @param {Array<*>} arr
  2141. * The array to filter.
  2142. *
  2143. * @param {Function} callback
  2144. * The callback function. The function receives the item as the first
  2145. * argument. Return `true` if the item is to be preserved.
  2146. *
  2147. * @return {Array<*>}
  2148. * A new, filtered array.
  2149. */
  2150. /**
  2151. * Map an array by a callback.
  2152. *
  2153. * @deprecated
  2154. * @function Highcharts.map
  2155. *
  2156. * @param {Array<*>} arr
  2157. * The array to map.
  2158. *
  2159. * @param {Function} fn
  2160. * The callback function. Return the new value for the new array.
  2161. *
  2162. * @return {Array<*>}
  2163. * A new array item with modified items.
  2164. */
  2165. /**
  2166. * Reduce an array to a single value.
  2167. *
  2168. * @deprecated
  2169. * @function Highcharts.reduce
  2170. *
  2171. * @param {Array<*>} arr
  2172. * The array to reduce.
  2173. *
  2174. * @param {Function} fn
  2175. * The callback function. Return the reduced value. Receives 4
  2176. * arguments: Accumulated/reduced value, current value, current array
  2177. * index, and the array.
  2178. *
  2179. * @param {*} initialValue
  2180. * The initial value of the accumulator.
  2181. *
  2182. * @return {*}
  2183. * The reduced value.
  2184. */
  2185. /**
  2186. * Test whether at least one element in the array passes the test implemented by
  2187. * the provided function.
  2188. *
  2189. * @deprecated
  2190. * @function Highcharts.some
  2191. *
  2192. * @param {Array<*>} arr
  2193. * The array to test
  2194. *
  2195. * @param {Function} fn
  2196. * The function to run on each item. Return truty to pass the test.
  2197. * Receives arguments `currentValue`, `index` and `array`.
  2198. *
  2199. * @param {*} ctx
  2200. * The context.
  2201. *
  2202. * @return {boolean}
  2203. */
  2204. objectEach({
  2205. map: 'map',
  2206. each: 'forEach',
  2207. grep: 'filter',
  2208. reduce: 'reduce',
  2209. some: 'some'
  2210. }, function (val, key) {
  2211. H[key] = function (arr) {
  2212. var _a;
  2213. error(32, false, void 0, (_a = {}, _a["Highcharts." + key] = "use Array." + val, _a));
  2214. return Array.prototype[val].apply(arr, [].slice.call(arguments, 1));
  2215. };
  2216. });
  2217. /* eslint-disable valid-jsdoc */
  2218. /**
  2219. * Add an event listener.
  2220. *
  2221. * @function Highcharts.addEvent<T>
  2222. *
  2223. * @param {Highcharts.Class<T>|T} el
  2224. * The element or object to add a listener to. It can be a
  2225. * {@link HTMLDOMElement}, an {@link SVGElement} or any other object.
  2226. *
  2227. * @param {string} type
  2228. * The event type.
  2229. *
  2230. * @param {Highcharts.EventCallbackFunction<T>|Function} fn
  2231. * The function callback to execute when the event is fired.
  2232. *
  2233. * @param {Highcharts.EventOptionsObject} [options]
  2234. * Options for adding the event.
  2235. *
  2236. * @return {Function}
  2237. * A callback function to remove the added event.
  2238. */
  2239. var addEvent = H.addEvent = function (el, type, fn, options) {
  2240. if (options === void 0) { options = {}; }
  2241. /* eslint-enable valid-jsdoc */
  2242. var events, addEventListener = (el.addEventListener || H.addEventListenerPolyfill);
  2243. // If we're setting events directly on the constructor, use a separate
  2244. // collection, `protoEvents` to distinguish it from the item events in
  2245. // `hcEvents`.
  2246. if (typeof el === 'function' && el.prototype) {
  2247. events = el.prototype.protoEvents = el.prototype.protoEvents || {};
  2248. }
  2249. else {
  2250. events = el.hcEvents = el.hcEvents || {};
  2251. }
  2252. // Allow click events added to points, otherwise they will be prevented by
  2253. // the TouchPointer.pinch function after a pinch zoom operation (#7091).
  2254. if (H.Point &&
  2255. el instanceof H.Point &&
  2256. el.series &&
  2257. el.series.chart) {
  2258. el.series.chart.runTrackerClick = true;
  2259. }
  2260. // Handle DOM events
  2261. if (addEventListener) {
  2262. addEventListener.call(el, type, fn, false);
  2263. }
  2264. if (!events[type]) {
  2265. events[type] = [];
  2266. }
  2267. var eventObject = {
  2268. fn: fn,
  2269. order: typeof options.order === 'number' ? options.order : Infinity
  2270. };
  2271. events[type].push(eventObject);
  2272. // Order the calls
  2273. events[type].sort(function (a, b) {
  2274. return a.order - b.order;
  2275. });
  2276. // Return a function that can be called to remove this event.
  2277. return function () {
  2278. removeEvent(el, type, fn);
  2279. };
  2280. };
  2281. /* eslint-disable valid-jsdoc */
  2282. /**
  2283. * Remove an event that was added with {@link Highcharts#addEvent}.
  2284. *
  2285. * @function Highcharts.removeEvent<T>
  2286. *
  2287. * @param {Highcharts.Class<T>|T} el
  2288. * The element to remove events on.
  2289. *
  2290. * @param {string} [type]
  2291. * The type of events to remove. If undefined, all events are removed
  2292. * from the element.
  2293. *
  2294. * @param {Highcharts.EventCallbackFunction<T>} [fn]
  2295. * The specific callback to remove. If undefined, all events that match
  2296. * the element and optionally the type are removed.
  2297. *
  2298. * @return {void}
  2299. */
  2300. var removeEvent = H.removeEvent = function removeEvent(el, type, fn) {
  2301. /* eslint-enable valid-jsdoc */
  2302. var events;
  2303. /**
  2304. * @private
  2305. * @param {string} type - event type
  2306. * @param {Highcharts.EventCallbackFunction<T>} fn - callback
  2307. * @return {void}
  2308. */
  2309. function removeOneEvent(type, fn) {
  2310. var removeEventListener = (el.removeEventListener || H.removeEventListenerPolyfill);
  2311. if (removeEventListener) {
  2312. removeEventListener.call(el, type, fn, false);
  2313. }
  2314. }
  2315. /**
  2316. * @private
  2317. * @param {any} eventCollection - collection
  2318. * @return {void}
  2319. */
  2320. function removeAllEvents(eventCollection) {
  2321. var types, len;
  2322. if (!el.nodeName) {
  2323. return; // break on non-DOM events
  2324. }
  2325. if (type) {
  2326. types = {};
  2327. types[type] = true;
  2328. }
  2329. else {
  2330. types = eventCollection;
  2331. }
  2332. objectEach(types, function (_val, n) {
  2333. if (eventCollection[n]) {
  2334. len = eventCollection[n].length;
  2335. while (len--) {
  2336. removeOneEvent(n, eventCollection[n][len].fn);
  2337. }
  2338. }
  2339. });
  2340. }
  2341. ['protoEvents', 'hcEvents'].forEach(function (coll, i) {
  2342. var eventElem = i ? el : el.prototype;
  2343. var eventCollection = eventElem && eventElem[coll];
  2344. if (eventCollection) {
  2345. if (type) {
  2346. events = (eventCollection[type] || []);
  2347. if (fn) {
  2348. eventCollection[type] = events.filter(function (obj) {
  2349. return fn !== obj.fn;
  2350. });
  2351. removeOneEvent(type, fn);
  2352. }
  2353. else {
  2354. removeAllEvents(eventCollection);
  2355. eventCollection[type] = [];
  2356. }
  2357. }
  2358. else {
  2359. removeAllEvents(eventCollection);
  2360. eventElem[coll] = {};
  2361. }
  2362. }
  2363. });
  2364. };
  2365. /* eslint-disable valid-jsdoc */
  2366. /**
  2367. * Fire an event that was registered with {@link Highcharts#addEvent}.
  2368. *
  2369. * @function Highcharts.fireEvent<T>
  2370. *
  2371. * @param {T} el
  2372. * The object to fire the event on. It can be a {@link HTMLDOMElement},
  2373. * an {@link SVGElement} or any other object.
  2374. *
  2375. * @param {string} type
  2376. * The type of event.
  2377. *
  2378. * @param {Highcharts.Dictionary<*>|Event} [eventArguments]
  2379. * Custom event arguments that are passed on as an argument to the event
  2380. * handler.
  2381. *
  2382. * @param {Highcharts.EventCallbackFunction<T>|Function} [defaultFunction]
  2383. * The default function to execute if the other listeners haven't
  2384. * returned false.
  2385. *
  2386. * @return {void}
  2387. */
  2388. var fireEvent = H.fireEvent = function (el, type, eventArguments, defaultFunction) {
  2389. /* eslint-enable valid-jsdoc */
  2390. var e, i;
  2391. eventArguments = eventArguments || {};
  2392. if (doc.createEvent &&
  2393. (el.dispatchEvent || el.fireEvent)) {
  2394. e = doc.createEvent('Events');
  2395. e.initEvent(type, true, true);
  2396. extend(e, eventArguments);
  2397. if (el.dispatchEvent) {
  2398. el.dispatchEvent(e);
  2399. }
  2400. else {
  2401. el.fireEvent(type, e);
  2402. }
  2403. }
  2404. else {
  2405. if (!eventArguments.target) {
  2406. // We're running a custom event
  2407. extend(eventArguments, {
  2408. // Attach a simple preventDefault function to skip
  2409. // default handler if called. The built-in
  2410. // defaultPrevented property is not overwritable (#5112)
  2411. preventDefault: function () {
  2412. eventArguments.defaultPrevented = true;
  2413. },
  2414. // Setting target to native events fails with clicking
  2415. // the zoom-out button in Chrome.
  2416. target: el,
  2417. // If the type is not set, we're running a custom event
  2418. // (#2297). If it is set, we're running a browser event,
  2419. // and setting it will cause en error in IE8 (#2465).
  2420. type: type
  2421. });
  2422. }
  2423. var fireInOrder = function (protoEvents, hcEvents) {
  2424. if (protoEvents === void 0) { protoEvents = []; }
  2425. if (hcEvents === void 0) { hcEvents = []; }
  2426. var iA = 0;
  2427. var iB = 0;
  2428. var length = protoEvents.length + hcEvents.length;
  2429. for (i = 0; i < length; i++) {
  2430. var obj = (!protoEvents[iA] ?
  2431. hcEvents[iB++] :
  2432. !hcEvents[iB] ?
  2433. protoEvents[iA++] :
  2434. protoEvents[iA].order <= hcEvents[iB].order ?
  2435. protoEvents[iA++] :
  2436. hcEvents[iB++]);
  2437. // If the event handler return false, prevent the default
  2438. // handler from executing
  2439. if (obj.fn.call(el, eventArguments) === false) {
  2440. eventArguments.preventDefault();
  2441. }
  2442. }
  2443. };
  2444. fireInOrder(el.protoEvents && el.protoEvents[type], el.hcEvents && el.hcEvents[type]);
  2445. }
  2446. // Run the default if not prevented
  2447. if (defaultFunction && !eventArguments.defaultPrevented) {
  2448. defaultFunction.call(el, eventArguments);
  2449. }
  2450. };
  2451. /**
  2452. * The global animate method, which uses Fx to create individual animators.
  2453. *
  2454. * @function Highcharts.animate
  2455. *
  2456. * @param {Highcharts.HTMLDOMElement|Highcharts.SVGElement} el
  2457. * The element to animate.
  2458. *
  2459. * @param {Highcharts.CSSObject|Highcharts.SVGAttributes} params
  2460. * An object containing key-value pairs of the properties to animate.
  2461. * Supports numeric as pixel-based CSS properties for HTML objects and
  2462. * attributes for SVGElements.
  2463. *
  2464. * @param {Highcharts.AnimationOptionsObject} [opt]
  2465. * Animation options.
  2466. *
  2467. * @return {void}
  2468. */
  2469. var animate = H.animate = function (el, params, opt) {
  2470. var start, unit = '', end, fx, args;
  2471. if (!isObject(opt)) { // Number or undefined/null
  2472. args = arguments;
  2473. opt = {
  2474. duration: args[2],
  2475. easing: args[3],
  2476. complete: args[4]
  2477. };
  2478. }
  2479. if (!isNumber(opt.duration)) {
  2480. opt.duration = 400;
  2481. }
  2482. opt.easing = typeof opt.easing === 'function' ?
  2483. opt.easing :
  2484. (Math[opt.easing] || Math.easeInOutSine);
  2485. opt.curAnim = merge(params);
  2486. objectEach(params, function (val, prop) {
  2487. // Stop current running animation of this property
  2488. stop(el, prop);
  2489. fx = new Fx(el, opt, prop);
  2490. end = null;
  2491. if (prop === 'd' && isArray(params.d)) {
  2492. fx.paths = fx.initPath(el, el.pathArray, params.d);
  2493. fx.toD = params.d;
  2494. start = 0;
  2495. end = 1;
  2496. }
  2497. else if (el.attr) {
  2498. start = el.attr(prop);
  2499. }
  2500. else {
  2501. start = parseFloat(getStyle(el, prop)) || 0;
  2502. if (prop !== 'opacity') {
  2503. unit = 'px';
  2504. }
  2505. }
  2506. if (!end) {
  2507. end = val;
  2508. }
  2509. if (end && end.match && end.match('px')) {
  2510. end = end.replace(/px/g, ''); // #4351
  2511. }
  2512. fx.run(start, end, unit);
  2513. });
  2514. };
  2515. /**
  2516. * Factory to create new series prototypes.
  2517. *
  2518. * @function Highcharts.seriesType
  2519. *
  2520. * @param {string} type
  2521. * The series type name.
  2522. *
  2523. * @param {string} parent
  2524. * The parent series type name. Use `line` to inherit from the basic
  2525. * {@link Series} object.
  2526. *
  2527. * @param {Highcharts.SeriesOptionsType|Highcharts.Dictionary<*>} options
  2528. * The additional default options that are merged with the parent's
  2529. * options.
  2530. *
  2531. * @param {Highcharts.Dictionary<*>} [props]
  2532. * The properties (functions and primitives) to set on the new
  2533. * prototype.
  2534. *
  2535. * @param {Highcharts.Dictionary<*>} [pointProps]
  2536. * Members for a series-specific extension of the {@link Point}
  2537. * prototype if needed.
  2538. *
  2539. * @return {Highcharts.Series}
  2540. * The newly created prototype as extended from {@link Series} or its
  2541. * derivatives.
  2542. */
  2543. // docs: add to API + extending Highcharts
  2544. var seriesType = H.seriesType = function (type, parent, options, props, pointProps) {
  2545. var defaultOptions = getOptions(), seriesTypes = H.seriesTypes;
  2546. // Merge the options
  2547. defaultOptions.plotOptions[type] = merge(defaultOptions.plotOptions[parent], options);
  2548. // Create the class
  2549. seriesTypes[type] = extendClass(seriesTypes[parent] || function () { }, props);
  2550. seriesTypes[type].prototype.type = type;
  2551. // Create the point class if needed
  2552. if (pointProps) {
  2553. seriesTypes[type].prototype.pointClass =
  2554. extendClass(H.Point, pointProps);
  2555. }
  2556. return seriesTypes[type];
  2557. };
  2558. var serialMode;
  2559. /**
  2560. * Get a unique key for using in internal element id's and pointers. The key is
  2561. * composed of a random hash specific to this Highcharts instance, and a
  2562. * counter.
  2563. *
  2564. * @example
  2565. * var id = uniqueKey(); // => 'highcharts-x45f6hp-0'
  2566. *
  2567. * @function Highcharts.uniqueKey
  2568. *
  2569. * @return {string}
  2570. * A unique key.
  2571. */
  2572. var uniqueKey = H.uniqueKey = (function () {
  2573. var hash = Math.random().toString(36).substring(2, 9) + '-';
  2574. var id = 0;
  2575. return function () {
  2576. return 'highcharts-' + (serialMode ? '' : hash) + id++;
  2577. };
  2578. }());
  2579. /**
  2580. * Activates a serial mode for element IDs provided by
  2581. * {@link Highcharts.uniqueKey}. This mode can be used in automated tests, where
  2582. * a simple comparison of two rendered SVG graphics is needed.
  2583. *
  2584. * **Note:** This is only for testing purposes and will break functionality in
  2585. * webpages with multiple charts.
  2586. *
  2587. * @example
  2588. * if (
  2589. * process &&
  2590. * process.env.NODE_ENV === 'development'
  2591. * ) {
  2592. * Highcharts.useSerialIds(true);
  2593. * }
  2594. *
  2595. * @function Highcharts.useSerialIds
  2596. *
  2597. * @param {boolean} [mode]
  2598. * Changes the state of serial mode.
  2599. *
  2600. * @return {boolean|undefined}
  2601. * State of the serial mode.
  2602. */
  2603. var useSerialIds = H.useSerialIds = function (mode) {
  2604. return (serialMode = pick(mode, serialMode));
  2605. };
  2606. var isFunction = H.isFunction = function (obj) {
  2607. return typeof obj === 'function';
  2608. };
  2609. /**
  2610. * Get the updated default options. Until 3.0.7, merely exposing defaultOptions
  2611. * for outside modules wasn't enough because the setOptions method created a new
  2612. * object.
  2613. *
  2614. * @function Highcharts.getOptions
  2615. *
  2616. * @return {Highcharts.Options}
  2617. */
  2618. var getOptions = H.getOptions = function () {
  2619. return H.defaultOptions;
  2620. };
  2621. /**
  2622. * Merge the default options with custom options and return the new options
  2623. * structure. Commonly used for defining reusable templates.
  2624. *
  2625. * @sample highcharts/global/useutc-false Setting a global option
  2626. * @sample highcharts/members/setoptions Applying a global theme
  2627. *
  2628. * @function Highcharts.setOptions
  2629. *
  2630. * @param {Highcharts.Options} options
  2631. * The new custom chart options.
  2632. *
  2633. * @return {Highcharts.Options}
  2634. * Updated options.
  2635. */
  2636. var setOptions = H.setOptions = function (options) {
  2637. // Copy in the default options
  2638. H.defaultOptions = merge(true, H.defaultOptions, options);
  2639. // Update the time object
  2640. if (options.time || options.global) {
  2641. H.time.update(merge(H.defaultOptions.global, H.defaultOptions.time, options.global, options.time));
  2642. }
  2643. return H.defaultOptions;
  2644. };
  2645. // Register Highcharts as a plugin in jQuery
  2646. if (win.jQuery) {
  2647. /**
  2648. * Highcharts-extended JQuery.
  2649. *
  2650. * @external JQuery
  2651. */
  2652. /**
  2653. * Helper function to return the chart of the current JQuery selector
  2654. * element.
  2655. *
  2656. * @function external:JQuery#highcharts
  2657. *
  2658. * @return {Highcharts.Chart}
  2659. * The chart that is linked to the JQuery selector element.
  2660. */ /**
  2661. * Factory function to create a chart in the current JQuery selector
  2662. * element.
  2663. *
  2664. * @function external:JQuery#highcharts
  2665. *
  2666. * @param {'Chart'|'Map'|'StockChart'|string} [className]
  2667. * Name of the factory class in the Highcharts namespace.
  2668. *
  2669. * @param {Highcharts.Options} [options]
  2670. * The chart options structure.
  2671. *
  2672. * @param {Highcharts.ChartCallbackFunction} [callback]
  2673. * Function to run when the chart has loaded and and all external
  2674. * images are loaded. Defining a
  2675. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  2676. * handler is equivalent.
  2677. *
  2678. * @return {JQuery}
  2679. * The current JQuery selector.
  2680. */
  2681. win.jQuery.fn.highcharts = function () {
  2682. var args = [].slice.call(arguments);
  2683. if (this[0]) { // this[0] is the renderTo div
  2684. // Create the chart
  2685. if (args[0]) {
  2686. new H[ // eslint-disable-line computed-property-spacing, no-new
  2687. // Constructor defaults to Chart
  2688. isString(args[0]) ? args.shift() : 'Chart'](this[0], args[0], args[1]);
  2689. return this;
  2690. }
  2691. // When called without parameters or with the return argument,
  2692. // return an existing chart
  2693. return charts[attr(this[0], 'data-highcharts-chart')];
  2694. }
  2695. };
  2696. }
  2697. // TODO use named exports when supported.
  2698. var utilitiesModule = {
  2699. Fx: H.Fx,
  2700. addEvent: addEvent,
  2701. animate: animate,
  2702. animObject: animObject,
  2703. arrayMax: arrayMax,
  2704. arrayMin: arrayMin,
  2705. attr: attr,
  2706. clamp: clamp,
  2707. clearTimeout: internalClearTimeout,
  2708. correctFloat: correctFloat,
  2709. createElement: createElement,
  2710. css: css,
  2711. defined: defined,
  2712. destroyObjectProperties: destroyObjectProperties,
  2713. discardElement: discardElement,
  2714. erase: erase,
  2715. error: error,
  2716. extend: extend,
  2717. extendClass: extendClass,
  2718. find: find,
  2719. fireEvent: fireEvent,
  2720. format: format,
  2721. getMagnitude: getMagnitude,
  2722. getNestedProperty: getNestedProperty,
  2723. getOptions: getOptions,
  2724. getStyle: getStyle,
  2725. inArray: inArray,
  2726. isArray: isArray,
  2727. isClass: isClass,
  2728. isDOMElement: isDOMElement,
  2729. isFunction: isFunction,
  2730. isNumber: isNumber,
  2731. isObject: isObject,
  2732. isString: isString,
  2733. merge: merge,
  2734. normalizeTickInterval: normalizeTickInterval,
  2735. numberFormat: numberFormat,
  2736. objectEach: objectEach,
  2737. offset: offset,
  2738. pad: pad,
  2739. pick: pick,
  2740. pInt: pInt,
  2741. relativeLength: relativeLength,
  2742. removeEvent: removeEvent,
  2743. seriesType: seriesType,
  2744. setAnimation: setAnimation,
  2745. setOptions: setOptions,
  2746. splat: splat,
  2747. stableSort: stableSort,
  2748. stop: stop,
  2749. syncTimeout: syncTimeout,
  2750. timeUnits: timeUnits,
  2751. uniqueKey: uniqueKey,
  2752. useSerialIds: useSerialIds,
  2753. wrap: wrap
  2754. };
  2755. return utilitiesModule;
  2756. });
  2757. _registerModule(_modules, 'parts/Color.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  2758. /* *
  2759. *
  2760. * (c) 2010-2020 Torstein Honsi
  2761. *
  2762. * License: www.highcharts.com/license
  2763. *
  2764. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2765. *
  2766. * */
  2767. /**
  2768. * A valid color to be parsed and handled by Highcharts. Highcharts internally
  2769. * supports hex colors like `#ffffff`, rgb colors like `rgb(255,255,255)` and
  2770. * rgba colors like `rgba(255,255,255,1)`. Other colors may be supported by the
  2771. * browsers and displayed correctly, but Highcharts is not able to process them
  2772. * and apply concepts like opacity and brightening.
  2773. *
  2774. * @typedef {string} Highcharts.ColorString
  2775. */
  2776. /**
  2777. * A valid color type than can be parsed and handled by Highcharts. It can be a
  2778. * color string, a gradient object, or a pattern object.
  2779. *
  2780. * @typedef {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} Highcharts.ColorType
  2781. */
  2782. /**
  2783. * Gradient options instead of a solid color.
  2784. *
  2785. * @example
  2786. * // Linear gradient used as a color option
  2787. * color: {
  2788. * linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 },
  2789. * stops: [
  2790. * [0, '#003399'], // start
  2791. * [0.5, '#ffffff'], // middle
  2792. * [1, '#3366AA'] // end
  2793. * ]
  2794. * }
  2795. *
  2796. * @interface Highcharts.GradientColorObject
  2797. */ /**
  2798. * Holds an object that defines the start position and the end position relative
  2799. * to the shape.
  2800. * @name Highcharts.GradientColorObject#linearGradient
  2801. * @type {Highcharts.LinearGradientColorObject|undefined}
  2802. */ /**
  2803. * Holds an object that defines the center position and the radius.
  2804. * @name Highcharts.GradientColorObject#radialGradient
  2805. * @type {Highcharts.RadialGradientColorObject|undefined}
  2806. */ /**
  2807. * The first item in each tuple is the position in the gradient, where 0 is the
  2808. * start of the gradient and 1 is the end of the gradient. Multiple stops can be
  2809. * applied. The second item is the color for each stop. This color can also be
  2810. * given in the rgba format.
  2811. * @name Highcharts.GradientColorObject#stops
  2812. * @type {Array<Highcharts.GradientColorStopObject>}
  2813. */
  2814. /**
  2815. * Color stop tuple.
  2816. *
  2817. * @see Highcharts.GradientColorObject
  2818. *
  2819. * @interface Highcharts.GradientColorStopObject
  2820. */ /**
  2821. * @name Highcharts.GradientColorStopObject#0
  2822. * @type {number}
  2823. */ /**
  2824. * @name Highcharts.GradientColorStopObject#1
  2825. * @type {Highcharts.ColorString}
  2826. */ /**
  2827. * @name Highcharts.GradoentColorStopObject#color
  2828. * @type {Highcharts.Color|undefined}
  2829. */
  2830. /**
  2831. * Defines the start position and the end position for a gradient relative
  2832. * to the shape. Start position (x1, y1) and end position (x2, y2) are relative
  2833. * to the shape, where 0 means top/left and 1 is bottom/right.
  2834. *
  2835. * @interface Highcharts.LinearGradientColorObject
  2836. */ /**
  2837. * Start horizontal position of the gradient. Float ranges 0-1.
  2838. * @name Highcharts.LinearGradientColorObject#x1
  2839. * @type {number}
  2840. */ /**
  2841. * End horizontal position of the gradient. Float ranges 0-1.
  2842. * @name Highcharts.LinearGradientColorObject#x2
  2843. * @type {number}
  2844. */ /**
  2845. * Start vertical position of the gradient. Float ranges 0-1.
  2846. * @name Highcharts.LinearGradientColorObject#y1
  2847. * @type {number}
  2848. */ /**
  2849. * End vertical position of the gradient. Float ranges 0-1.
  2850. * @name Highcharts.LinearGradientColorObject#y2
  2851. * @type {number}
  2852. */
  2853. /**
  2854. * Defines the center position and the radius for a gradient.
  2855. *
  2856. * @interface Highcharts.RadialGradientColorObject
  2857. */ /**
  2858. * Center horizontal position relative to the shape. Float ranges 0-1.
  2859. * @name Highcharts.RadialGradientColorObject#cx
  2860. * @type {number}
  2861. */ /**
  2862. * Center vertical position relative to the shape. Float ranges 0-1.
  2863. * @name Highcharts.RadialGradientColorObject#cy
  2864. * @type {number}
  2865. */ /**
  2866. * Radius relative to the shape. Float ranges 0-1.
  2867. * @name Highcharts.RadialGradientColorObject#r
  2868. * @type {number}
  2869. */
  2870. var isNumber = U.isNumber, merge = U.merge, pInt = U.pInt;
  2871. /* eslint-disable no-invalid-this, valid-jsdoc */
  2872. /**
  2873. * Handle color operations. Some object methods are chainable.
  2874. *
  2875. * @class
  2876. * @name Highcharts.Color
  2877. *
  2878. * @param {Highcharts.ColorType} input
  2879. * The input color in either rbga or hex format
  2880. */
  2881. var Color = /** @class */ (function () {
  2882. /* *
  2883. *
  2884. * Constructors
  2885. *
  2886. * */
  2887. function Color(input) {
  2888. // Collection of parsers. This can be extended from the outside by pushing
  2889. // parsers to Highcharts.Color.prototype.parsers.
  2890. this.parsers = [{
  2891. // RGBA color
  2892. // eslint-disable-next-line max-len
  2893. 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*\)/,
  2894. parse: function (result) {
  2895. return [
  2896. pInt(result[1]),
  2897. pInt(result[2]),
  2898. pInt(result[3]),
  2899. parseFloat(result[4], 10)
  2900. ];
  2901. }
  2902. }, {
  2903. // RGB color
  2904. regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
  2905. parse: function (result) {
  2906. return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
  2907. }
  2908. }];
  2909. this.rgba = [];
  2910. // Backwards compatibility, allow instanciation without new (#13053)
  2911. if (!(this instanceof Color)) {
  2912. return new Color(input);
  2913. }
  2914. this.init(input);
  2915. }
  2916. /* *
  2917. *
  2918. * Static Functions
  2919. *
  2920. * */
  2921. /**
  2922. * Creates a color instance out of a color string or object.
  2923. *
  2924. * @function Highcharts.Color.parse
  2925. *
  2926. * @param {Highcharts.ColorType} input
  2927. * The input color in either rbga or hex format.
  2928. *
  2929. * @return {Highcharts.Color}
  2930. * Color instance.
  2931. */
  2932. Color.parse = function (input) {
  2933. return new Color(input);
  2934. };
  2935. /* *
  2936. *
  2937. * Functions
  2938. *
  2939. * */
  2940. /**
  2941. * Parse the input color to rgba array
  2942. *
  2943. * @private
  2944. * @function Highcharts.Color#init
  2945. *
  2946. * @param {Highcharts.ColorType} input
  2947. * The input color in either rbga or hex format
  2948. *
  2949. * @return {void}
  2950. */
  2951. Color.prototype.init = function (input) {
  2952. var result, rgba, i, parser, len;
  2953. this.input = input = Color.names[input && input.toLowerCase ?
  2954. input.toLowerCase() :
  2955. ''] || input;
  2956. // Gradients
  2957. if (input && input.stops) {
  2958. this.stops = input.stops.map(function (stop) {
  2959. return new Color(stop[1]);
  2960. });
  2961. // Solid colors
  2962. }
  2963. else {
  2964. // Bitmasking as input[0] is not working for legacy IE.
  2965. if (input &&
  2966. input.charAt &&
  2967. input.charAt() === '#') {
  2968. len = input.length;
  2969. input = parseInt(input.substr(1), 16);
  2970. // Handle long-form, e.g. #AABBCC
  2971. if (len === 7) {
  2972. rgba = [
  2973. (input & 0xFF0000) >> 16,
  2974. (input & 0xFF00) >> 8,
  2975. (input & 0xFF),
  2976. 1
  2977. ];
  2978. // Handle short-form, e.g. #ABC
  2979. // In short form, the value is assumed to be the same
  2980. // for both nibbles for each component. e.g. #ABC = #AABBCC
  2981. }
  2982. else if (len === 4) {
  2983. rgba = [
  2984. (((input & 0xF00) >> 4) |
  2985. (input & 0xF00) >> 8),
  2986. (((input & 0xF0) >> 4) |
  2987. (input & 0xF0)),
  2988. ((input & 0xF) << 4) | (input & 0xF),
  2989. 1
  2990. ];
  2991. }
  2992. }
  2993. // Otherwise, check regex parsers
  2994. if (!rgba) {
  2995. i = this.parsers.length;
  2996. while (i-- && !rgba) {
  2997. parser = this.parsers[i];
  2998. result = parser.regex.exec(input);
  2999. if (result) {
  3000. rgba = parser.parse(result);
  3001. }
  3002. }
  3003. }
  3004. }
  3005. this.rgba = rgba || [];
  3006. };
  3007. /**
  3008. * Return the color or gradient stops in the specified format
  3009. *
  3010. * @function Highcharts.Color#get
  3011. *
  3012. * @param {string} [format]
  3013. * Possible values are 'a', 'rgb', 'rgba' (default).
  3014. *
  3015. * @return {Highcharts.ColorType}
  3016. * This color as a string or gradient stops.
  3017. */
  3018. Color.prototype.get = function (format) {
  3019. var input = this.input, rgba = this.rgba, ret;
  3020. if (typeof this.stops !== 'undefined') {
  3021. ret = merge(input);
  3022. ret.stops = [].concat(ret.stops);
  3023. this.stops.forEach(function (stop, i) {
  3024. ret.stops[i] = [
  3025. ret.stops[i][0],
  3026. stop.get(format)
  3027. ];
  3028. });
  3029. // it's NaN if gradient colors on a column chart
  3030. }
  3031. else if (rgba && isNumber(rgba[0])) {
  3032. if (format === 'rgb' || (!format && rgba[3] === 1)) {
  3033. ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
  3034. }
  3035. else if (format === 'a') {
  3036. ret = rgba[3];
  3037. }
  3038. else {
  3039. ret = 'rgba(' + rgba.join(',') + ')';
  3040. }
  3041. }
  3042. else {
  3043. ret = input;
  3044. }
  3045. return ret;
  3046. };
  3047. /**
  3048. * Brighten the color instance.
  3049. *
  3050. * @function Highcharts.Color#brighten
  3051. *
  3052. * @param {number} alpha
  3053. * The alpha value.
  3054. *
  3055. * @return {Highcharts.Color}
  3056. * This color with modifications.
  3057. */
  3058. Color.prototype.brighten = function (alpha) {
  3059. var i, rgba = this.rgba;
  3060. if (this.stops) {
  3061. this.stops.forEach(function (stop) {
  3062. stop.brighten(alpha);
  3063. });
  3064. }
  3065. else if (isNumber(alpha) && alpha !== 0) {
  3066. for (i = 0; i < 3; i++) {
  3067. rgba[i] += pInt(alpha * 255);
  3068. if (rgba[i] < 0) {
  3069. rgba[i] = 0;
  3070. }
  3071. if (rgba[i] > 255) {
  3072. rgba[i] = 255;
  3073. }
  3074. }
  3075. }
  3076. return this;
  3077. };
  3078. /**
  3079. * Set the color's opacity to a given alpha value.
  3080. *
  3081. * @function Highcharts.Color#setOpacity
  3082. *
  3083. * @param {number} alpha
  3084. * Opacity between 0 and 1.
  3085. *
  3086. * @return {Highcharts.Color}
  3087. * Color with modifications.
  3088. */
  3089. Color.prototype.setOpacity = function (alpha) {
  3090. this.rgba[3] = alpha;
  3091. return this;
  3092. };
  3093. /**
  3094. * Return an intermediate color between two colors.
  3095. *
  3096. * @function Highcharts.Color#tweenTo
  3097. *
  3098. * @param {Highcharts.Color} to
  3099. * The color object to tween to.
  3100. *
  3101. * @param {number} pos
  3102. * The intermediate position, where 0 is the from color (current
  3103. * color item), and 1 is the `to` color.
  3104. *
  3105. * @return {Highcharts.ColorString}
  3106. * The intermediate color in rgba notation.
  3107. */
  3108. Color.prototype.tweenTo = function (to, pos) {
  3109. // Check for has alpha, because rgba colors perform worse due to lack of
  3110. // support in WebKit.
  3111. var fromRgba = this.rgba, toRgba = to.rgba, hasAlpha, ret;
  3112. // Unsupported color, return to-color (#3920, #7034)
  3113. if (!toRgba.length || !fromRgba || !fromRgba.length) {
  3114. ret = to.input || 'none';
  3115. // Interpolate
  3116. }
  3117. else {
  3118. hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
  3119. ret = (hasAlpha ? 'rgba(' : 'rgb(') +
  3120. Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
  3121. ',' +
  3122. Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
  3123. ',' +
  3124. Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
  3125. (hasAlpha ?
  3126. (',' +
  3127. (toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))) :
  3128. '') +
  3129. ')';
  3130. }
  3131. return ret;
  3132. };
  3133. /* *
  3134. *
  3135. * Static Properties
  3136. *
  3137. * */
  3138. // Collection of named colors. Can be extended from the outside by adding
  3139. // colors to Highcharts.Color.names.
  3140. Color.names = {
  3141. white: '#ffffff',
  3142. black: '#000000'
  3143. };
  3144. return Color;
  3145. }());
  3146. H.Color = Color;
  3147. /**
  3148. * Creates a color instance out of a color string.
  3149. *
  3150. * @function Highcharts.color
  3151. *
  3152. * @param {Highcharts.ColorType} input
  3153. * The input color in either rbga or hex format
  3154. *
  3155. * @return {Highcharts.Color}
  3156. * Color instance
  3157. */
  3158. H.color = Color.parse;
  3159. return H.Color;
  3160. });
  3161. _registerModule(_modules, 'parts/SVGElement.js', [_modules['parts/Color.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Color, H, U) {
  3162. /* *
  3163. *
  3164. * (c) 2010-2020 Torstein Honsi
  3165. *
  3166. * License: www.highcharts.com/license
  3167. *
  3168. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3169. *
  3170. * */
  3171. var deg2rad = H.deg2rad, doc = H.doc, hasTouch = H.hasTouch, isFirefox = H.isFirefox, noop = H.noop, svg = H.svg, SVG_NS = H.SVG_NS, win = H.win;
  3172. var animate = U.animate, animObject = U.animObject, attr = U.attr, createElement = U.createElement, css = U.css, defined = U.defined, erase = U.erase, extend = U.extend, fireEvent = U.fireEvent, inArray = U.inArray, isArray = U.isArray, isFunction = U.isFunction, isNumber = U.isNumber, isString = U.isString, merge = U.merge, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, stop = U.stop, uniqueKey = U.uniqueKey;
  3173. /**
  3174. * The horizontal alignment of an element.
  3175. *
  3176. * @typedef {"center"|"left"|"right"} Highcharts.AlignValue
  3177. */
  3178. /**
  3179. * Options to align the element relative to the chart or another box.
  3180. *
  3181. * @interface Highcharts.AlignObject
  3182. */ /**
  3183. * Horizontal alignment. Can be one of `left`, `center` and `right`.
  3184. *
  3185. * @name Highcharts.AlignObject#align
  3186. * @type {Highcharts.AlignValue|undefined}
  3187. *
  3188. * @default left
  3189. */ /**
  3190. * Vertical alignment. Can be one of `top`, `middle` and `bottom`.
  3191. *
  3192. * @name Highcharts.AlignObject#verticalAlign
  3193. * @type {Highcharts.VerticalAlignValue|undefined}
  3194. *
  3195. * @default top
  3196. */ /**
  3197. * Horizontal pixel offset from alignment.
  3198. *
  3199. * @name Highcharts.AlignObject#x
  3200. * @type {number|undefined}
  3201. *
  3202. * @default 0
  3203. */ /**
  3204. * Vertical pixel offset from alignment.
  3205. *
  3206. * @name Highcharts.AlignObject#y
  3207. * @type {number|undefined}
  3208. *
  3209. * @default 0
  3210. */ /**
  3211. * Use the `transform` attribute with translateX and translateY custom
  3212. * attributes to align this elements rather than `x` and `y` attributes.
  3213. *
  3214. * @name Highcharts.AlignObject#alignByTranslate
  3215. * @type {boolean|undefined}
  3216. *
  3217. * @default false
  3218. */
  3219. /**
  3220. * Bounding box of an element.
  3221. *
  3222. * @interface Highcharts.BBoxObject
  3223. * @extends Highcharts.PositionObject
  3224. */ /**
  3225. * Height of the bounding box.
  3226. *
  3227. * @name Highcharts.BBoxObject#height
  3228. * @type {number}
  3229. */ /**
  3230. * Width of the bounding box.
  3231. *
  3232. * @name Highcharts.BBoxObject#width
  3233. * @type {number}
  3234. */ /**
  3235. * Horizontal position of the bounding box.
  3236. *
  3237. * @name Highcharts.BBoxObject#x
  3238. * @type {number}
  3239. */ /**
  3240. * Vertical position of the bounding box.
  3241. *
  3242. * @name Highcharts.BBoxObject#y
  3243. * @type {number}
  3244. */
  3245. /**
  3246. * An object of key-value pairs for SVG attributes. Attributes in Highcharts
  3247. * elements for the most parts correspond to SVG, but some are specific to
  3248. * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
  3249. * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
  3250. * attributes containing a hyphen are _not_ camel-cased, they should be
  3251. * quoted to preserve the hyphen.
  3252. *
  3253. * @example
  3254. * {
  3255. * 'stroke': '#ff0000', // basic
  3256. * 'stroke-width': 2, // hyphenated
  3257. * 'rotation': 45 // custom
  3258. * 'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
  3259. * }
  3260. *
  3261. * @interface Highcharts.SVGAttributes
  3262. */ /**
  3263. * @name Highcharts.SVGAttributes#[key:string]
  3264. * @type {*}
  3265. */ /**
  3266. * @name Highcharts.SVGAttributes#d
  3267. * @type {string|Highcharts.SVGPathArray|undefined}
  3268. */ /**
  3269. * @name Highcharts.SVGAttributes#fill
  3270. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  3271. */ /**
  3272. * @name Highcharts.SVGAttributes#inverted
  3273. * @type {boolean|undefined}
  3274. */ /**
  3275. * @name Highcharts.SVGAttributes#matrix
  3276. * @type {Array<number>|undefined}
  3277. */ /**
  3278. * @name Highcharts.SVGAttributes#rotation
  3279. * @type {number|undefined}
  3280. */ /**
  3281. * @name Highcharts.SVGAttributes#rotationOriginX
  3282. * @type {number|undefined}
  3283. */ /**
  3284. * @name Highcharts.SVGAttributes#rotationOriginY
  3285. * @type {number|undefined}
  3286. */ /**
  3287. * @name Highcharts.SVGAttributes#scaleX
  3288. * @type {number|undefined}
  3289. */ /**
  3290. * @name Highcharts.SVGAttributes#scaleY
  3291. * @type {number|undefined}
  3292. */ /**
  3293. * @name Highcharts.SVGAttributes#stroke
  3294. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  3295. */ /**
  3296. * @name Highcharts.SVGAttributes#style
  3297. * @type {string|Highcharts.CSSObject|undefined}
  3298. */ /**
  3299. * @name Highcharts.SVGAttributes#translateX
  3300. * @type {number|undefined}
  3301. */ /**
  3302. * @name Highcharts.SVGAttributes#translateY
  3303. * @type {number|undefined}
  3304. */ /**
  3305. * @name Highcharts.SVGAttributes#zIndex
  3306. * @type {number|undefined}
  3307. */
  3308. /**
  3309. * An SVG DOM element. The type is a reference to the regular SVGElement in the
  3310. * global scope.
  3311. *
  3312. * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
  3313. *
  3314. * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
  3315. */
  3316. /**
  3317. * The vertical alignment of an element.
  3318. *
  3319. * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignValue
  3320. */
  3321. ''; // detach doclets above
  3322. /* eslint-disable no-invalid-this, valid-jsdoc */
  3323. /**
  3324. * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
  3325. * rendering layer of Highcharts. Combined with the
  3326. * {@link Highcharts.SVGRenderer}
  3327. * object, these prototypes allow freeform annotation in the charts or even in
  3328. * HTML pages without instanciating a chart. The SVGElement can also wrap HTML
  3329. * labels, when `text` or `label` elements are created with the `useHTML`
  3330. * parameter.
  3331. *
  3332. * The SVGElement instances are created through factory functions on the
  3333. * {@link Highcharts.SVGRenderer}
  3334. * object, like
  3335. * {@link Highcharts.SVGRenderer#rect|rect},
  3336. * {@link Highcharts.SVGRenderer#path|path},
  3337. * {@link Highcharts.SVGRenderer#text|text},
  3338. * {@link Highcharts.SVGRenderer#label|label},
  3339. * {@link Highcharts.SVGRenderer#g|g}
  3340. * and more.
  3341. *
  3342. * @class
  3343. * @name Highcharts.SVGElement
  3344. */
  3345. var SVGElement = /** @class */ (function () {
  3346. function SVGElement() {
  3347. /* *
  3348. *
  3349. * Properties
  3350. *
  3351. * */
  3352. this.element = void 0;
  3353. this.height = void 0;
  3354. this.opacity = 1; // Default base for animation
  3355. this.renderer = void 0;
  3356. this.SVG_NS = SVG_NS;
  3357. // Custom attributes used for symbols, these should be filtered out when
  3358. // setting SVGElement attributes (#9375).
  3359. this.symbolCustomAttribs = [
  3360. 'x',
  3361. 'y',
  3362. 'width',
  3363. 'height',
  3364. 'r',
  3365. 'start',
  3366. 'end',
  3367. 'innerR',
  3368. 'anchorX',
  3369. 'anchorY',
  3370. 'rounded'
  3371. ];
  3372. this.width = void 0;
  3373. }
  3374. /* *
  3375. *
  3376. * Functions
  3377. *
  3378. * */
  3379. /**
  3380. * Get the current value of an attribute or pseudo attribute,
  3381. * used mainly for animation. Called internally from
  3382. * the {@link Highcharts.SVGRenderer#attr} function.
  3383. *
  3384. * @private
  3385. * @function Highcharts.SVGElement#_defaultGetter
  3386. *
  3387. * @param {string} key
  3388. * Property key.
  3389. *
  3390. * @return {number|string}
  3391. * Property value.
  3392. */
  3393. SVGElement.prototype._defaultGetter = function (key) {
  3394. var ret = pick(this[key + 'Value'], // align getter
  3395. this[key], this.element ? this.element.getAttribute(key) : null, 0);
  3396. if (/^[\-0-9\.]+$/.test(ret)) { // is numerical
  3397. ret = parseFloat(ret);
  3398. }
  3399. return ret;
  3400. };
  3401. /**
  3402. * @private
  3403. * @function Highcharts.SVGElement#_defaultSetter
  3404. *
  3405. * @param {string} value
  3406. *
  3407. * @param {string} key
  3408. *
  3409. * @param {Highcharts.SVGDOMElement} element
  3410. *
  3411. * @return {void}
  3412. */
  3413. SVGElement.prototype._defaultSetter = function (value, key, element) {
  3414. element.setAttribute(key, value);
  3415. };
  3416. /**
  3417. * Add the element to the DOM. All elements must be added this way.
  3418. *
  3419. * @sample highcharts/members/renderer-g
  3420. * Elements added to a group
  3421. *
  3422. * @function Highcharts.SVGElement#add
  3423. *
  3424. * @param {Highcharts.SVGElement} [parent]
  3425. * The parent item to add it to. If undefined, the element is added
  3426. * to the {@link Highcharts.SVGRenderer.box}.
  3427. *
  3428. * @return {Highcharts.SVGElement}
  3429. * Returns the SVGElement for chaining.
  3430. */
  3431. SVGElement.prototype.add = function (parent) {
  3432. var renderer = this.renderer, element = this.element, inserted;
  3433. if (parent) {
  3434. this.parentGroup = parent;
  3435. }
  3436. // Mark as inverted
  3437. this.parentInverted = parent && parent.inverted;
  3438. // Build formatted text
  3439. if (typeof this.textStr !== 'undefined' &&
  3440. this.element.nodeName === 'text' // Not for SVGLabel instances
  3441. ) {
  3442. renderer.buildText(this);
  3443. }
  3444. // Mark as added
  3445. this.added = true;
  3446. // If we're adding to renderer root, or other elements in the group
  3447. // have a z index, we need to handle it
  3448. if (!parent || parent.handleZ || this.zIndex) {
  3449. inserted = this.zIndexSetter();
  3450. }
  3451. // If zIndex is not handled, append at the end
  3452. if (!inserted) {
  3453. (parent ?
  3454. parent.element :
  3455. renderer.box).appendChild(element);
  3456. }
  3457. // fire an event for internal hooks
  3458. if (this.onAdd) {
  3459. this.onAdd();
  3460. }
  3461. return this;
  3462. };
  3463. /**
  3464. * Add a class name to an element.
  3465. *
  3466. * @function Highcharts.SVGElement#addClass
  3467. *
  3468. * @param {string} className
  3469. * The new class name to add.
  3470. *
  3471. * @param {boolean} [replace=false]
  3472. * When true, the existing class name(s) will be overwritten with the new
  3473. * one. When false, the new one is added.
  3474. *
  3475. * @return {Highcharts.SVGElement}
  3476. * Return the SVG element for chainability.
  3477. */
  3478. SVGElement.prototype.addClass = function (className, replace) {
  3479. var currentClassName = replace ? '' : (this.attr('class') || '');
  3480. // Trim the string and remove duplicates
  3481. className = (className || '')
  3482. .split(/ /g)
  3483. .reduce(function (newClassName, name) {
  3484. if (currentClassName.indexOf(name) === -1) {
  3485. newClassName.push(name);
  3486. }
  3487. return newClassName;
  3488. }, (currentClassName ?
  3489. [currentClassName] :
  3490. []))
  3491. .join(' ');
  3492. if (className !== currentClassName) {
  3493. this.attr('class', className);
  3494. }
  3495. return this;
  3496. };
  3497. /**
  3498. * This method is executed in the end of `attr()`, after setting all
  3499. * attributes in the hash. In can be used to efficiently consolidate
  3500. * multiple attributes in one SVG property -- e.g., translate, rotate and
  3501. * scale are merged in one "transform" attribute in the SVG node.
  3502. *
  3503. * @private
  3504. * @function Highcharts.SVGElement#afterSetters
  3505. */
  3506. SVGElement.prototype.afterSetters = function () {
  3507. // Update transform. Do this outside the loop to prevent redundant
  3508. // updating for batch setting of attributes.
  3509. if (this.doTransform) {
  3510. this.updateTransform();
  3511. this.doTransform = false;
  3512. }
  3513. };
  3514. /**
  3515. * Align the element relative to the chart or another box.
  3516. *
  3517. * @function Highcharts.SVGElement#align
  3518. *
  3519. * @param {Highcharts.AlignObject} [alignOptions]
  3520. * The alignment options. The function can be called without this
  3521. * parameter in order to re-align an element after the box has been
  3522. * updated.
  3523. *
  3524. * @param {boolean} [alignByTranslate]
  3525. * Align element by translation.
  3526. *
  3527. * @param {string|Highcharts.BBoxObject} [box]
  3528. * The box to align to, needs a width and height. When the box is a
  3529. * string, it refers to an object in the Renderer. For example, when
  3530. * box is `spacingBox`, it refers to `Renderer.spacingBox` which
  3531. * holds `width`, `height`, `x` and `y` properties.
  3532. *
  3533. * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
  3534. */
  3535. SVGElement.prototype.align = function (alignOptions, alignByTranslate, box) {
  3536. var align, vAlign, x, y, attribs = {}, alignTo, renderer = this.renderer, alignedObjects = renderer.alignedObjects, alignFactor, vAlignFactor;
  3537. // First call on instanciate
  3538. if (alignOptions) {
  3539. this.alignOptions = alignOptions;
  3540. this.alignByTranslate = alignByTranslate;
  3541. if (!box || isString(box)) {
  3542. this.alignTo = alignTo = box || 'renderer';
  3543. // prevent duplicates, like legendGroup after resize
  3544. erase(alignedObjects, this);
  3545. alignedObjects.push(this);
  3546. box = void 0; // reassign it below
  3547. }
  3548. // When called on resize, no arguments are supplied
  3549. }
  3550. else {
  3551. alignOptions = this.alignOptions;
  3552. alignByTranslate = this.alignByTranslate;
  3553. alignTo = this.alignTo;
  3554. }
  3555. box = pick(box, renderer[alignTo], renderer);
  3556. // Assign variables
  3557. align = alignOptions.align;
  3558. vAlign = alignOptions.verticalAlign;
  3559. // default: left align
  3560. x = (box.x || 0) + (alignOptions.x || 0);
  3561. // default: top align
  3562. y = (box.y || 0) + (alignOptions.y || 0);
  3563. // Align
  3564. if (align === 'right') {
  3565. alignFactor = 1;
  3566. }
  3567. else if (align === 'center') {
  3568. alignFactor = 2;
  3569. }
  3570. if (alignFactor) {
  3571. x += (box.width - (alignOptions.width || 0)) /
  3572. alignFactor;
  3573. }
  3574. attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
  3575. // Vertical align
  3576. if (vAlign === 'bottom') {
  3577. vAlignFactor = 1;
  3578. }
  3579. else if (vAlign === 'middle') {
  3580. vAlignFactor = 2;
  3581. }
  3582. if (vAlignFactor) {
  3583. y += (box.height - (alignOptions.height || 0)) /
  3584. vAlignFactor;
  3585. }
  3586. attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
  3587. // Animate only if already placed
  3588. this[this.placed ? 'animate' : 'attr'](attribs);
  3589. this.placed = true;
  3590. this.alignAttr = attribs;
  3591. return this;
  3592. };
  3593. /**
  3594. * @private
  3595. * @function Highcharts.SVGElement#alignSetter
  3596. * @param {"left"|"center"|"right"} value
  3597. */
  3598. SVGElement.prototype.alignSetter = function (value) {
  3599. var convert = {
  3600. left: 'start',
  3601. center: 'middle',
  3602. right: 'end'
  3603. };
  3604. if (convert[value]) {
  3605. this.alignValue = value;
  3606. this.element.setAttribute('text-anchor', convert[value]);
  3607. }
  3608. };
  3609. /**
  3610. * Animate to given attributes or CSS properties.
  3611. *
  3612. * @sample highcharts/members/element-on/
  3613. * Setting some attributes by animation
  3614. *
  3615. * @function Highcharts.SVGElement#animate
  3616. *
  3617. * @param {Highcharts.SVGAttributes} params
  3618. * SVG attributes or CSS to animate.
  3619. *
  3620. * @param {boolean|Highcharts.AnimationOptionsObject} [options]
  3621. * Animation options.
  3622. *
  3623. * @param {Function} [complete]
  3624. * Function to perform at the end of animation.
  3625. *
  3626. * @return {Highcharts.SVGElement}
  3627. * Returns the SVGElement for chaining.
  3628. */
  3629. SVGElement.prototype.animate = function (params, options, complete) {
  3630. var animOptions = animObject(pick(options, this.renderer.globalAnimation, true));
  3631. // When the page is hidden save resources in the background by not
  3632. // running animation at all (#9749).
  3633. if (pick(doc.hidden, doc.msHidden, doc.webkitHidden, false)) {
  3634. animOptions.duration = 0;
  3635. }
  3636. if (animOptions.duration !== 0) {
  3637. // allows using a callback with the global animation without
  3638. // overwriting it
  3639. if (complete) {
  3640. animOptions.complete = complete;
  3641. }
  3642. animate(this, params, animOptions);
  3643. }
  3644. else {
  3645. this.attr(params, void 0, complete);
  3646. // Call the end step synchronously
  3647. objectEach(params, function (val, prop) {
  3648. if (animOptions.step) {
  3649. animOptions.step.call(this, val, { prop: prop, pos: 1 });
  3650. }
  3651. }, this);
  3652. }
  3653. return this;
  3654. };
  3655. /**
  3656. * Apply a text outline through a custom CSS property, by copying the text
  3657. * element and apply stroke to the copy. Used internally. Contrast checks at
  3658. * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
  3659. *
  3660. * @example
  3661. * // Specific color
  3662. * text.css({
  3663. * textOutline: '1px black'
  3664. * });
  3665. * // Automatic contrast
  3666. * text.css({
  3667. * color: '#000000', // black text
  3668. * textOutline: '1px contrast' // => white outline
  3669. * });
  3670. *
  3671. * @private
  3672. * @function Highcharts.SVGElement#applyTextOutline
  3673. *
  3674. * @param {string} textOutline
  3675. * A custom CSS `text-outline` setting, defined by `width color`.
  3676. */
  3677. SVGElement.prototype.applyTextOutline = function (textOutline) {
  3678. var elem = this.element, tspans, hasContrast = textOutline.indexOf('contrast') !== -1, styles = {}, color, strokeWidth, firstRealChild;
  3679. // When the text shadow is set to contrast, use dark stroke for light
  3680. // text and vice versa.
  3681. if (hasContrast) {
  3682. styles.textOutline = textOutline = textOutline.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
  3683. }
  3684. // Extract the stroke width and color
  3685. textOutline = textOutline.split(' ');
  3686. color = textOutline[textOutline.length - 1];
  3687. strokeWidth = textOutline[0];
  3688. if (strokeWidth && strokeWidth !== 'none' && H.svg) {
  3689. this.fakeTS = true; // Fake text shadow
  3690. tspans = [].slice.call(elem.getElementsByTagName('tspan'));
  3691. // In order to get the right y position of the clone,
  3692. // copy over the y setter
  3693. this.ySetter = this.xSetter;
  3694. // Since the stroke is applied on center of the actual outline, we
  3695. // need to double it to get the correct stroke-width outside the
  3696. // glyphs.
  3697. strokeWidth = strokeWidth.replace(/(^[\d\.]+)(.*?)$/g, function (match, digit, unit) {
  3698. return (2 * digit) + unit;
  3699. });
  3700. // Remove shadows from previous runs.
  3701. this.removeTextOutline(tspans);
  3702. // Check if the element contains RTL characters.
  3703. // Comparing against Hebrew and Arabic characters,
  3704. // excluding Arabic digits. Source:
  3705. // https://www.unicode.org/Public/UNIDATA/extracted/DerivedBidiClass.txt
  3706. var isRTL_1 = elem.textContent ?
  3707. /^[\u0591-\u065F\u066A-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/
  3708. .test(elem.textContent) : false;
  3709. // For each of the tspans, create a stroked copy behind it.
  3710. firstRealChild = elem.firstChild;
  3711. tspans.forEach(function (tspan, y) {
  3712. var clone;
  3713. // Let the first line start at the correct X position
  3714. if (y === 0) {
  3715. tspan.setAttribute('x', elem.getAttribute('x'));
  3716. y = elem.getAttribute('y');
  3717. tspan.setAttribute('y', y || 0);
  3718. if (y === null) {
  3719. elem.setAttribute('y', 0);
  3720. }
  3721. }
  3722. // Create the clone and apply outline properties.
  3723. // For RTL elements apply outline properties for orginal element
  3724. // to prevent outline from overlapping the text.
  3725. // For RTL in Firefox keep the orginal order (#10162).
  3726. clone = tspan.cloneNode(true);
  3727. attr((isRTL_1 && !isFirefox) ? tspan : clone, {
  3728. 'class': 'highcharts-text-outline',
  3729. fill: color,
  3730. stroke: color,
  3731. 'stroke-width': strokeWidth,
  3732. 'stroke-linejoin': 'round'
  3733. });
  3734. elem.insertBefore(clone, firstRealChild);
  3735. });
  3736. // Create a whitespace between tspan and clone,
  3737. // to fix the display of Arabic characters in Firefox.
  3738. if (isRTL_1 && isFirefox && tspans[0]) {
  3739. var whitespace = tspans[0].cloneNode(true);
  3740. whitespace.textContent = ' ';
  3741. elem.insertBefore(whitespace, firstRealChild);
  3742. }
  3743. }
  3744. };
  3745. /**
  3746. * @function Highcharts.SVGElement#attr
  3747. * @param {string} key
  3748. * @return {number|string}
  3749. */ /**
  3750. * Apply native and custom attributes to the SVG elements.
  3751. *
  3752. * In order to set the rotation center for rotation, set x and y to 0 and
  3753. * use `translateX` and `translateY` attributes to position the element
  3754. * instead.
  3755. *
  3756. * Attributes frequently used in Highcharts are `fill`, `stroke`,
  3757. * `stroke-width`.
  3758. *
  3759. * @sample highcharts/members/renderer-rect/
  3760. * Setting some attributes
  3761. *
  3762. * @example
  3763. * // Set multiple attributes
  3764. * element.attr({
  3765. * stroke: 'red',
  3766. * fill: 'blue',
  3767. * x: 10,
  3768. * y: 10
  3769. * });
  3770. *
  3771. * // Set a single attribute
  3772. * element.attr('stroke', 'red');
  3773. *
  3774. * // Get an attribute
  3775. * element.attr('stroke'); // => 'red'
  3776. *
  3777. * @function Highcharts.SVGElement#attr
  3778. *
  3779. * @param {string|Highcharts.SVGAttributes} [hash]
  3780. * The native and custom SVG attributes.
  3781. *
  3782. * @param {number|string|Highcharts.SVGPathArray} [val]
  3783. * If the type of the first argument is `string`, the second can be a
  3784. * value, which will serve as a single attribute setter. If the first
  3785. * argument is a string and the second is undefined, the function
  3786. * serves as a getter and the current value of the property is
  3787. * returned.
  3788. *
  3789. * @param {Function} [complete]
  3790. * A callback function to execute after setting the attributes. This
  3791. * makes the function compliant and interchangeable with the
  3792. * {@link SVGElement#animate} function.
  3793. *
  3794. * @param {boolean} [continueAnimation=true]
  3795. * Used internally when `.attr` is called as part of an animation
  3796. * step. Otherwise, calling `.attr` for an attribute will stop
  3797. * animation for that attribute.
  3798. *
  3799. * @return {Highcharts.SVGElement}
  3800. * If used as a setter, it returns the current
  3801. * {@link Highcharts.SVGElement} so the calls can be chained. If
  3802. * used as a getter, the current value of the attribute is returned.
  3803. */
  3804. SVGElement.prototype.attr = function (hash, val, complete, continueAnimation) {
  3805. var key, element = this.element, hasSetSymbolSize, ret = this, skipAttr, setter, symbolCustomAttribs = this.symbolCustomAttribs;
  3806. // single key-value pair
  3807. if (typeof hash === 'string' && typeof val !== 'undefined') {
  3808. key = hash;
  3809. hash = {};
  3810. hash[key] = val;
  3811. }
  3812. // used as a getter: first argument is a string, second is undefined
  3813. if (typeof hash === 'string') {
  3814. ret = (this[hash + 'Getter'] ||
  3815. this._defaultGetter).call(this, hash, element);
  3816. // setter
  3817. }
  3818. else {
  3819. objectEach(hash, function eachAttribute(val, key) {
  3820. skipAttr = false;
  3821. // Unless .attr is from the animator update, stop current
  3822. // running animation of this property
  3823. if (!continueAnimation) {
  3824. stop(this, key);
  3825. }
  3826. // Special handling of symbol attributes
  3827. if (this.symbolName &&
  3828. symbolCustomAttribs.indexOf(key) !== -1) {
  3829. if (!hasSetSymbolSize) {
  3830. this.symbolAttr(hash);
  3831. hasSetSymbolSize = true;
  3832. }
  3833. skipAttr = true;
  3834. }
  3835. if (this.rotation && (key === 'x' || key === 'y')) {
  3836. this.doTransform = true;
  3837. }
  3838. if (!skipAttr) {
  3839. setter = (this[key + 'Setter'] ||
  3840. this._defaultSetter);
  3841. setter.call(this, val, key, element);
  3842. // Let the shadow follow the main element
  3843. if (!this.styledMode &&
  3844. this.shadows &&
  3845. /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(key)) {
  3846. this.updateShadows(key, val, setter);
  3847. }
  3848. }
  3849. }, this);
  3850. this.afterSetters();
  3851. }
  3852. // In accordance with animate, run a complete callback
  3853. if (complete) {
  3854. complete.call(this);
  3855. }
  3856. return ret;
  3857. };
  3858. /**
  3859. * Apply a clipping rectangle to this element.
  3860. *
  3861. * @function Highcharts.SVGElement#clip
  3862. *
  3863. * @param {Highcharts.ClipRectElement} [clipRect]
  3864. * The clipping rectangle. If skipped, the current clip is removed.
  3865. *
  3866. * @return {Highcharts.SVGElement}
  3867. * Returns the SVG element to allow chaining.
  3868. */
  3869. SVGElement.prototype.clip = function (clipRect) {
  3870. return this.attr('clip-path', clipRect ?
  3871. 'url(' + this.renderer.url + '#' + clipRect.id + ')' :
  3872. 'none');
  3873. };
  3874. /**
  3875. * Calculate the coordinates needed for drawing a rectangle crisply and
  3876. * return the calculated attributes.
  3877. *
  3878. * @function Highcharts.SVGElement#crisp
  3879. *
  3880. * @param {Highcharts.RectangleObject} rect
  3881. * Rectangle to crisp.
  3882. *
  3883. * @param {number} [strokeWidth]
  3884. * The stroke width to consider when computing crisp positioning. It can
  3885. * also be set directly on the rect parameter.
  3886. *
  3887. * @return {Highcharts.RectangleObject}
  3888. * The modified rectangle arguments.
  3889. */
  3890. SVGElement.prototype.crisp = function (rect, strokeWidth) {
  3891. var wrapper = this, normalizer;
  3892. strokeWidth = strokeWidth || rect.strokeWidth || 0;
  3893. // Math.round because strokeWidth can sometimes have roundoff errors
  3894. normalizer = Math.round(strokeWidth) % 2 / 2;
  3895. // normalize for crisp edges
  3896. rect.x = Math.floor(rect.x || wrapper.x || 0) + normalizer;
  3897. rect.y = Math.floor(rect.y || wrapper.y || 0) + normalizer;
  3898. rect.width = Math.floor((rect.width || wrapper.width || 0) - 2 * normalizer);
  3899. rect.height = Math.floor((rect.height || wrapper.height || 0) - 2 * normalizer);
  3900. if (defined(rect.strokeWidth)) {
  3901. rect.strokeWidth = strokeWidth;
  3902. }
  3903. return rect;
  3904. };
  3905. /**
  3906. * Build and apply an SVG gradient out of a common JavaScript configuration
  3907. * object. This function is called from the attribute setters. An event
  3908. * hook is added for supporting other complex color types.
  3909. *
  3910. * @private
  3911. * @function Highcharts.SVGElement#complexColor
  3912. *
  3913. * @param {Highcharts.GradientColorObject|Highcharts.PatternObject} colorOptions
  3914. * The gradient or pattern options structure.
  3915. *
  3916. * @param {string} prop
  3917. * The property to apply, can either be `fill` or `stroke`.
  3918. *
  3919. * @param {Highcharts.SVGDOMElement} elem
  3920. * SVG element to apply the gradient on.
  3921. */
  3922. SVGElement.prototype.complexColor = function (colorOptions, prop, elem) {
  3923. var renderer = this.renderer, colorObject, gradName, gradAttr, radAttr, gradients, stops, stopColor, stopOpacity, radialReference, id, key = [], value;
  3924. fireEvent(this.renderer, 'complexColor', {
  3925. args: arguments
  3926. }, function () {
  3927. // Apply linear or radial gradients
  3928. if (colorOptions.radialGradient) {
  3929. gradName = 'radialGradient';
  3930. }
  3931. else if (colorOptions.linearGradient) {
  3932. gradName = 'linearGradient';
  3933. }
  3934. if (gradName) {
  3935. gradAttr = colorOptions[gradName];
  3936. gradients = renderer.gradients;
  3937. stops = colorOptions.stops;
  3938. radialReference = elem.radialReference;
  3939. // Keep < 2.2 kompatibility
  3940. if (isArray(gradAttr)) {
  3941. colorOptions[gradName] = gradAttr = {
  3942. x1: gradAttr[0],
  3943. y1: gradAttr[1],
  3944. x2: gradAttr[2],
  3945. y2: gradAttr[3],
  3946. gradientUnits: 'userSpaceOnUse'
  3947. };
  3948. }
  3949. // Correct the radial gradient for the radial reference system
  3950. if (gradName === 'radialGradient' &&
  3951. radialReference &&
  3952. !defined(gradAttr.gradientUnits)) {
  3953. // Save the radial attributes for updating
  3954. radAttr = gradAttr;
  3955. gradAttr = merge(gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' });
  3956. }
  3957. // Build the unique key to detect whether we need to create a
  3958. // new element (#1282)
  3959. objectEach(gradAttr, function (val, n) {
  3960. if (n !== 'id') {
  3961. key.push(n, val);
  3962. }
  3963. });
  3964. objectEach(stops, function (val) {
  3965. key.push(val);
  3966. });
  3967. key = key.join(',');
  3968. // Check if a gradient object with the same config object is
  3969. // created within this renderer
  3970. if (gradients[key]) {
  3971. id = gradients[key].attr('id');
  3972. }
  3973. else {
  3974. // Set the id and create the element
  3975. gradAttr.id = id = uniqueKey();
  3976. var gradientObject_1 = gradients[key] =
  3977. renderer.createElement(gradName)
  3978. .attr(gradAttr)
  3979. .add(renderer.defs);
  3980. gradientObject_1.radAttr = radAttr;
  3981. // The gradient needs to keep a list of stops to be able to
  3982. // destroy them
  3983. gradientObject_1.stops = [];
  3984. stops.forEach(function (stop) {
  3985. var stopObject;
  3986. if (stop[1].indexOf('rgba') === 0) {
  3987. colorObject = Color.parse(stop[1]);
  3988. stopColor = colorObject.get('rgb');
  3989. stopOpacity = colorObject.get('a');
  3990. }
  3991. else {
  3992. stopColor = stop[1];
  3993. stopOpacity = 1;
  3994. }
  3995. stopObject = renderer.createElement('stop').attr({
  3996. offset: stop[0],
  3997. 'stop-color': stopColor,
  3998. 'stop-opacity': stopOpacity
  3999. }).add(gradientObject_1);
  4000. // Add the stop element to the gradient
  4001. gradientObject_1.stops.push(stopObject);
  4002. });
  4003. }
  4004. // Set the reference to the gradient object
  4005. value = 'url(' + renderer.url + '#' + id + ')';
  4006. elem.setAttribute(prop, value);
  4007. elem.gradient = key;
  4008. // Allow the color to be concatenated into tooltips formatters
  4009. // etc. (#2995)
  4010. colorOptions.toString = function () {
  4011. return value;
  4012. };
  4013. }
  4014. });
  4015. };
  4016. /**
  4017. * Set styles for the element. In addition to CSS styles supported by
  4018. * native SVG and HTML elements, there are also some custom made for
  4019. * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
  4020. * elements.
  4021. *
  4022. * @sample highcharts/members/renderer-text-on-chart/
  4023. * Styled text
  4024. *
  4025. * @function Highcharts.SVGElement#css
  4026. *
  4027. * @param {Highcharts.CSSObject} styles
  4028. * The new CSS styles.
  4029. *
  4030. * @return {Highcharts.SVGElement}
  4031. * Return the SVG element for chaining.
  4032. */
  4033. SVGElement.prototype.css = function (styles) {
  4034. var oldStyles = this.styles, newStyles = {}, elem = this.element, textWidth, serializedCss = '', hyphenate, hasNew = !oldStyles,
  4035. // These CSS properties are interpreted internally by the SVG
  4036. // renderer, but are not supported by SVG and should not be added to
  4037. // the DOM. In styled mode, no CSS should find its way to the DOM
  4038. // whatsoever (#6173, #6474).
  4039. svgPseudoProps = ['textOutline', 'textOverflow', 'width'];
  4040. // convert legacy
  4041. if (styles && styles.color) {
  4042. styles.fill = styles.color;
  4043. }
  4044. // Filter out existing styles to increase performance (#2640)
  4045. if (oldStyles) {
  4046. objectEach(styles, function (style, n) {
  4047. if (oldStyles && oldStyles[n] !== style) {
  4048. newStyles[n] = style;
  4049. hasNew = true;
  4050. }
  4051. });
  4052. }
  4053. if (hasNew) {
  4054. // Merge the new styles with the old ones
  4055. if (oldStyles) {
  4056. styles = extend(oldStyles, newStyles);
  4057. }
  4058. // Get the text width from style
  4059. if (styles) {
  4060. // Previously set, unset it (#8234)
  4061. if (styles.width === null || styles.width === 'auto') {
  4062. delete this.textWidth;
  4063. // Apply new
  4064. }
  4065. else if (elem.nodeName.toLowerCase() === 'text' &&
  4066. styles.width) {
  4067. textWidth = this.textWidth = pInt(styles.width);
  4068. }
  4069. }
  4070. // store object
  4071. this.styles = styles;
  4072. if (textWidth && (!svg && this.renderer.forExport)) {
  4073. delete styles.width;
  4074. }
  4075. // Serialize and set style attribute
  4076. if (elem.namespaceURI === this.SVG_NS) { // #7633
  4077. hyphenate = function (a, b) {
  4078. return '-' + b.toLowerCase();
  4079. };
  4080. objectEach(styles, function (style, n) {
  4081. if (svgPseudoProps.indexOf(n) === -1) {
  4082. serializedCss +=
  4083. n.replace(/([A-Z])/g, hyphenate) + ':' +
  4084. style + ';';
  4085. }
  4086. });
  4087. if (serializedCss) {
  4088. attr(elem, 'style', serializedCss); // #1881
  4089. }
  4090. }
  4091. else {
  4092. css(elem, styles);
  4093. }
  4094. if (this.added) {
  4095. // Rebuild text after added. Cache mechanisms in the buildText
  4096. // will prevent building if there are no significant changes.
  4097. if (this.element.nodeName === 'text') {
  4098. this.renderer.buildText(this);
  4099. }
  4100. // Apply text outline after added
  4101. if (styles && styles.textOutline) {
  4102. this.applyTextOutline(styles.textOutline);
  4103. }
  4104. }
  4105. }
  4106. return this;
  4107. };
  4108. /**
  4109. * @private
  4110. * @function Highcharts.SVGElement#dashstyleSetter
  4111. * @param {string} value
  4112. */
  4113. SVGElement.prototype.dashstyleSetter = function (value) {
  4114. var i, strokeWidth = this['stroke-width'];
  4115. // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
  4116. // strokeWidth function, we should be able to use that instead.
  4117. if (strokeWidth === 'inherit') {
  4118. strokeWidth = 1;
  4119. }
  4120. value = value && value.toLowerCase();
  4121. if (value) {
  4122. var v = value
  4123. .replace('shortdashdotdot', '3,1,1,1,1,1,')
  4124. .replace('shortdashdot', '3,1,1,1')
  4125. .replace('shortdot', '1,1,')
  4126. .replace('shortdash', '3,1,')
  4127. .replace('longdash', '8,3,')
  4128. .replace(/dot/g, '1,3,')
  4129. .replace('dash', '4,3,')
  4130. .replace(/,$/, '')
  4131. .split(','); // ending comma
  4132. i = v.length;
  4133. while (i--) {
  4134. v[i] = '' + (pInt(v[i]) * pick(strokeWidth, NaN));
  4135. }
  4136. value = v.join(',').replace(/NaN/g, 'none'); // #3226
  4137. this.element.setAttribute('stroke-dasharray', value);
  4138. }
  4139. };
  4140. /**
  4141. * Destroy the element and element wrapper and clear up the DOM and event
  4142. * hooks.
  4143. *
  4144. * @function Highcharts.SVGElement#destroy
  4145. */
  4146. SVGElement.prototype.destroy = function () {
  4147. var wrapper = this, element = wrapper.element || {}, renderer = wrapper.renderer, parentToClean = (renderer.isSVG &&
  4148. element.nodeName === 'SPAN' &&
  4149. wrapper.parentGroup ||
  4150. void 0), grandParent, ownerSVGElement = element.ownerSVGElement, i;
  4151. // remove events
  4152. element.onclick = element.onmouseout = element.onmouseover =
  4153. element.onmousemove = element.point = null;
  4154. stop(wrapper); // stop running animations
  4155. if (wrapper.clipPath && ownerSVGElement) {
  4156. var clipPath_1 = wrapper.clipPath;
  4157. // Look for existing references to this clipPath and remove them
  4158. // before destroying the element (#6196).
  4159. // The upper case version is for Edge
  4160. [].forEach.call(ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'), function (el) {
  4161. var clipPathAttr = el.getAttribute('clip-path');
  4162. if (clipPathAttr.indexOf(clipPath_1.element.id) > -1) {
  4163. el.removeAttribute('clip-path');
  4164. }
  4165. });
  4166. wrapper.clipPath = clipPath_1.destroy();
  4167. }
  4168. // Destroy stops in case this is a gradient object @todo old code?
  4169. if (wrapper.stops) {
  4170. for (i = 0; i < wrapper.stops.length; i++) {
  4171. wrapper.stops[i].destroy();
  4172. }
  4173. wrapper.stops.length = 0;
  4174. wrapper.stops = void 0;
  4175. }
  4176. // remove element
  4177. wrapper.safeRemoveChild(element);
  4178. if (!renderer.styledMode) {
  4179. wrapper.destroyShadows();
  4180. }
  4181. // In case of useHTML, clean up empty containers emulating SVG groups
  4182. // (#1960, #2393, #2697).
  4183. while (parentToClean &&
  4184. parentToClean.div &&
  4185. parentToClean.div.childNodes.length === 0) {
  4186. grandParent = parentToClean.parentGroup;
  4187. wrapper.safeRemoveChild(parentToClean.div);
  4188. delete parentToClean.div;
  4189. parentToClean = grandParent;
  4190. }
  4191. // remove from alignObjects
  4192. if (wrapper.alignTo) {
  4193. erase(renderer.alignedObjects, wrapper);
  4194. }
  4195. objectEach(wrapper, function (val, key) {
  4196. // Destroy child elements of a group
  4197. if (wrapper[key] &&
  4198. wrapper[key].parentGroup === wrapper &&
  4199. wrapper[key].destroy) {
  4200. wrapper[key].destroy();
  4201. }
  4202. // Delete all properties
  4203. delete wrapper[key];
  4204. });
  4205. return;
  4206. };
  4207. /**
  4208. * Destroy shadows on the element.
  4209. *
  4210. * @private
  4211. * @function Highcharts.SVGElement#destroyShadows
  4212. *
  4213. * @return {void}
  4214. */
  4215. SVGElement.prototype.destroyShadows = function () {
  4216. (this.shadows || []).forEach(function (shadow) {
  4217. this.safeRemoveChild(shadow);
  4218. }, this);
  4219. this.shadows = void 0;
  4220. };
  4221. /**
  4222. * @private
  4223. */
  4224. SVGElement.prototype.destroyTextPath = function (elem, path) {
  4225. var textElement = elem.getElementsByTagName('text')[0];
  4226. var tspans;
  4227. if (textElement) {
  4228. // Remove textPath attributes
  4229. textElement.removeAttribute('dx');
  4230. textElement.removeAttribute('dy');
  4231. // Remove ID's:
  4232. path.element.setAttribute('id', '');
  4233. // Check if textElement includes textPath,
  4234. if (this.textPathWrapper &&
  4235. textElement.getElementsByTagName('textPath').length) {
  4236. // Move nodes to <text>
  4237. tspans = this.textPathWrapper.element.childNodes;
  4238. // Now move all <tspan>'s to the <textPath> node
  4239. while (tspans.length) {
  4240. textElement.appendChild(tspans[0]);
  4241. }
  4242. // Remove <textPath> from the DOM
  4243. textElement.removeChild(this.textPathWrapper.element);
  4244. }
  4245. }
  4246. else if (elem.getAttribute('dx') || elem.getAttribute('dy')) {
  4247. // Remove textPath attributes from elem
  4248. // to get correct text-outline position
  4249. elem.removeAttribute('dx');
  4250. elem.removeAttribute('dy');
  4251. }
  4252. if (this.textPathWrapper) {
  4253. // Set textPathWrapper to undefined and destroy it
  4254. this.textPathWrapper = this.textPathWrapper.destroy();
  4255. }
  4256. };
  4257. /**
  4258. * @private
  4259. * @function Highcharts.SVGElement#dSettter
  4260. * @param {number|string|Highcharts.SVGPathArray} value
  4261. * @param {string} key
  4262. * @param {Highcharts.SVGDOMElement} element
  4263. */
  4264. SVGElement.prototype.dSetter = function (value, key, element) {
  4265. if (isArray(value)) {
  4266. // Backwards compatibility, convert one-dimensional array into an
  4267. // array of segments
  4268. if (typeof value[0] === 'string') {
  4269. value = this.renderer.pathToSegments(value);
  4270. }
  4271. this.pathArray = value;
  4272. value = value.reduce(function (acc, seg, i) {
  4273. if (!seg || !seg.join) {
  4274. return (seg || '').toString();
  4275. }
  4276. return (i ? acc + ' ' : '') + seg.join(' ');
  4277. }, '');
  4278. }
  4279. if (/(NaN| {2}|^$)/.test(value)) {
  4280. value = 'M 0 0';
  4281. }
  4282. // Check for cache before resetting. Resetting causes disturbance in the
  4283. // DOM, causing flickering in some cases in Edge/IE (#6747). Also
  4284. // possible performance gain.
  4285. if (this[key] !== value) {
  4286. element.setAttribute(key, value);
  4287. this[key] = value;
  4288. }
  4289. };
  4290. /**
  4291. * Fade out an element by animating its opacity down to 0, and hide it on
  4292. * complete. Used internally for the tooltip.
  4293. *
  4294. * @function Highcharts.SVGElement#fadeOut
  4295. *
  4296. * @param {number} [duration=150]
  4297. * The fade duration in milliseconds.
  4298. */
  4299. SVGElement.prototype.fadeOut = function (duration) {
  4300. var elemWrapper = this;
  4301. elemWrapper.animate({
  4302. opacity: 0
  4303. }, {
  4304. duration: pick(duration, 150),
  4305. complete: function () {
  4306. // #3088, assuming we're only using this for tooltips
  4307. elemWrapper.attr({ y: -9999 }).hide();
  4308. }
  4309. });
  4310. };
  4311. /**
  4312. * @private
  4313. * @function Highcharts.SVGElement#fillSetter
  4314. * @param {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} value
  4315. * @param {string} key
  4316. * @param {Highcharts.SVGDOMElement} element
  4317. */
  4318. SVGElement.prototype.fillSetter = function (value, key, element) {
  4319. if (typeof value === 'string') {
  4320. element.setAttribute(key, value);
  4321. }
  4322. else if (value) {
  4323. this.complexColor(value, key, element);
  4324. }
  4325. };
  4326. /**
  4327. * Get the bounding box (width, height, x and y) for the element. Generally
  4328. * used to get rendered text size. Since this is called a lot in charts,
  4329. * the results are cached based on text properties, in order to save DOM
  4330. * traffic. The returned bounding box includes the rotation, so for example
  4331. * a single text line of rotation 90 will report a greater height, and a
  4332. * width corresponding to the line-height.
  4333. *
  4334. * @sample highcharts/members/renderer-on-chart/
  4335. * Draw a rectangle based on a text's bounding box
  4336. *
  4337. * @function Highcharts.SVGElement#getBBox
  4338. *
  4339. * @param {boolean} [reload]
  4340. * Skip the cache and get the updated DOM bouding box.
  4341. *
  4342. * @param {number} [rot]
  4343. * Override the element's rotation. This is internally used on axis
  4344. * labels with a value of 0 to find out what the bounding box would
  4345. * be have been if it were not rotated.
  4346. *
  4347. * @return {Highcharts.BBoxObject}
  4348. * The bounding box with `x`, `y`, `width` and `height` properties.
  4349. */
  4350. SVGElement.prototype.getBBox = function (reload, rot) {
  4351. var wrapper = this, bBox, // = wrapper.bBox,
  4352. renderer = wrapper.renderer, width, height, element = wrapper.element, styles = wrapper.styles, fontSize, textStr = wrapper.textStr, toggleTextShadowShim, cache = renderer.cache, cacheKeys = renderer.cacheKeys, isSVG = element.namespaceURI === wrapper.SVG_NS, cacheKey;
  4353. var rotation = pick(rot, wrapper.rotation, 0);
  4354. fontSize = renderer.styledMode ? (element &&
  4355. SVGElement.prototype.getStyle.call(element, 'font-size')) : (styles && styles.fontSize);
  4356. // Avoid undefined and null (#7316)
  4357. if (defined(textStr)) {
  4358. cacheKey = textStr.toString();
  4359. // Since numbers are monospaced, and numerical labels appear a lot
  4360. // in a chart, we assume that a label of n characters has the same
  4361. // bounding box as others of the same length. Unless there is inner
  4362. // HTML in the label. In that case, leave the numbers as is (#5899).
  4363. if (cacheKey.indexOf('<') === -1) {
  4364. cacheKey = cacheKey.replace(/[0-9]/g, '0');
  4365. }
  4366. // Properties that affect bounding box
  4367. cacheKey += [
  4368. '',
  4369. rotation,
  4370. fontSize,
  4371. wrapper.textWidth,
  4372. styles && styles.textOverflow,
  4373. styles && styles.fontWeight // #12163
  4374. ].join(',');
  4375. }
  4376. if (cacheKey && !reload) {
  4377. bBox = cache[cacheKey];
  4378. }
  4379. // No cache found
  4380. if (!bBox) {
  4381. // SVG elements
  4382. if (isSVG || renderer.forExport) {
  4383. try { // Fails in Firefox if the container has display: none.
  4384. // When the text shadow shim is used, we need to hide the
  4385. // fake shadows to get the correct bounding box (#3872)
  4386. toggleTextShadowShim = this.fakeTS && function (display) {
  4387. [].forEach.call(element.querySelectorAll('.highcharts-text-outline'), function (tspan) {
  4388. tspan.style.display = display;
  4389. });
  4390. };
  4391. // Workaround for #3842, Firefox reporting wrong bounding
  4392. // box for shadows
  4393. if (isFunction(toggleTextShadowShim)) {
  4394. toggleTextShadowShim('none');
  4395. }
  4396. bBox = element.getBBox ?
  4397. // SVG: use extend because IE9 is not allowed to change
  4398. // width and height in case of rotation (below)
  4399. extend({}, element.getBBox()) : {
  4400. // Legacy IE in export mode
  4401. width: element.offsetWidth,
  4402. height: element.offsetHeight
  4403. };
  4404. // #3842
  4405. if (isFunction(toggleTextShadowShim)) {
  4406. toggleTextShadowShim('');
  4407. }
  4408. }
  4409. catch (e) {
  4410. '';
  4411. }
  4412. // If the bBox is not set, the try-catch block above failed. The
  4413. // other condition is for Opera that returns a width of
  4414. // -Infinity on hidden elements.
  4415. if (!bBox || bBox.width < 0) {
  4416. bBox = { width: 0, height: 0 };
  4417. }
  4418. // VML Renderer or useHTML within SVG
  4419. }
  4420. else {
  4421. bBox = wrapper.htmlGetBBox();
  4422. }
  4423. // True SVG elements as well as HTML elements in modern browsers
  4424. // using the .useHTML option need to compensated for rotation
  4425. if (renderer.isSVG) {
  4426. width = bBox.width;
  4427. height = bBox.height;
  4428. // Workaround for wrong bounding box in IE, Edge and Chrome on
  4429. // Windows. With Highcharts' default font, IE and Edge report
  4430. // a box height of 16.899 and Chrome rounds it to 17. If this
  4431. // stands uncorrected, it results in more padding added below
  4432. // the text than above when adding a label border or background.
  4433. // Also vertical positioning is affected.
  4434. // https://jsfiddle.net/highcharts/em37nvuj/
  4435. // (#1101, #1505, #1669, #2568, #6213).
  4436. if (isSVG) {
  4437. bBox.height = height = ({
  4438. '11px,17': 14,
  4439. '13px,20': 16
  4440. }[styles &&
  4441. styles.fontSize + ',' + Math.round(height)] ||
  4442. height);
  4443. }
  4444. // Adjust for rotated text
  4445. if (rotation) {
  4446. var rad = rotation * deg2rad;
  4447. bBox.width = Math.abs(height * Math.sin(rad)) +
  4448. Math.abs(width * Math.cos(rad));
  4449. bBox.height = Math.abs(height * Math.cos(rad)) +
  4450. Math.abs(width * Math.sin(rad));
  4451. }
  4452. }
  4453. // Cache it. When loading a chart in a hidden iframe in Firefox and
  4454. // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
  4455. if (cacheKey && bBox.height > 0) {
  4456. // Rotate (#4681)
  4457. while (cacheKeys.length > 250) {
  4458. delete cache[cacheKeys.shift()];
  4459. }
  4460. if (!cache[cacheKey]) {
  4461. cacheKeys.push(cacheKey);
  4462. }
  4463. cache[cacheKey] = bBox;
  4464. }
  4465. }
  4466. return bBox;
  4467. };
  4468. /**
  4469. * Get the computed style. Only in styled mode.
  4470. *
  4471. * @example
  4472. * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
  4473. *
  4474. * @function Highcharts.SVGElement#getStyle
  4475. *
  4476. * @param {string} prop
  4477. * The property name to check for.
  4478. *
  4479. * @return {string}
  4480. * The current computed value.
  4481. */
  4482. SVGElement.prototype.getStyle = function (prop) {
  4483. return win
  4484. .getComputedStyle(this.element || this, '')
  4485. .getPropertyValue(prop);
  4486. };
  4487. /**
  4488. * Check if an element has the given class name.
  4489. *
  4490. * @function Highcharts.SVGElement#hasClass
  4491. *
  4492. * @param {string} className
  4493. * The class name to check for.
  4494. *
  4495. * @return {boolean}
  4496. * Whether the class name is found.
  4497. */
  4498. SVGElement.prototype.hasClass = function (className) {
  4499. return ('' + this.attr('class'))
  4500. .split(' ')
  4501. .indexOf(className) !== -1;
  4502. };
  4503. /**
  4504. * Hide the element, similar to setting the `visibility` attribute to
  4505. * `hidden`.
  4506. *
  4507. * @function Highcharts.SVGElement#hide
  4508. *
  4509. * @param {boolean} [hideByTranslation=false]
  4510. * The flag to determine if element should be hidden by moving out
  4511. * of the viewport. Used for example for dataLabels.
  4512. *
  4513. * @return {Highcharts.SVGElement}
  4514. * Returns the SVGElement for chaining.
  4515. */
  4516. SVGElement.prototype.hide = function (hideByTranslation) {
  4517. if (hideByTranslation) {
  4518. this.attr({ y: -9999 });
  4519. }
  4520. else {
  4521. this.attr({ visibility: 'hidden' });
  4522. }
  4523. return this;
  4524. };
  4525. /**
  4526. * @private
  4527. */
  4528. SVGElement.prototype.htmlGetBBox = function () {
  4529. return { height: 0, width: 0, x: 0, y: 0 };
  4530. };
  4531. /**
  4532. * Initialize the SVG element. This function only exists to make the
  4533. * initialization process overridable. It should not be called directly.
  4534. *
  4535. * @function Highcharts.SVGElement#init
  4536. *
  4537. * @param {Highcharts.SVGRenderer} renderer
  4538. * The SVGRenderer instance to initialize to.
  4539. *
  4540. * @param {string} nodeName
  4541. * The SVG node name.
  4542. */
  4543. SVGElement.prototype.init = function (renderer, nodeName) {
  4544. /**
  4545. * The primary DOM node. Each `SVGElement` instance wraps a main DOM
  4546. * node, but may also represent more nodes.
  4547. *
  4548. * @name Highcharts.SVGElement#element
  4549. * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
  4550. */
  4551. this.element = nodeName === 'span' ?
  4552. createElement(nodeName) :
  4553. doc.createElementNS(this.SVG_NS, nodeName);
  4554. /**
  4555. * The renderer that the SVGElement belongs to.
  4556. *
  4557. * @name Highcharts.SVGElement#renderer
  4558. * @type {Highcharts.SVGRenderer}
  4559. */
  4560. this.renderer = renderer;
  4561. fireEvent(this, 'afterInit');
  4562. };
  4563. /**
  4564. * Invert a group, rotate and flip. This is used internally on inverted
  4565. * charts, where the points and graphs are drawn as if not inverted, then
  4566. * the series group elements are inverted.
  4567. *
  4568. * @function Highcharts.SVGElement#invert
  4569. *
  4570. * @param {boolean} inverted
  4571. * Whether to invert or not. An inverted shape can be un-inverted by
  4572. * setting it to false.
  4573. *
  4574. * @return {Highcharts.SVGElement}
  4575. * Return the SVGElement for chaining.
  4576. */
  4577. SVGElement.prototype.invert = function (inverted) {
  4578. var wrapper = this;
  4579. wrapper.inverted = inverted;
  4580. wrapper.updateTransform();
  4581. return wrapper;
  4582. };
  4583. /**
  4584. * Add an event listener. This is a simple setter that replaces all other
  4585. * events of the same type, opposed to the {@link Highcharts#addEvent}
  4586. * function.
  4587. *
  4588. * @sample highcharts/members/element-on/
  4589. * A clickable rectangle
  4590. *
  4591. * @function Highcharts.SVGElement#on
  4592. *
  4593. * @param {string} eventType
  4594. * The event type. If the type is `click`, Highcharts will internally
  4595. * translate it to a `touchstart` event on touch devices, to prevent the
  4596. * browser from waiting for a click event from firing.
  4597. *
  4598. * @param {Function} handler
  4599. * The handler callback.
  4600. *
  4601. * @return {Highcharts.SVGElement}
  4602. * The SVGElement for chaining.
  4603. */
  4604. SVGElement.prototype.on = function (eventType, handler) {
  4605. var svgElement = this, element = svgElement.element, touchStartPos, touchEventFired;
  4606. // touch
  4607. if (hasTouch && eventType === 'click') {
  4608. element.ontouchstart = function (e) {
  4609. // save touch position for later calculation
  4610. touchStartPos = {
  4611. clientX: e.touches[0].clientX,
  4612. clientY: e.touches[0].clientY
  4613. };
  4614. };
  4615. // Instead of ontouchstart, event handlers should be called
  4616. // on touchend - similar to how current mouseup events are called
  4617. element.ontouchend = function (e) {
  4618. // hasMoved is a boolean variable containing logic if page
  4619. // was scrolled, so if touch position changed more than
  4620. // ~4px (value borrowed from general touch handler)
  4621. var hasMoved = touchStartPos.clientX ? Math.sqrt(Math.pow(touchStartPos.clientX - e.changedTouches[0].clientX, 2) +
  4622. Math.pow(touchStartPos.clientY - e.changedTouches[0].clientY, 2)) >= 4 : false;
  4623. if (!hasMoved) { // only call handlers if page was not scrolled
  4624. handler.call(element, e);
  4625. }
  4626. touchEventFired = true;
  4627. // prevent other events from being fired. #9682
  4628. e.preventDefault();
  4629. };
  4630. element.onclick = function (e) {
  4631. // Do not call onclick handler if touch event was fired already.
  4632. if (!touchEventFired) {
  4633. handler.call(element, e);
  4634. }
  4635. };
  4636. }
  4637. else {
  4638. // simplest possible event model for internal use
  4639. element['on' + eventType] = handler;
  4640. }
  4641. return this;
  4642. };
  4643. /**
  4644. * @private
  4645. * @function Highcharts.SVGElement#opacitySetter
  4646. * @param {string} value
  4647. * @param {string} key
  4648. * @param {Highcharts.SVGDOMElement} element
  4649. */
  4650. SVGElement.prototype.opacitySetter = function (value, key, element) {
  4651. this[key] = value;
  4652. element.setAttribute(key, value);
  4653. };
  4654. /**
  4655. * Remove a class name from the element.
  4656. *
  4657. * @function Highcharts.SVGElement#removeClass
  4658. *
  4659. * @param {string|RegExp} className
  4660. * The class name to remove.
  4661. *
  4662. * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
  4663. */
  4664. SVGElement.prototype.removeClass = function (className) {
  4665. return this.attr('class', ('' + this.attr('class'))
  4666. .replace(isString(className) ?
  4667. new RegExp("(^| )" + className + "( |$)") : // #12064, #13590
  4668. className, ' ')
  4669. .replace(/ +/g, ' ')
  4670. .trim());
  4671. };
  4672. /**
  4673. * @private
  4674. * @param {Array<Highcharts.SVGDOMElement>} tspans
  4675. * Text spans.
  4676. */
  4677. SVGElement.prototype.removeTextOutline = function (tspans) {
  4678. // Iterate from the end to
  4679. // support removing items inside the cycle (#6472).
  4680. var i = tspans.length, tspan;
  4681. while (i--) {
  4682. tspan = tspans[i];
  4683. if (tspan.getAttribute('class') === 'highcharts-text-outline') {
  4684. // Remove then erase
  4685. erase(tspans, this.element.removeChild(tspan));
  4686. }
  4687. }
  4688. };
  4689. /**
  4690. * Removes an element from the DOM.
  4691. *
  4692. * @private
  4693. * @function Highcharts.SVGElement#safeRemoveChild
  4694. *
  4695. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  4696. * The DOM node to remove.
  4697. */
  4698. SVGElement.prototype.safeRemoveChild = function (element) {
  4699. var parentNode = element.parentNode;
  4700. if (parentNode) {
  4701. parentNode.removeChild(element);
  4702. }
  4703. };
  4704. /**
  4705. * Set the coordinates needed to draw a consistent radial gradient across
  4706. * a shape regardless of positioning inside the chart. Used on pie slices
  4707. * to make all the slices have the same radial reference point.
  4708. *
  4709. * @function Highcharts.SVGElement#setRadialReference
  4710. *
  4711. * @param {Array<number>} coordinates
  4712. * The center reference. The format is `[centerX, centerY, diameter]` in
  4713. * pixels.
  4714. *
  4715. * @return {Highcharts.SVGElement}
  4716. * Returns the SVGElement for chaining.
  4717. */
  4718. SVGElement.prototype.setRadialReference = function (coordinates) {
  4719. var existingGradient = (this.element.gradient &&
  4720. this.renderer.gradients[this.element.gradient]);
  4721. this.element.radialReference = coordinates;
  4722. // On redrawing objects with an existing gradient, the gradient needs
  4723. // to be repositioned (#3801)
  4724. if (existingGradient && existingGradient.radAttr) {
  4725. existingGradient.animate(this.renderer.getRadialAttr(coordinates, existingGradient.radAttr));
  4726. }
  4727. return this;
  4728. };
  4729. /**
  4730. * @private
  4731. * @function Highcharts.SVGElement#setTextPath
  4732. * @param {Highcharts.SVGElement} path
  4733. * Path to follow.
  4734. * @param {Highcharts.DataLabelsTextPathOptionsObject} textPathOptions
  4735. * Options.
  4736. * @return {Highcharts.SVGElement}
  4737. * Returns the SVGElement for chaining.
  4738. */
  4739. SVGElement.prototype.setTextPath = function (path, textPathOptions) {
  4740. var elem = this.element, attribsMap = {
  4741. textAnchor: 'text-anchor'
  4742. }, attrs, adder = false, textPathElement, textPathId, textPathWrapper = this.textPathWrapper, tspans, firstTime = !textPathWrapper;
  4743. // Defaults
  4744. textPathOptions = merge(true, {
  4745. enabled: true,
  4746. attributes: {
  4747. dy: -5,
  4748. startOffset: '50%',
  4749. textAnchor: 'middle'
  4750. }
  4751. }, textPathOptions);
  4752. attrs = textPathOptions.attributes;
  4753. if (path && textPathOptions && textPathOptions.enabled) {
  4754. // In case of fixed width for a text, string is rebuilt
  4755. // (e.g. ellipsis is applied), so we need to rebuild textPath too
  4756. if (textPathWrapper &&
  4757. textPathWrapper.element.parentNode === null) {
  4758. // When buildText functionality was triggered again
  4759. // and deletes textPathWrapper parentNode
  4760. firstTime = true;
  4761. textPathWrapper = textPathWrapper.destroy();
  4762. }
  4763. else if (textPathWrapper) {
  4764. // Case after drillup when spans were added into
  4765. // the DOM outside the textPathWrapper parentGroup
  4766. this.removeTextOutline.call(textPathWrapper.parentGroup, [].slice.call(elem.getElementsByTagName('tspan')));
  4767. }
  4768. // label() has padding, text() doesn't
  4769. if (this.options && this.options.padding) {
  4770. attrs.dx = -this.options.padding;
  4771. }
  4772. if (!textPathWrapper) {
  4773. // Create <textPath>, defer the DOM adder
  4774. this.textPathWrapper = textPathWrapper =
  4775. this.renderer.createElement('textPath');
  4776. adder = true;
  4777. }
  4778. textPathElement = textPathWrapper.element;
  4779. // Set ID for the path
  4780. textPathId = path.element.getAttribute('id');
  4781. if (!textPathId) {
  4782. path.element.setAttribute('id', textPathId = uniqueKey());
  4783. }
  4784. // Change DOM structure, by placing <textPath> tag in <text>
  4785. if (firstTime) {
  4786. tspans = elem.getElementsByTagName('tspan');
  4787. // Now move all <tspan>'s to the <textPath> node
  4788. while (tspans.length) {
  4789. // Remove "y" from tspans, as Firefox translates them
  4790. tspans[0].setAttribute('y', 0);
  4791. // Remove "x" from tspans
  4792. if (isNumber(attrs.dx)) {
  4793. tspans[0].setAttribute('x', -attrs.dx);
  4794. }
  4795. textPathElement.appendChild(tspans[0]);
  4796. }
  4797. }
  4798. // Add <textPath> to the DOM
  4799. if (adder &&
  4800. textPathWrapper) {
  4801. textPathWrapper.add({
  4802. // label() is placed in a group, text() is standalone
  4803. element: this.text ? this.text.element : elem
  4804. });
  4805. }
  4806. // Set basic options:
  4807. // Use `setAttributeNS` because Safari needs this..
  4808. textPathElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.renderer.url + '#' + textPathId);
  4809. // Presentation attributes:
  4810. // dx/dy options must by set on <text> (parent),
  4811. // the rest should be set on <textPath>
  4812. if (defined(attrs.dy)) {
  4813. textPathElement.parentNode
  4814. .setAttribute('dy', attrs.dy);
  4815. delete attrs.dy;
  4816. }
  4817. if (defined(attrs.dx)) {
  4818. textPathElement.parentNode
  4819. .setAttribute('dx', attrs.dx);
  4820. delete attrs.dx;
  4821. }
  4822. // Additional attributes
  4823. objectEach(attrs, function (val, key) {
  4824. textPathElement.setAttribute(attribsMap[key] || key, val);
  4825. });
  4826. // Remove translation, text that follows path does not need that
  4827. elem.removeAttribute('transform');
  4828. // Remove shadows and text outlines
  4829. this.removeTextOutline.call(textPathWrapper, [].slice.call(elem.getElementsByTagName('tspan')));
  4830. // Remove background and border for label(), see #10545
  4831. // Alternatively, we can disable setting background rects in
  4832. // series.drawDataLabels()
  4833. if (this.text && !this.renderer.styledMode) {
  4834. this.attr({
  4835. fill: 'none',
  4836. 'stroke-width': 0
  4837. });
  4838. }
  4839. // Disable some functions
  4840. this.updateTransform = noop;
  4841. this.applyTextOutline = noop;
  4842. }
  4843. else if (textPathWrapper) {
  4844. // Reset to prototype
  4845. delete this.updateTransform;
  4846. delete this.applyTextOutline;
  4847. // Restore DOM structure:
  4848. this.destroyTextPath(elem, path);
  4849. // Bring attributes back
  4850. this.updateTransform();
  4851. // Set textOutline back for text()
  4852. if (this.options && this.options.rotation) {
  4853. this.applyTextOutline(this.options.style.textOutline);
  4854. }
  4855. }
  4856. return this;
  4857. };
  4858. /**
  4859. * Add a shadow to the element. Must be called after the element is added to
  4860. * the DOM. In styled mode, this method is not used, instead use `defs` and
  4861. * filters.
  4862. *
  4863. * @example
  4864. * renderer.rect(10, 100, 100, 100)
  4865. * .attr({ fill: 'red' })
  4866. * .shadow(true);
  4867. *
  4868. * @function Highcharts.SVGElement#shadow
  4869. *
  4870. * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions]
  4871. * The shadow options. If `true`, the default options are applied. If
  4872. * `false`, the current shadow will be removed.
  4873. *
  4874. * @param {Highcharts.SVGElement} [group]
  4875. * The SVG group element where the shadows will be applied. The
  4876. * default is to add it to the same parent as the current element.
  4877. * Internally, this is ised for pie slices, where all the shadows are
  4878. * added to an element behind all the slices.
  4879. *
  4880. * @param {boolean} [cutOff]
  4881. * Used internally for column shadows.
  4882. *
  4883. * @return {Highcharts.SVGElement}
  4884. * Returns the SVGElement for chaining.
  4885. */
  4886. SVGElement.prototype.shadow = function (shadowOptions, group, cutOff) {
  4887. var shadows = [], i, shadow, element = this.element, strokeWidth, shadowElementOpacity, update = false, oldShadowOptions = this.oldShadowOptions,
  4888. // compensate for inverted plot area
  4889. transform;
  4890. var defaultShadowOptions = {
  4891. color: '#000000',
  4892. offsetX: 1,
  4893. offsetY: 1,
  4894. opacity: 0.15,
  4895. width: 3
  4896. };
  4897. var options;
  4898. if (shadowOptions === true) {
  4899. options = defaultShadowOptions;
  4900. }
  4901. else if (typeof shadowOptions === 'object') {
  4902. options = extend(defaultShadowOptions, shadowOptions);
  4903. }
  4904. // Update shadow when options change (#12091).
  4905. if (options) {
  4906. // Go over each key to look for change
  4907. if (options && oldShadowOptions) {
  4908. objectEach(options, function (value, key) {
  4909. if (value !== oldShadowOptions[key]) {
  4910. update = true;
  4911. }
  4912. });
  4913. }
  4914. if (update) {
  4915. this.destroyShadows();
  4916. }
  4917. this.oldShadowOptions = options;
  4918. }
  4919. if (!options) {
  4920. this.destroyShadows();
  4921. }
  4922. else if (!this.shadows) {
  4923. shadowElementOpacity = options.opacity / options.width;
  4924. transform = this.parentInverted ?
  4925. 'translate(-1,-1)' :
  4926. "translate(" + options.offsetX + ", " + options.offsetY + ")";
  4927. for (i = 1; i <= options.width; i++) {
  4928. shadow = element.cloneNode(false);
  4929. strokeWidth = (options.width * 2) + 1 - (2 * i);
  4930. attr(shadow, {
  4931. stroke: (shadowOptions.color ||
  4932. '#000000'),
  4933. 'stroke-opacity': shadowElementOpacity * i,
  4934. 'stroke-width': strokeWidth,
  4935. transform: transform,
  4936. fill: 'none'
  4937. });
  4938. shadow.setAttribute('class', (shadow.getAttribute('class') || '') + ' highcharts-shadow');
  4939. if (cutOff) {
  4940. attr(shadow, 'height', Math.max(attr(shadow, 'height') - strokeWidth, 0));
  4941. shadow.cutHeight = strokeWidth;
  4942. }
  4943. if (group) {
  4944. group.element.appendChild(shadow);
  4945. }
  4946. else if (element.parentNode) {
  4947. element.parentNode.insertBefore(shadow, element);
  4948. }
  4949. shadows.push(shadow);
  4950. }
  4951. this.shadows = shadows;
  4952. }
  4953. return this;
  4954. };
  4955. /**
  4956. * Show the element after it has been hidden.
  4957. *
  4958. * @function Highcharts.SVGElement#show
  4959. *
  4960. * @param {boolean} [inherit=false]
  4961. * Set the visibility attribute to `inherit` rather than `visible`.
  4962. * The difference is that an element with `visibility="visible"`
  4963. * will be visible even if the parent is hidden.
  4964. *
  4965. * @return {Highcharts.SVGElement}
  4966. * Returns the SVGElement for chaining.
  4967. */
  4968. SVGElement.prototype.show = function (inherit) {
  4969. return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
  4970. };
  4971. /**
  4972. * WebKit and Batik have problems with a stroke-width of zero, so in this
  4973. * case we remove the stroke attribute altogether. #1270, #1369, #3065,
  4974. * #3072.
  4975. *
  4976. * @private
  4977. * @function Highcharts.SVGElement#strokeSetter
  4978. * @param {number|string} value
  4979. * @param {string} key
  4980. * @param {Highcharts.SVGDOMElement} element
  4981. */
  4982. SVGElement.prototype.strokeSetter = function (value, key, element) {
  4983. this[key] = value;
  4984. // Only apply the stroke attribute if the stroke width is defined and
  4985. // larger than 0
  4986. if (this.stroke && this['stroke-width']) {
  4987. // Use prototype as instance may be overridden
  4988. SVGElement.prototype.fillSetter.call(this, this.stroke, 'stroke', element);
  4989. element.setAttribute('stroke-width', this['stroke-width']);
  4990. this.hasStroke = true;
  4991. }
  4992. else if (key === 'stroke-width' && value === 0 && this.hasStroke) {
  4993. element.removeAttribute('stroke');
  4994. this.hasStroke = false;
  4995. }
  4996. else if (this.renderer.styledMode && this['stroke-width']) {
  4997. element.setAttribute('stroke-width', this['stroke-width']);
  4998. this.hasStroke = true;
  4999. }
  5000. };
  5001. /**
  5002. * Get the computed stroke width in pixel values. This is used extensively
  5003. * when drawing shapes to ensure the shapes are rendered crisp and
  5004. * positioned correctly relative to each other. Using
  5005. * `shape-rendering: crispEdges` leaves us less control over positioning,
  5006. * for example when we want to stack columns next to each other, or position
  5007. * things pixel-perfectly within the plot box.
  5008. *
  5009. * The common pattern when placing a shape is:
  5010. * - Create the SVGElement and add it to the DOM. In styled mode, it will
  5011. * now receive a stroke width from the style sheet. In classic mode we
  5012. * will add the `stroke-width` attribute.
  5013. * - Read the computed `elem.strokeWidth()`.
  5014. * - Place it based on the stroke width.
  5015. *
  5016. * @function Highcharts.SVGElement#strokeWidth
  5017. *
  5018. * @return {number}
  5019. * The stroke width in pixels. Even if the given stroke widtch (in CSS or by
  5020. * attributes) is based on `em` or other units, the pixel size is returned.
  5021. */
  5022. SVGElement.prototype.strokeWidth = function () {
  5023. // In non-styled mode, read the stroke width as set by .attr
  5024. if (!this.renderer.styledMode) {
  5025. return this['stroke-width'] || 0;
  5026. }
  5027. // In styled mode, read computed stroke width
  5028. var val = this.getStyle('stroke-width'), ret = 0, dummy;
  5029. // Read pixel values directly
  5030. if (val.indexOf('px') === val.length - 2) {
  5031. ret = pInt(val);
  5032. // Other values like em, pt etc need to be measured
  5033. }
  5034. else if (val !== '') {
  5035. dummy = doc.createElementNS(SVG_NS, 'rect');
  5036. attr(dummy, {
  5037. width: val,
  5038. 'stroke-width': 0
  5039. });
  5040. this.element.parentNode.appendChild(dummy);
  5041. ret = dummy.getBBox().width;
  5042. dummy.parentNode.removeChild(dummy);
  5043. }
  5044. return ret;
  5045. };
  5046. /**
  5047. * If one of the symbol size affecting parameters are changed,
  5048. * check all the others only once for each call to an element's
  5049. * .attr() method
  5050. *
  5051. * @private
  5052. * @function Highcharts.SVGElement#symbolAttr
  5053. *
  5054. * @param {Highcharts.SVGAttributes} hash
  5055. * The attributes to set.
  5056. */
  5057. SVGElement.prototype.symbolAttr = function (hash) {
  5058. var wrapper = this;
  5059. [
  5060. 'x',
  5061. 'y',
  5062. 'r',
  5063. 'start',
  5064. 'end',
  5065. 'width',
  5066. 'height',
  5067. 'innerR',
  5068. 'anchorX',
  5069. 'anchorY',
  5070. 'clockwise'
  5071. ].forEach(function (key) {
  5072. wrapper[key] = pick(hash[key], wrapper[key]);
  5073. });
  5074. wrapper.attr({
  5075. d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
  5076. });
  5077. };
  5078. /**
  5079. * @private
  5080. * @function Highcharts.SVGElement#textSetter
  5081. * @param {string} value
  5082. */
  5083. SVGElement.prototype.textSetter = function (value) {
  5084. if (value !== this.textStr) {
  5085. // Delete size caches when the text changes
  5086. // delete this.bBox; // old code in series-label
  5087. delete this.textPxLength;
  5088. this.textStr = value;
  5089. if (this.added) {
  5090. this.renderer.buildText(this);
  5091. }
  5092. }
  5093. };
  5094. /**
  5095. * @private
  5096. * @function Highcharts.SVGElement#titleSetter
  5097. * @param {string} value
  5098. */
  5099. SVGElement.prototype.titleSetter = function (value) {
  5100. var titleNode = this.element.getElementsByTagName('title')[0];
  5101. if (!titleNode) {
  5102. titleNode = doc.createElementNS(this.SVG_NS, 'title');
  5103. this.element.appendChild(titleNode);
  5104. }
  5105. // Remove text content if it exists
  5106. if (titleNode.firstChild) {
  5107. titleNode.removeChild(titleNode.firstChild);
  5108. }
  5109. titleNode.appendChild(doc.createTextNode(
  5110. // #3276, #3895
  5111. String(pick(value, ''))
  5112. .replace(/<[^>]*>/g, '')
  5113. .replace(/&lt;/g, '<')
  5114. .replace(/&gt;/g, '>')));
  5115. };
  5116. /**
  5117. * Bring the element to the front. Alternatively, a new zIndex can be set.
  5118. *
  5119. * @sample highcharts/members/element-tofront/
  5120. * Click an element to bring it to front
  5121. *
  5122. * @function Highcharts.SVGElement#toFront
  5123. *
  5124. * @return {Highcharts.SVGElement}
  5125. * Returns the SVGElement for chaining.
  5126. */
  5127. SVGElement.prototype.toFront = function () {
  5128. var element = this.element;
  5129. element.parentNode.appendChild(element);
  5130. return this;
  5131. };
  5132. /**
  5133. * Move an object and its children by x and y values.
  5134. *
  5135. * @function Highcharts.SVGElement#translate
  5136. *
  5137. * @param {number} x
  5138. * The x value.
  5139. *
  5140. * @param {number} y
  5141. * The y value.
  5142. *
  5143. * @return {Highcharts.SVGElement}
  5144. */
  5145. SVGElement.prototype.translate = function (x, y) {
  5146. return this.attr({
  5147. translateX: x,
  5148. translateY: y
  5149. });
  5150. };
  5151. /**
  5152. * Update the shadow elements with new attributes.
  5153. *
  5154. * @private
  5155. * @function Highcharts.SVGElement#updateShadows
  5156. *
  5157. * @param {string} key
  5158. * The attribute name.
  5159. *
  5160. * @param {number} value
  5161. * The value of the attribute.
  5162. *
  5163. * @param {Function} setter
  5164. * The setter function, inherited from the parent wrapper.
  5165. */
  5166. SVGElement.prototype.updateShadows = function (key, value, setter) {
  5167. var shadows = this.shadows;
  5168. if (shadows) {
  5169. var i = shadows.length;
  5170. while (i--) {
  5171. setter.call(shadows[i], key === 'height' ?
  5172. Math.max(value - (shadows[i].cutHeight || 0), 0) :
  5173. key === 'd' ? this.d : value, key, shadows[i]);
  5174. }
  5175. }
  5176. };
  5177. /**
  5178. * Update the transform attribute based on internal properties. Deals with
  5179. * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
  5180. * attributes and updates the SVG `transform` attribute.
  5181. *
  5182. * @private
  5183. * @function Highcharts.SVGElement#updateTransform
  5184. */
  5185. SVGElement.prototype.updateTransform = function () {
  5186. var wrapper = this, translateX = wrapper.translateX || 0, translateY = wrapper.translateY || 0, scaleX = wrapper.scaleX, scaleY = wrapper.scaleY, inverted = wrapper.inverted, rotation = wrapper.rotation, matrix = wrapper.matrix, element = wrapper.element, transform;
  5187. // Flipping affects translate as adjustment for flipping around the
  5188. // group's axis
  5189. if (inverted) {
  5190. translateX += wrapper.width;
  5191. translateY += wrapper.height;
  5192. }
  5193. // Apply translate. Nearly all transformed elements have translation,
  5194. // so instead of checking for translate = 0, do it always (#1767,
  5195. // #1846).
  5196. transform = ['translate(' + translateX + ',' + translateY + ')'];
  5197. // apply matrix
  5198. if (defined(matrix)) {
  5199. transform.push('matrix(' + matrix.join(',') + ')');
  5200. }
  5201. // apply rotation
  5202. if (inverted) {
  5203. transform.push('rotate(90) scale(-1,1)');
  5204. }
  5205. else if (rotation) { // text rotation
  5206. transform.push('rotate(' + rotation + ' ' +
  5207. pick(this.rotationOriginX, element.getAttribute('x'), 0) +
  5208. ' ' +
  5209. pick(this.rotationOriginY, element.getAttribute('y') || 0) + ')');
  5210. }
  5211. // apply scale
  5212. if (defined(scaleX) || defined(scaleY)) {
  5213. transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
  5214. }
  5215. if (transform.length) {
  5216. element.setAttribute('transform', transform.join(' '));
  5217. }
  5218. };
  5219. /**
  5220. * @private
  5221. * @function Highcharts.SVGElement#visibilitySetter
  5222. *
  5223. * @param {string} value
  5224. *
  5225. * @param {string} key
  5226. *
  5227. * @param {Highcharts.SVGDOMElement} element
  5228. *
  5229. * @return {void}
  5230. */
  5231. SVGElement.prototype.visibilitySetter = function (value, key, element) {
  5232. // IE9-11 doesn't handle visibilty:inherit well, so we remove the
  5233. // attribute instead (#2881, #3909)
  5234. if (value === 'inherit') {
  5235. element.removeAttribute(key);
  5236. }
  5237. else if (this[key] !== value) { // #6747
  5238. element.setAttribute(key, value);
  5239. }
  5240. this[key] = value;
  5241. };
  5242. /**
  5243. * @private
  5244. * @function Highcharts.SVGElement#xGetter
  5245. *
  5246. * @param {string} key
  5247. *
  5248. * @return {number|string|null}
  5249. */
  5250. SVGElement.prototype.xGetter = function (key) {
  5251. if (this.element.nodeName === 'circle') {
  5252. if (key === 'x') {
  5253. key = 'cx';
  5254. }
  5255. else if (key === 'y') {
  5256. key = 'cy';
  5257. }
  5258. }
  5259. return this._defaultGetter(key);
  5260. };
  5261. /**
  5262. * @private
  5263. * @function Highcharts.SVGElement#zIndexSetter
  5264. * @param {number} [value]
  5265. * @param {string} [key]
  5266. * @return {boolean}
  5267. */
  5268. SVGElement.prototype.zIndexSetter = function (value, key) {
  5269. var renderer = this.renderer, parentGroup = this.parentGroup, parentWrapper = parentGroup || renderer, parentNode = parentWrapper.element || renderer.box, childNodes, otherElement, otherZIndex, element = this.element, inserted = false, undefinedOtherZIndex, svgParent = parentNode === renderer.box, run = this.added, i;
  5270. if (defined(value)) {
  5271. // So we can read it for other elements in the group
  5272. element.setAttribute('data-z-index', value);
  5273. value = +value;
  5274. if (this[key] === value) {
  5275. // Only update when needed (#3865)
  5276. run = false;
  5277. }
  5278. }
  5279. else if (defined(this[key])) {
  5280. element.removeAttribute('data-z-index');
  5281. }
  5282. this[key] = value;
  5283. // Insert according to this and other elements' zIndex. Before .add() is
  5284. // called, nothing is done. Then on add, or by later calls to
  5285. // zIndexSetter, the node is placed on the right place in the DOM.
  5286. if (run) {
  5287. value = this.zIndex;
  5288. if (value && parentGroup) {
  5289. parentGroup.handleZ = true;
  5290. }
  5291. childNodes = parentNode.childNodes;
  5292. for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
  5293. otherElement = childNodes[i];
  5294. otherZIndex = otherElement.getAttribute('data-z-index');
  5295. undefinedOtherZIndex = !defined(otherZIndex);
  5296. if (otherElement !== element) {
  5297. if (
  5298. // Negative zIndex versus no zIndex:
  5299. // On all levels except the highest. If the parent is
  5300. // <svg>, then we don't want to put items before <desc>
  5301. // or <defs>
  5302. value < 0 &&
  5303. undefinedOtherZIndex &&
  5304. !svgParent &&
  5305. !i) {
  5306. parentNode.insertBefore(element, childNodes[i]);
  5307. inserted = true;
  5308. }
  5309. else if (
  5310. // Insert after the first element with a lower zIndex
  5311. pInt(otherZIndex) <= value ||
  5312. // If negative zIndex, add this before first undefined
  5313. // zIndex element
  5314. (undefinedOtherZIndex &&
  5315. (!defined(value) || value >= 0))) {
  5316. parentNode.insertBefore(element, childNodes[i + 1] || null // null for oldIE export
  5317. );
  5318. inserted = true;
  5319. }
  5320. }
  5321. }
  5322. if (!inserted) {
  5323. parentNode.insertBefore(element, childNodes[svgParent ? 3 : 0] || null // null for oldIE
  5324. );
  5325. inserted = true;
  5326. }
  5327. }
  5328. return inserted;
  5329. };
  5330. return SVGElement;
  5331. }());
  5332. // Some shared setters and getters
  5333. SVGElement.prototype['stroke-widthSetter'] = SVGElement.prototype.strokeSetter;
  5334. SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
  5335. SVGElement.prototype.matrixSetter =
  5336. SVGElement.prototype.rotationOriginXSetter =
  5337. SVGElement.prototype.rotationOriginYSetter =
  5338. SVGElement.prototype.rotationSetter =
  5339. SVGElement.prototype.scaleXSetter =
  5340. SVGElement.prototype.scaleYSetter =
  5341. SVGElement.prototype.translateXSetter =
  5342. SVGElement.prototype.translateYSetter =
  5343. SVGElement.prototype.verticalAlignSetter = function (value, key) {
  5344. this[key] = value;
  5345. this.doTransform = true;
  5346. };
  5347. H.SVGElement = SVGElement;
  5348. return H.SVGElement;
  5349. });
  5350. _registerModule(_modules, 'parts/SVGLabel.js', [_modules['parts/SVGElement.js'], _modules['parts/Utilities.js']], function (SVGElement, U) {
  5351. /* *
  5352. *
  5353. * (c) 2010-2020 Torstein Honsi
  5354. *
  5355. * License: www.highcharts.com/license
  5356. *
  5357. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5358. *
  5359. * */
  5360. var __extends = (this && this.__extends) || (function () {
  5361. var extendStatics = function (d, b) {
  5362. extendStatics = Object.setPrototypeOf ||
  5363. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  5364. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  5365. return extendStatics(d, b);
  5366. };
  5367. return function (d, b) {
  5368. extendStatics(d, b);
  5369. function __() { this.constructor = d; }
  5370. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  5371. };
  5372. })();
  5373. var defined = U.defined, extend = U.extend, isNumber = U.isNumber, merge = U.merge, removeEvent = U.removeEvent;
  5374. /**
  5375. * SVG label to render text.
  5376. * @private
  5377. * @class
  5378. * @name Highcharts.SVGLabel
  5379. * @augments Highcharts.SVGElement
  5380. */
  5381. var SVGLabel = /** @class */ (function (_super) {
  5382. __extends(SVGLabel, _super);
  5383. /* *
  5384. *
  5385. * Constructors
  5386. *
  5387. * */
  5388. function SVGLabel(renderer, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  5389. var _this = _super.call(this) || this;
  5390. _this.init(renderer, 'g');
  5391. _this.textStr = str;
  5392. _this.x = x;
  5393. _this.y = y;
  5394. _this.anchorX = anchorX;
  5395. _this.anchorY = anchorY;
  5396. _this.baseline = baseline;
  5397. _this.className = className;
  5398. if (className !== 'button') {
  5399. _this.addClass('highcharts-label');
  5400. }
  5401. if (className) {
  5402. _this.addClass('highcharts-' + className);
  5403. }
  5404. _this.text = renderer.text('', 0, 0, useHTML)
  5405. .attr({
  5406. zIndex: 1
  5407. });
  5408. // Validate the shape argument
  5409. var hasBGImage;
  5410. if (typeof shape === 'string') {
  5411. hasBGImage = /^url\((.*?)\)$/.test(shape);
  5412. if (_this.renderer.symbols[shape] || hasBGImage) {
  5413. _this.symbolKey = shape;
  5414. }
  5415. }
  5416. _this.bBox = SVGLabel.emptyBBox;
  5417. _this.padding = 3;
  5418. _this.paddingLeft = 0;
  5419. _this.baselineOffset = 0;
  5420. _this.needsBox = renderer.styledMode || hasBGImage;
  5421. _this.deferredAttr = {};
  5422. _this.alignFactor = 0;
  5423. return _this;
  5424. }
  5425. /* *
  5426. *
  5427. * Functions
  5428. *
  5429. * */
  5430. SVGLabel.prototype.alignSetter = function (value) {
  5431. var alignFactor = {
  5432. left: 0,
  5433. center: 0.5,
  5434. right: 1
  5435. }[value];
  5436. if (alignFactor !== this.alignFactor) {
  5437. this.alignFactor = alignFactor;
  5438. // Bounding box exists, means we're dynamically changing
  5439. if (this.bBox && isNumber(this.xSetting)) {
  5440. this.attr({ x: this.xSetting }); // #5134
  5441. }
  5442. }
  5443. };
  5444. SVGLabel.prototype.anchorXSetter = function (value, key) {
  5445. this.anchorX = value;
  5446. this.boxAttr(key, Math.round(value) - this.getCrispAdjust() - this.xSetting);
  5447. };
  5448. SVGLabel.prototype.anchorYSetter = function (value, key) {
  5449. this.anchorY = value;
  5450. this.boxAttr(key, value - this.ySetting);
  5451. };
  5452. /*
  5453. * Set a box attribute, or defer it if the box is not yet created
  5454. */
  5455. SVGLabel.prototype.boxAttr = function (key, value) {
  5456. if (this.box) {
  5457. this.box.attr(key, value);
  5458. }
  5459. else {
  5460. this.deferredAttr[key] = value;
  5461. }
  5462. };
  5463. /*
  5464. * Pick up some properties and apply them to the text instead of the
  5465. * wrapper.
  5466. */
  5467. SVGLabel.prototype.css = function (styles) {
  5468. if (styles) {
  5469. var textStyles = {}, isWidth, isFontStyle;
  5470. // Create a copy to avoid altering the original object
  5471. // (#537)
  5472. styles = merge(styles);
  5473. SVGLabel.textProps.forEach(function (prop) {
  5474. if (typeof styles[prop] !== 'undefined') {
  5475. textStyles[prop] = styles[prop];
  5476. delete styles[prop];
  5477. }
  5478. });
  5479. this.text.css(textStyles);
  5480. isWidth = 'width' in textStyles;
  5481. isFontStyle = 'fontSize' in textStyles ||
  5482. 'fontWeight' in textStyles;
  5483. // Update existing text, box (#9400, #12163)
  5484. if (isWidth || isFontStyle) {
  5485. this.updateBoxSize();
  5486. // Keep updated (#9400, #12163)
  5487. if (isFontStyle) {
  5488. this.updateTextPadding();
  5489. }
  5490. }
  5491. }
  5492. return SVGElement.prototype.css.call(this, styles);
  5493. };
  5494. /*
  5495. * Destroy and release memory.
  5496. */
  5497. SVGLabel.prototype.destroy = function () {
  5498. // Added by button implementation
  5499. removeEvent(this.element, 'mouseenter');
  5500. removeEvent(this.element, 'mouseleave');
  5501. if (this.text) {
  5502. this.text.destroy();
  5503. }
  5504. if (this.box) {
  5505. this.box = this.box.destroy();
  5506. }
  5507. // Call base implementation to destroy the rest
  5508. SVGElement.prototype.destroy.call(this);
  5509. return void 0;
  5510. };
  5511. SVGLabel.prototype.fillSetter = function (value, key) {
  5512. if (value) {
  5513. this.needsBox = true;
  5514. }
  5515. // for animation getter (#6776)
  5516. this.fill = value;
  5517. this.boxAttr(key, value);
  5518. };
  5519. /*
  5520. * Return the bounding box of the box, not the group.
  5521. */
  5522. SVGLabel.prototype.getBBox = function () {
  5523. var bBox = this.bBox;
  5524. var padding = this.padding;
  5525. return {
  5526. width: bBox.width + 2 * padding,
  5527. height: bBox.height + 2 * padding,
  5528. x: bBox.x - padding,
  5529. y: bBox.y - padding
  5530. };
  5531. };
  5532. SVGLabel.prototype.getCrispAdjust = function () {
  5533. return this.renderer.styledMode && this.box ?
  5534. this.box.strokeWidth() % 2 / 2 :
  5535. (this['stroke-width'] ? parseInt(this['stroke-width'], 10) : 0) % 2 / 2;
  5536. };
  5537. SVGLabel.prototype.heightSetter = function (value) {
  5538. this.heightSetting = value;
  5539. };
  5540. // Event handling. In case of useHTML, we need to make sure that events
  5541. // are captured on the span as well, and that mouseenter/mouseleave
  5542. // between the SVG group and the HTML span are not treated as real
  5543. // enter/leave events. #13310.
  5544. SVGLabel.prototype.on = function (eventType, handler) {
  5545. var label = this;
  5546. var text = label.text;
  5547. var span = text && text.element.tagName === 'SPAN' ? text : void 0;
  5548. var selectiveHandler;
  5549. if (span) {
  5550. selectiveHandler = function (e) {
  5551. if ((eventType === 'mouseenter' ||
  5552. eventType === 'mouseleave') &&
  5553. e.relatedTarget instanceof Element &&
  5554. (label.element.contains(e.relatedTarget) ||
  5555. span.element.contains(e.relatedTarget))) {
  5556. return;
  5557. }
  5558. handler.call(label.element, e);
  5559. };
  5560. span.on(eventType, selectiveHandler);
  5561. }
  5562. SVGElement.prototype.on.call(label, eventType, selectiveHandler || handler);
  5563. return label;
  5564. };
  5565. /*
  5566. * After the text element is added, get the desired size of the border
  5567. * box and add it before the text in the DOM.
  5568. */
  5569. SVGLabel.prototype.onAdd = function () {
  5570. var str = this.textStr;
  5571. this.text.add(this);
  5572. this.attr({
  5573. // Alignment is available now (#3295, 0 not rendered if given
  5574. // as a value)
  5575. text: (defined(str) ? str : ''),
  5576. x: this.x,
  5577. y: this.y
  5578. });
  5579. if (this.box && defined(this.anchorX)) {
  5580. this.attr({
  5581. anchorX: this.anchorX,
  5582. anchorY: this.anchorY
  5583. });
  5584. }
  5585. };
  5586. SVGLabel.prototype.paddingSetter = function (value) {
  5587. if (defined(value) && value !== this.padding) {
  5588. this.padding = value;
  5589. this.updateTextPadding();
  5590. }
  5591. };
  5592. SVGLabel.prototype.paddingLeftSetter = function (value) {
  5593. if (defined(value) && value !== this.paddingLeft) {
  5594. this.paddingLeft = value;
  5595. this.updateTextPadding();
  5596. }
  5597. };
  5598. SVGLabel.prototype.rSetter = function (value, key) {
  5599. this.boxAttr(key, value);
  5600. };
  5601. SVGLabel.prototype.shadow = function (b) {
  5602. if (b && !this.renderer.styledMode) {
  5603. this.updateBoxSize();
  5604. if (this.box) {
  5605. this.box.shadow(b);
  5606. }
  5607. }
  5608. return this;
  5609. };
  5610. SVGLabel.prototype.strokeSetter = function (value, key) {
  5611. // for animation getter (#6776)
  5612. this.stroke = value;
  5613. this.boxAttr(key, value);
  5614. };
  5615. SVGLabel.prototype['stroke-widthSetter'] = function (value, key) {
  5616. if (value) {
  5617. this.needsBox = true;
  5618. }
  5619. this['stroke-width'] = value;
  5620. this.boxAttr(key, value);
  5621. };
  5622. SVGLabel.prototype['text-alignSetter'] = function (value) {
  5623. this.textAlign = value;
  5624. };
  5625. SVGLabel.prototype.textSetter = function (text) {
  5626. if (typeof text !== 'undefined') {
  5627. // Must use .attr to ensure transforms are done (#10009)
  5628. this.text.attr({ text: text });
  5629. }
  5630. this.updateBoxSize();
  5631. this.updateTextPadding();
  5632. };
  5633. /*
  5634. * This function runs after the label is added to the DOM (when the bounding
  5635. * box is available), and after the text of the label is updated to detect
  5636. * the new bounding box and reflect it in the border box.
  5637. */
  5638. SVGLabel.prototype.updateBoxSize = function () {
  5639. var style = this.text.element.style, crispAdjust, attribs = {};
  5640. var padding = this.padding;
  5641. var paddingLeft = this.paddingLeft;
  5642. // #12165 error when width is null (auto)
  5643. // #12163 when fontweight: bold, recalculate bBox withot cache
  5644. // #3295 && 3514 box failure when string equals 0
  5645. var bBox = ((!isNumber(this.widthSetting) || !isNumber(this.heightSetting) || this.textAlign) &&
  5646. defined(this.text.textStr)) ?
  5647. this.text.getBBox() : SVGLabel.emptyBBox;
  5648. this.width = ((this.widthSetting || bBox.width || 0) +
  5649. 2 * padding +
  5650. paddingLeft);
  5651. this.height = (this.heightSetting || bBox.height || 0) + 2 * padding;
  5652. // Update the label-scoped y offset. Math.min because of inline
  5653. // style (#9400)
  5654. this.baselineOffset = padding + Math.min(this.renderer.fontMetrics(style && style.fontSize, this.text).b,
  5655. // When the height is 0, there is no bBox, so go with the font
  5656. // metrics. Highmaps CSS demos.
  5657. bBox.height || Infinity);
  5658. if (this.needsBox) {
  5659. // Create the border box if it is not already present
  5660. if (!this.box) {
  5661. // Symbol definition exists (#5324)
  5662. var box = this.box = this.symbolKey ?
  5663. this.renderer.symbol(this.symbolKey) :
  5664. this.renderer.rect();
  5665. box.addClass(// Don't use label className for buttons
  5666. (this.className === 'button' ? '' : 'highcharts-label-box') +
  5667. (this.className ? ' highcharts-' + this.className + '-box' : ''));
  5668. box.add(this);
  5669. crispAdjust = this.getCrispAdjust();
  5670. attribs.x = crispAdjust;
  5671. attribs.y = (this.baseline ? -this.baselineOffset : 0) + crispAdjust;
  5672. }
  5673. // Apply the box attributes
  5674. attribs.width = Math.round(this.width);
  5675. attribs.height = Math.round(this.height);
  5676. this.box.attr(extend(attribs, this.deferredAttr));
  5677. this.deferredAttr = {};
  5678. }
  5679. this.bBox = bBox;
  5680. };
  5681. /*
  5682. * This function runs after setting text or padding, but only if padding
  5683. * is changed.
  5684. */
  5685. SVGLabel.prototype.updateTextPadding = function () {
  5686. var text = this.text;
  5687. // Determine y based on the baseline
  5688. var textY = this.baseline ? 0 : this.baselineOffset;
  5689. var textX = this.paddingLeft + this.padding;
  5690. // compensate for alignment
  5691. if (defined(this.widthSetting) &&
  5692. this.bBox &&
  5693. (this.textAlign === 'center' || this.textAlign === 'right')) {
  5694. textX += { center: 0.5, right: 1 }[this.textAlign] *
  5695. (this.widthSetting - this.bBox.width);
  5696. }
  5697. // update if anything changed
  5698. if (textX !== text.x || textY !== text.y) {
  5699. text.attr('x', textX);
  5700. // #8159 - prevent misplaced data labels in treemap
  5701. // (useHTML: true)
  5702. if (text.hasBoxWidthChanged) {
  5703. this.bBox = text.getBBox(true);
  5704. this.updateBoxSize();
  5705. }
  5706. if (typeof textY !== 'undefined') {
  5707. text.attr('y', textY);
  5708. }
  5709. }
  5710. // record current values
  5711. text.x = textX;
  5712. text.y = textY;
  5713. };
  5714. SVGLabel.prototype.widthSetter = function (value) {
  5715. // width:auto => null
  5716. this.widthSetting = isNumber(value) ? value : void 0;
  5717. };
  5718. SVGLabel.prototype.xSetter = function (value) {
  5719. this.x = value; // for animation getter
  5720. if (this.alignFactor) {
  5721. value -= this.alignFactor * ((this.widthSetting || this.bBox.width) +
  5722. 2 * this.padding);
  5723. // Force animation even when setting to the same value (#7898)
  5724. this['forceAnimate:x'] = true;
  5725. }
  5726. this.xSetting = Math.round(value);
  5727. this.attr('translateX', this.xSetting);
  5728. };
  5729. SVGLabel.prototype.ySetter = function (value) {
  5730. this.ySetting = this.y = Math.round(value);
  5731. this.attr('translateY', this.ySetting);
  5732. };
  5733. /* *
  5734. *
  5735. * Static Properties
  5736. *
  5737. * */
  5738. SVGLabel.emptyBBox = { width: 0, height: 0, x: 0, y: 0 };
  5739. /* *
  5740. *
  5741. * Properties
  5742. *
  5743. * */
  5744. /**
  5745. * For labels, these CSS properties are applied to the `text` node directly.
  5746. *
  5747. * @private
  5748. * @name Highcharts.SVGLabel#textProps
  5749. * @type {Array<string>}
  5750. */
  5751. SVGLabel.textProps = [
  5752. 'color', 'cursor', 'direction', 'fontFamily', 'fontSize', 'fontStyle',
  5753. 'fontWeight', 'lineHeight', 'textAlign', 'textDecoration',
  5754. 'textOutline', 'textOverflow', 'width'
  5755. ];
  5756. return SVGLabel;
  5757. }(SVGElement));
  5758. return SVGLabel;
  5759. });
  5760. _registerModule(_modules, 'parts/SVGRenderer.js', [_modules['parts/Color.js'], _modules['parts/Globals.js'], _modules['parts/SVGElement.js'], _modules['parts/SVGLabel.js'], _modules['parts/Utilities.js']], function (Color, H, SVGElement, SVGLabel, U) {
  5761. /* *
  5762. *
  5763. * (c) 2010-2020 Torstein Honsi
  5764. *
  5765. * License: www.highcharts.com/license
  5766. *
  5767. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  5768. *
  5769. * */
  5770. var addEvent = U.addEvent, attr = U.attr, createElement = U.createElement, css = U.css, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, merge = U.merge, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, removeEvent = U.removeEvent, splat = U.splat, stop = U.stop, uniqueKey = U.uniqueKey;
  5771. /**
  5772. * A clipping rectangle that can be applied to one or more {@link SVGElement}
  5773. * instances. It is instanciated with the {@link SVGRenderer#clipRect} function
  5774. * and applied with the {@link SVGElement#clip} function.
  5775. *
  5776. * @example
  5777. * var circle = renderer.circle(100, 100, 100)
  5778. * .attr({ fill: 'red' })
  5779. * .add();
  5780. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  5781. *
  5782. * // Leave only the lower right quarter visible
  5783. * circle.clip(clipRect);
  5784. *
  5785. * @typedef {Highcharts.SVGElement} Highcharts.ClipRectElement
  5786. */
  5787. /**
  5788. * The font metrics.
  5789. *
  5790. * @interface Highcharts.FontMetricsObject
  5791. */ /**
  5792. * The baseline relative to the top of the box.
  5793. *
  5794. * @name Highcharts.FontMetricsObject#b
  5795. * @type {number}
  5796. */ /**
  5797. * The font size.
  5798. *
  5799. * @name Highcharts.FontMetricsObject#f
  5800. * @type {number}
  5801. */ /**
  5802. * The line height.
  5803. *
  5804. * @name Highcharts.FontMetricsObject#h
  5805. * @type {number}
  5806. */
  5807. /**
  5808. * An object containing `x` and `y` properties for the position of an element.
  5809. *
  5810. * @interface Highcharts.PositionObject
  5811. */ /**
  5812. * X position of the element.
  5813. * @name Highcharts.PositionObject#x
  5814. * @type {number}
  5815. */ /**
  5816. * Y position of the element.
  5817. * @name Highcharts.PositionObject#y
  5818. * @type {number}
  5819. */
  5820. /**
  5821. * A rectangle.
  5822. *
  5823. * @interface Highcharts.RectangleObject
  5824. */ /**
  5825. * Height of the rectangle.
  5826. * @name Highcharts.RectangleObject#height
  5827. * @type {number}
  5828. */ /**
  5829. * Width of the rectangle.
  5830. * @name Highcharts.RectangleObject#width
  5831. * @type {number}
  5832. */ /**
  5833. * Horizontal position of the rectangle.
  5834. * @name Highcharts.RectangleObject#x
  5835. * @type {number}
  5836. */ /**
  5837. * Vertical position of the rectangle.
  5838. * @name Highcharts.RectangleObject#y
  5839. * @type {number}
  5840. */
  5841. /**
  5842. * The shadow options.
  5843. *
  5844. * @interface Highcharts.ShadowOptionsObject
  5845. */ /**
  5846. * The shadow color.
  5847. * @name Highcharts.ShadowOptionsObject#color
  5848. * @type {Highcharts.ColorString|undefined}
  5849. * @default #000000
  5850. */ /**
  5851. * The horizontal offset from the element.
  5852. *
  5853. * @name Highcharts.ShadowOptionsObject#offsetX
  5854. * @type {number|undefined}
  5855. * @default 1
  5856. */ /**
  5857. * The vertical offset from the element.
  5858. * @name Highcharts.ShadowOptionsObject#offsetY
  5859. * @type {number|undefined}
  5860. * @default 1
  5861. */ /**
  5862. * The shadow opacity.
  5863. *
  5864. * @name Highcharts.ShadowOptionsObject#opacity
  5865. * @type {number|undefined}
  5866. * @default 0.15
  5867. */ /**
  5868. * The shadow width or distance from the element.
  5869. * @name Highcharts.ShadowOptionsObject#width
  5870. * @type {number|undefined}
  5871. * @default 3
  5872. */
  5873. /**
  5874. * @interface Highcharts.SizeObject
  5875. */ /**
  5876. * @name Highcharts.SizeObject#height
  5877. * @type {number}
  5878. */ /**
  5879. * @name Highcharts.SizeObject#width
  5880. * @type {number}
  5881. */
  5882. /**
  5883. * Serialized form of an SVG definition, including children. Some key
  5884. * property names are reserved: tagName, textContent, and children.
  5885. *
  5886. * @interface Highcharts.SVGDefinitionObject
  5887. */ /**
  5888. * @name Highcharts.SVGDefinitionObject#[key:string]
  5889. * @type {boolean|number|string|Array<Highcharts.SVGDefinitionObject>|undefined}
  5890. */ /**
  5891. * @name Highcharts.SVGDefinitionObject#children
  5892. * @type {Array<Highcharts.SVGDefinitionObject>|undefined}
  5893. */ /**
  5894. * @name Highcharts.SVGDefinitionObject#tagName
  5895. * @type {string|undefined}
  5896. */ /**
  5897. * @name Highcharts.SVGDefinitionObject#textContent
  5898. * @type {string|undefined}
  5899. */
  5900. /**
  5901. * Array of path commands, that will go into the `d` attribute of an SVG
  5902. * element.
  5903. *
  5904. * @typedef {Array<Array<Highcharts.SVGPathCommand,number?,number?,number?,number?,number?,number?,number?>>} Highcharts.SVGPathArray
  5905. */
  5906. /**
  5907. * Possible path commands in an SVG path array. Valid values are `A`, `C`, `H`,
  5908. * `L`, `M`, `Q`, `S`, `T`, `V`, `Z`.
  5909. *
  5910. * @typedef {string} Highcharts.SVGPathCommand
  5911. * @validvalue ["a","c","h","l","m","q","s","t","v","z","A","C","H","L","M","Q","S","T","V","Z"]
  5912. */
  5913. /**
  5914. * An extendable collection of functions for defining symbol paths. Symbols are
  5915. * used internally for point markers, button and label borders and backgrounds,
  5916. * or custom shapes. Extendable by adding to {@link SVGRenderer#symbols}.
  5917. *
  5918. * @interface Highcharts.SymbolDictionary
  5919. */ /**
  5920. * @name Highcharts.SymbolDictionary#[key:string]
  5921. * @type {Function|undefined}
  5922. */ /**
  5923. * @name Highcharts.SymbolDictionary#arc
  5924. * @type {Function|undefined}
  5925. */ /**
  5926. * @name Highcharts.SymbolDictionary#callout
  5927. * @type {Function|undefined}
  5928. */ /**
  5929. * @name Highcharts.SymbolDictionary#circle
  5930. * @type {Function|undefined}
  5931. */ /**
  5932. * @name Highcharts.SymbolDictionary#diamond
  5933. * @type {Function|undefined}
  5934. */ /**
  5935. * @name Highcharts.SymbolDictionary#square
  5936. * @type {Function|undefined}
  5937. */ /**
  5938. * @name Highcharts.SymbolDictionary#triangle
  5939. * @type {Function|undefined}
  5940. */
  5941. /**
  5942. * Can be one of `arc`, `callout`, `circle`, `diamond`, `square`, `triangle`,
  5943. * and `triangle-down`. Symbols are used internally for point markers, button
  5944. * and label borders and backgrounds, or custom shapes. Extendable by adding to
  5945. * {@link SVGRenderer#symbols}.
  5946. *
  5947. * @typedef {"arc"|"callout"|"circle"|"diamond"|"square"|"triangle"|"triangle-down"} Highcharts.SymbolKeyValue
  5948. */
  5949. /**
  5950. * Additional options, depending on the actual symbol drawn.
  5951. *
  5952. * @interface Highcharts.SymbolOptionsObject
  5953. */ /**
  5954. * The anchor X position for the `callout` symbol. This is where the chevron
  5955. * points to.
  5956. *
  5957. * @name Highcharts.SymbolOptionsObject#anchorX
  5958. * @type {number|undefined}
  5959. */ /**
  5960. * The anchor Y position for the `callout` symbol. This is where the chevron
  5961. * points to.
  5962. *
  5963. * @name Highcharts.SymbolOptionsObject#anchorY
  5964. * @type {number|undefined}
  5965. */ /**
  5966. * The end angle of an `arc` symbol.
  5967. *
  5968. * @name Highcharts.SymbolOptionsObject#end
  5969. * @type {number|undefined}
  5970. */ /**
  5971. * Whether to draw `arc` symbol open or closed.
  5972. *
  5973. * @name Highcharts.SymbolOptionsObject#open
  5974. * @type {boolean|undefined}
  5975. */ /**
  5976. * The radius of an `arc` symbol, or the border radius for the `callout` symbol.
  5977. *
  5978. * @name Highcharts.SymbolOptionsObject#r
  5979. * @type {number|undefined}
  5980. */ /**
  5981. * The start angle of an `arc` symbol.
  5982. *
  5983. * @name Highcharts.SymbolOptionsObject#start
  5984. * @type {number|undefined}
  5985. */
  5986. /* eslint-disable no-invalid-this, valid-jsdoc */
  5987. var charts = H.charts, deg2rad = H.deg2rad, doc = H.doc, isFirefox = H.isFirefox, isMS = H.isMS, isWebKit = H.isWebKit, noop = H.noop, svg = H.svg, SVG_NS = H.SVG_NS, symbolSizes = H.symbolSizes, win = H.win;
  5988. /**
  5989. * Allows direct access to the Highcharts rendering layer in order to draw
  5990. * primitive shapes like circles, rectangles, paths or text directly on a chart,
  5991. * or independent from any chart. The SVGRenderer represents a wrapper object
  5992. * for SVG in modern browsers. Through the VMLRenderer, part of the `oldie.js`
  5993. * module, it also brings vector graphics to IE <= 8.
  5994. *
  5995. * An existing chart's renderer can be accessed through {@link Chart.renderer}.
  5996. * The renderer can also be used completely decoupled from a chart.
  5997. *
  5998. * @sample highcharts/members/renderer-on-chart
  5999. * Annotating a chart programmatically.
  6000. * @sample highcharts/members/renderer-basic
  6001. * Independent SVG drawing.
  6002. *
  6003. * @example
  6004. * // Use directly without a chart object.
  6005. * var renderer = new Highcharts.Renderer(parentNode, 600, 400);
  6006. *
  6007. * @class
  6008. * @name Highcharts.SVGRenderer
  6009. *
  6010. * @param {Highcharts.HTMLDOMElement} container
  6011. * Where to put the SVG in the web page.
  6012. *
  6013. * @param {number} width
  6014. * The width of the SVG.
  6015. *
  6016. * @param {number} height
  6017. * The height of the SVG.
  6018. *
  6019. * @param {Highcharts.CSSObject} [style]
  6020. * The box style, if not in styleMode
  6021. *
  6022. * @param {boolean} [forExport=false]
  6023. * Whether the rendered content is intended for export.
  6024. *
  6025. * @param {boolean} [allowHTML=true]
  6026. * Whether the renderer is allowed to include HTML text, which will be
  6027. * projected on top of the SVG.
  6028. *
  6029. * @param {boolean} [styledMode=false]
  6030. * Whether the renderer belongs to a chart that is in styled mode.
  6031. * If it does, it will avoid setting presentational attributes in
  6032. * some cases, but not when set explicitly through `.attr` and `.css`
  6033. * etc.
  6034. */
  6035. var SVGRenderer = /** @class */ (function () {
  6036. /* *
  6037. *
  6038. * Constructors
  6039. *
  6040. * */
  6041. function SVGRenderer(container, width, height, style, forExport, allowHTML, styledMode) {
  6042. /* *
  6043. *
  6044. * Properties
  6045. *
  6046. * */
  6047. this.alignedObjects = void 0;
  6048. /**
  6049. * The root `svg` node of the renderer.
  6050. *
  6051. * @name Highcharts.SVGRenderer#box
  6052. * @type {Highcharts.SVGDOMElement}
  6053. */
  6054. this.box = void 0;
  6055. /**
  6056. * The wrapper for the root `svg` node of the renderer.
  6057. *
  6058. * @name Highcharts.SVGRenderer#boxWrapper
  6059. * @type {Highcharts.SVGElement}
  6060. */
  6061. this.boxWrapper = void 0;
  6062. this.cache = void 0;
  6063. this.cacheKeys = void 0;
  6064. this.chartIndex = void 0;
  6065. /**
  6066. * A pointer to the `defs` node of the root SVG.
  6067. *
  6068. * @name Highcharts.SVGRenderer#defs
  6069. * @type {Highcharts.SVGElement}
  6070. */
  6071. this.defs = void 0;
  6072. this.globalAnimation = void 0;
  6073. this.gradients = void 0;
  6074. this.height = void 0;
  6075. this.imgCount = void 0;
  6076. this.isSVG = void 0;
  6077. this.style = void 0;
  6078. /**
  6079. * Page url used for internal references.
  6080. *
  6081. * @private
  6082. * @name Highcharts.SVGRenderer#url
  6083. * @type {string}
  6084. */
  6085. this.url = void 0;
  6086. this.width = void 0;
  6087. this.init(container, width, height, style, forExport, allowHTML, styledMode);
  6088. }
  6089. /* *
  6090. *
  6091. * Functions
  6092. *
  6093. * */
  6094. /**
  6095. * Initialize the SVGRenderer. Overridable initializer function that takes
  6096. * the same parameters as the constructor.
  6097. *
  6098. * @function Highcharts.SVGRenderer#init
  6099. *
  6100. * @param {Highcharts.HTMLDOMElement} container
  6101. * Where to put the SVG in the web page.
  6102. *
  6103. * @param {number} width
  6104. * The width of the SVG.
  6105. *
  6106. * @param {number} height
  6107. * The height of the SVG.
  6108. *
  6109. * @param {Highcharts.CSSObject} [style]
  6110. * The box style, if not in styleMode
  6111. *
  6112. * @param {boolean} [forExport=false]
  6113. * Whether the rendered content is intended for export.
  6114. *
  6115. * @param {boolean} [allowHTML=true]
  6116. * Whether the renderer is allowed to include HTML text, which will be
  6117. * projected on top of the SVG.
  6118. *
  6119. * @param {boolean} [styledMode=false]
  6120. * Whether the renderer belongs to a chart that is in styled mode. If it
  6121. * does, it will avoid setting presentational attributes in some cases, but
  6122. * not when set explicitly through `.attr` and `.css` etc.
  6123. */
  6124. SVGRenderer.prototype.init = function (container, width, height, style, forExport, allowHTML, styledMode) {
  6125. var renderer = this, boxWrapper, element, desc;
  6126. boxWrapper = renderer.createElement('svg')
  6127. .attr({
  6128. version: '1.1',
  6129. 'class': 'highcharts-root'
  6130. });
  6131. if (!styledMode) {
  6132. boxWrapper.css(this.getStyle(style));
  6133. }
  6134. element = boxWrapper.element;
  6135. container.appendChild(element);
  6136. // Always use ltr on the container, otherwise text-anchor will be
  6137. // flipped and text appear outside labels, buttons, tooltip etc (#3482)
  6138. attr(container, 'dir', 'ltr');
  6139. // For browsers other than IE, add the namespace attribute (#1978)
  6140. if (container.innerHTML.indexOf('xmlns') === -1) {
  6141. attr(element, 'xmlns', this.SVG_NS);
  6142. }
  6143. // object properties
  6144. renderer.isSVG = true;
  6145. this.box = element;
  6146. this.boxWrapper = boxWrapper;
  6147. renderer.alignedObjects = [];
  6148. // #24, #672, #1070
  6149. this.url = ((isFirefox || isWebKit) &&
  6150. doc.getElementsByTagName('base').length) ?
  6151. win.location.href
  6152. .split('#')[0] // remove the hash
  6153. .replace(/<[^>]*>/g, '') // wing cut HTML
  6154. // escape parantheses and quotes
  6155. .replace(/([\('\)])/g, '\\$1')
  6156. // replace spaces (needed for Safari only)
  6157. .replace(/ /g, '%20') :
  6158. '';
  6159. // Add description
  6160. desc = this.createElement('desc').add();
  6161. desc.element.appendChild(doc.createTextNode('Created with Highcharts 8.1.2'));
  6162. renderer.defs = this.createElement('defs').add();
  6163. renderer.allowHTML = allowHTML;
  6164. renderer.forExport = forExport;
  6165. renderer.styledMode = styledMode;
  6166. renderer.gradients = {}; // Object where gradient SvgElements are stored
  6167. renderer.cache = {}; // Cache for numerical bounding boxes
  6168. renderer.cacheKeys = [];
  6169. renderer.imgCount = 0;
  6170. renderer.setSize(width, height, false);
  6171. // Issue 110 workaround:
  6172. // In Firefox, if a div is positioned by percentage, its pixel position
  6173. // may land between pixels. The container itself doesn't display this,
  6174. // but an SVG element inside this container will be drawn at subpixel
  6175. // precision. In order to draw sharp lines, this must be compensated
  6176. // for. This doesn't seem to work inside iframes though (like in
  6177. // jsFiddle).
  6178. var subPixelFix, rect;
  6179. if (isFirefox && container.getBoundingClientRect) {
  6180. subPixelFix = function () {
  6181. css(container, { left: 0, top: 0 });
  6182. rect = container.getBoundingClientRect();
  6183. css(container, {
  6184. left: (Math.ceil(rect.left) - rect.left) + 'px',
  6185. top: (Math.ceil(rect.top) - rect.top) + 'px'
  6186. });
  6187. };
  6188. // run the fix now
  6189. subPixelFix();
  6190. // run it on resize
  6191. renderer.unSubPixelFix = addEvent(win, 'resize', subPixelFix);
  6192. }
  6193. };
  6194. /**
  6195. * General method for adding a definition to the SVG `defs` tag. Can be used
  6196. * for gradients, fills, filters etc. Styled mode only. A hook for adding
  6197. * general definitions to the SVG's defs tag. Definitions can be referenced
  6198. * from the CSS by its `id`. Read more in
  6199. * [gradients, shadows and patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns).
  6200. * Styled mode only.
  6201. *
  6202. * @function Highcharts.SVGRenderer#definition
  6203. *
  6204. * @param {Highcharts.SVGDefinitionObject} def
  6205. * A serialized form of an SVG definition, including children.
  6206. *
  6207. * @return {Highcharts.SVGElement}
  6208. * The inserted node.
  6209. */
  6210. SVGRenderer.prototype.definition = function (def) {
  6211. var ren = this;
  6212. /**
  6213. * @private
  6214. * @param {Highcharts.SVGDefinitionObject} config - SVG definition
  6215. * @param {Highcharts.SVGElement} [parent] - parent node
  6216. */
  6217. function recurse(config, parent) {
  6218. var ret;
  6219. splat(config).forEach(function (item) {
  6220. var node = ren.createElement(item.tagName), attr = {};
  6221. // Set attributes
  6222. objectEach(item, function (val, key) {
  6223. if (key !== 'tagName' &&
  6224. key !== 'children' &&
  6225. key !== 'textContent') {
  6226. attr[key] = val;
  6227. }
  6228. });
  6229. node.attr(attr);
  6230. // Add to the tree
  6231. node.add(parent || ren.defs);
  6232. // Add text content
  6233. if (item.textContent) {
  6234. node.element.appendChild(doc.createTextNode(item.textContent));
  6235. }
  6236. // Recurse
  6237. recurse(item.children || [], node);
  6238. ret = node;
  6239. });
  6240. // Return last node added (on top level it's the only one)
  6241. return ret;
  6242. }
  6243. return recurse(def);
  6244. };
  6245. /**
  6246. * Get the global style setting for the renderer.
  6247. *
  6248. * @private
  6249. * @function Highcharts.SVGRenderer#getStyle
  6250. *
  6251. * @param {Highcharts.CSSObject} style
  6252. * Style settings.
  6253. *
  6254. * @return {Highcharts.CSSObject}
  6255. * The style settings mixed with defaults.
  6256. */
  6257. SVGRenderer.prototype.getStyle = function (style) {
  6258. this.style = extend({
  6259. fontFamily: '"Lucida Grande", "Lucida Sans Unicode", ' +
  6260. 'Arial, Helvetica, sans-serif',
  6261. fontSize: '12px'
  6262. }, style);
  6263. return this.style;
  6264. };
  6265. /**
  6266. * Apply the global style on the renderer, mixed with the default styles.
  6267. *
  6268. * @function Highcharts.SVGRenderer#setStyle
  6269. *
  6270. * @param {Highcharts.CSSObject} style
  6271. * CSS to apply.
  6272. */
  6273. SVGRenderer.prototype.setStyle = function (style) {
  6274. this.boxWrapper.css(this.getStyle(style));
  6275. };
  6276. /**
  6277. * Detect whether the renderer is hidden. This happens when one of the
  6278. * parent elements has `display: none`. Used internally to detect when we
  6279. * needto render preliminarily in another div to get the text bounding boxes
  6280. * right.
  6281. *
  6282. * @function Highcharts.SVGRenderer#isHidden
  6283. *
  6284. * @return {boolean}
  6285. * True if it is hidden.
  6286. */
  6287. SVGRenderer.prototype.isHidden = function () {
  6288. return !this.boxWrapper.getBBox().width;
  6289. };
  6290. /**
  6291. * Destroys the renderer and its allocated members.
  6292. *
  6293. * @function Highcharts.SVGRenderer#destroy
  6294. *
  6295. * @return {null}
  6296. */
  6297. SVGRenderer.prototype.destroy = function () {
  6298. var renderer = this, rendererDefs = renderer.defs;
  6299. renderer.box = null;
  6300. renderer.boxWrapper = renderer.boxWrapper.destroy();
  6301. // Call destroy on all gradient elements
  6302. destroyObjectProperties(renderer.gradients || {});
  6303. renderer.gradients = null;
  6304. // Defs are null in VMLRenderer
  6305. // Otherwise, destroy them here.
  6306. if (rendererDefs) {
  6307. renderer.defs = rendererDefs.destroy();
  6308. }
  6309. // Remove sub pixel fix handler (#982)
  6310. if (renderer.unSubPixelFix) {
  6311. renderer.unSubPixelFix();
  6312. }
  6313. renderer.alignedObjects = null;
  6314. return null;
  6315. };
  6316. /**
  6317. * Create a wrapper for an SVG element. Serves as a factory for
  6318. * {@link SVGElement}, but this function is itself mostly called from
  6319. * primitive factories like {@link SVGRenderer#path}, {@link
  6320. * SVGRenderer#rect} or {@link SVGRenderer#text}.
  6321. *
  6322. * @function Highcharts.SVGRenderer#createElement
  6323. *
  6324. * @param {string} nodeName
  6325. * The node name, for example `rect`, `g` etc.
  6326. *
  6327. * @return {Highcharts.SVGElement}
  6328. * The generated SVGElement.
  6329. */
  6330. SVGRenderer.prototype.createElement = function (nodeName) {
  6331. var wrapper = new this.Element();
  6332. wrapper.init(this, nodeName);
  6333. return wrapper;
  6334. };
  6335. /**
  6336. * Get converted radial gradient attributes according to the radial
  6337. * reference. Used internally from the {@link SVGElement#colorGradient}
  6338. * function.
  6339. *
  6340. * @private
  6341. * @function Highcharts.SVGRenderer#getRadialAttr
  6342. */
  6343. SVGRenderer.prototype.getRadialAttr = function (radialReference, gradAttr) {
  6344. return {
  6345. cx: (radialReference[0] - radialReference[2] / 2) +
  6346. gradAttr.cx * radialReference[2],
  6347. cy: (radialReference[1] - radialReference[2] / 2) +
  6348. gradAttr.cy * radialReference[2],
  6349. r: gradAttr.r * radialReference[2]
  6350. };
  6351. };
  6352. /**
  6353. * Truncate the text node contents to a given length. Used when the css
  6354. * width is set. If the `textOverflow` is `ellipsis`, the text is truncated
  6355. * character by character to the given length. If not, the text is
  6356. * word-wrapped line by line.
  6357. *
  6358. * @private
  6359. * @function Highcharts.SVGRenderer#truncate
  6360. *
  6361. * @return {boolean}
  6362. * True if tspan is too long.
  6363. */
  6364. SVGRenderer.prototype.truncate = function (wrapper, tspan, text, words, startAt, width, getString) {
  6365. var renderer = this, rotation = wrapper.rotation, str,
  6366. // Word wrap can not be truncated to shorter than one word, ellipsis
  6367. // text can be completely blank.
  6368. minIndex = words ? 1 : 0, maxIndex = (text || words).length, currentIndex = maxIndex,
  6369. // Cache the lengths to avoid checking the same twice
  6370. lengths = [], updateTSpan = function (s) {
  6371. if (tspan.firstChild) {
  6372. tspan.removeChild(tspan.firstChild);
  6373. }
  6374. if (s) {
  6375. tspan.appendChild(doc.createTextNode(s));
  6376. }
  6377. }, getSubStringLength = function (charEnd, concatenatedEnd) {
  6378. // charEnd is useed when finding the character-by-character
  6379. // break for ellipsis, concatenatedEnd is used for word-by-word
  6380. // break for word wrapping.
  6381. var end = concatenatedEnd || charEnd;
  6382. if (typeof lengths[end] === 'undefined') {
  6383. // Modern browsers
  6384. if (tspan.getSubStringLength) {
  6385. // Fails with DOM exception on unit-tests/legend/members
  6386. // of unknown reason. Desired width is 0, text content
  6387. // is "5" and end is 1.
  6388. try {
  6389. lengths[end] = startAt +
  6390. tspan.getSubStringLength(0, words ? end + 1 : end);
  6391. }
  6392. catch (e) {
  6393. '';
  6394. }
  6395. // Legacy
  6396. }
  6397. else if (renderer.getSpanWidth) { // #9058 jsdom
  6398. updateTSpan(getString(text || words, charEnd));
  6399. lengths[end] = startAt +
  6400. renderer.getSpanWidth(wrapper, tspan);
  6401. }
  6402. }
  6403. return lengths[end];
  6404. }, actualWidth, truncated;
  6405. wrapper.rotation = 0; // discard rotation when computing box
  6406. actualWidth = getSubStringLength(tspan.textContent.length);
  6407. truncated = startAt + actualWidth > width;
  6408. if (truncated) {
  6409. // Do a binary search for the index where to truncate the text
  6410. while (minIndex <= maxIndex) {
  6411. currentIndex = Math.ceil((minIndex + maxIndex) / 2);
  6412. // When checking words for word-wrap, we need to build the
  6413. // string and measure the subStringLength at the concatenated
  6414. // word length.
  6415. if (words) {
  6416. str = getString(words, currentIndex);
  6417. }
  6418. actualWidth = getSubStringLength(currentIndex, str && str.length - 1);
  6419. if (minIndex === maxIndex) {
  6420. // Complete
  6421. minIndex = maxIndex + 1;
  6422. }
  6423. else if (actualWidth > width) {
  6424. // Too large. Set max index to current.
  6425. maxIndex = currentIndex - 1;
  6426. }
  6427. else {
  6428. // Within width. Set min index to current.
  6429. minIndex = currentIndex;
  6430. }
  6431. }
  6432. // If max index was 0 it means the shortest possible text was also
  6433. // too large. For ellipsis that means only the ellipsis, while for
  6434. // word wrap it means the whole first word.
  6435. if (maxIndex === 0) {
  6436. // Remove ellipsis
  6437. updateTSpan('');
  6438. // If the new text length is one less than the original, we don't
  6439. // need the ellipsis
  6440. }
  6441. else if (!(text && maxIndex === text.length - 1)) {
  6442. updateTSpan(str || getString(text || words, currentIndex));
  6443. }
  6444. }
  6445. // When doing line wrapping, prepare for the next line by removing the
  6446. // items from this line.
  6447. if (words) {
  6448. words.splice(0, currentIndex);
  6449. }
  6450. wrapper.actualWidth = actualWidth;
  6451. wrapper.rotation = rotation; // Apply rotation again.
  6452. return truncated;
  6453. };
  6454. /**
  6455. * Parse a simple HTML string into SVG tspans. Called internally when text
  6456. * is set on an SVGElement. The function supports a subset of HTML tags, CSS
  6457. * text features like `width`, `text-overflow`, `white-space`, and also
  6458. * attributes like `href` and `style`.
  6459. *
  6460. * @private
  6461. * @function Highcharts.SVGRenderer#buildText
  6462. *
  6463. * @param {Highcharts.SVGElement} wrapper
  6464. * The parent SVGElement.
  6465. */
  6466. SVGRenderer.prototype.buildText = function (wrapper) {
  6467. var textNode = wrapper.element, renderer = this, forExport = renderer.forExport, textStr = pick(wrapper.textStr, '').toString(), hasMarkup = textStr.indexOf('<') !== -1, lines, childNodes = textNode.childNodes, truncated, parentX = attr(textNode, 'x'), textStyles = wrapper.styles, width = wrapper.textWidth, textLineHeight = textStyles && textStyles.lineHeight, textOutline = textStyles && textStyles.textOutline, ellipsis = textStyles && textStyles.textOverflow === 'ellipsis', noWrap = textStyles && textStyles.whiteSpace === 'nowrap', fontSize = textStyles && textStyles.fontSize, textCache, isSubsequentLine, i = childNodes.length, tempParent = width && !wrapper.added && this.box, getLineHeight = function (tspan) {
  6468. var fontSizeStyle;
  6469. if (!renderer.styledMode) {
  6470. fontSizeStyle =
  6471. /(px|em)$/.test(tspan && tspan.style.fontSize) ?
  6472. tspan.style.fontSize :
  6473. (fontSize || renderer.style.fontSize || 12);
  6474. }
  6475. return textLineHeight ?
  6476. pInt(textLineHeight) :
  6477. renderer.fontMetrics(fontSizeStyle,
  6478. // Get the computed size from parent if not explicit
  6479. (tspan.getAttribute('style') ? tspan : textNode)).h;
  6480. }, unescapeEntities = function (inputStr, except) {
  6481. objectEach(renderer.escapes, function (value, key) {
  6482. if (!except || except.indexOf(value) === -1) {
  6483. inputStr = inputStr.toString().replace(new RegExp(value, 'g'), key);
  6484. }
  6485. });
  6486. return inputStr;
  6487. }, parseAttribute = function (s, attr) {
  6488. var start, delimiter;
  6489. start = s.indexOf('<');
  6490. s = s.substring(start, s.indexOf('>') - start);
  6491. start = s.indexOf(attr + '=');
  6492. if (start !== -1) {
  6493. start = start + attr.length + 1;
  6494. delimiter = s.charAt(start);
  6495. if (delimiter === '"' || delimiter === "'") { // eslint-disable-line quotes
  6496. s = s.substring(start + 1);
  6497. return s.substring(0, s.indexOf(delimiter));
  6498. }
  6499. }
  6500. };
  6501. var regexMatchBreaks = /<br.*?>/g;
  6502. // The buildText code is quite heavy, so if we're not changing something
  6503. // that affects the text, skip it (#6113).
  6504. textCache = [
  6505. textStr,
  6506. ellipsis,
  6507. noWrap,
  6508. textLineHeight,
  6509. textOutline,
  6510. fontSize,
  6511. width
  6512. ].join(',');
  6513. if (textCache === wrapper.textCache) {
  6514. return;
  6515. }
  6516. wrapper.textCache = textCache;
  6517. // Remove old text
  6518. while (i--) {
  6519. textNode.removeChild(childNodes[i]);
  6520. }
  6521. // Skip tspans, add text directly to text node. The forceTSpan is a hook
  6522. // used in text outline hack.
  6523. if (!hasMarkup &&
  6524. !textOutline &&
  6525. !ellipsis &&
  6526. !width &&
  6527. (textStr.indexOf(' ') === -1 ||
  6528. (noWrap && !regexMatchBreaks.test(textStr)))) {
  6529. textNode.appendChild(doc.createTextNode(unescapeEntities(textStr)));
  6530. // Complex strings, add more logic
  6531. }
  6532. else {
  6533. if (tempParent) {
  6534. // attach it to the DOM to read offset width
  6535. tempParent.appendChild(textNode);
  6536. }
  6537. if (hasMarkup) {
  6538. lines = renderer.styledMode ? (textStr
  6539. .replace(/<(b|strong)>/g, '<span class="highcharts-strong">')
  6540. .replace(/<(i|em)>/g, '<span class="highcharts-emphasized">')) : (textStr
  6541. .replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
  6542. .replace(/<(i|em)>/g, '<span style="font-style:italic">'));
  6543. lines = lines
  6544. .replace(/<a/g, '<span')
  6545. .replace(/<\/(b|strong|i|em|a)>/g, '</span>')
  6546. .split(regexMatchBreaks);
  6547. }
  6548. else {
  6549. lines = [textStr];
  6550. }
  6551. // Trim empty lines (#5261)
  6552. lines = lines.filter(function (line) {
  6553. return line !== '';
  6554. });
  6555. // build the lines
  6556. lines.forEach(function (line, lineNo) {
  6557. var spans, spanNo = 0, lineLength = 0;
  6558. line = line
  6559. // Trim to prevent useless/costly process on the spaces
  6560. // (#5258)
  6561. .replace(/^\s+|\s+$/g, '')
  6562. .replace(/<span/g, '|||<span')
  6563. .replace(/<\/span>/g, '</span>|||');
  6564. spans = line.split('|||');
  6565. spans.forEach(function buildTextSpans(span) {
  6566. if (span !== '' || spans.length === 1) {
  6567. var attributes = {}, tspan = doc.createElementNS(renderer.SVG_NS, 'tspan'), a, classAttribute, styleAttribute, // #390
  6568. hrefAttribute;
  6569. classAttribute = parseAttribute(span, 'class');
  6570. if (classAttribute) {
  6571. attr(tspan, 'class', classAttribute);
  6572. }
  6573. styleAttribute = parseAttribute(span, 'style');
  6574. if (styleAttribute) {
  6575. styleAttribute = styleAttribute.replace(/(;| |^)color([ :])/, '$1fill$2');
  6576. attr(tspan, 'style', styleAttribute);
  6577. }
  6578. // For anchors, wrap the tspan in an <a> tag and apply
  6579. // the href attribute as is (#13559). Not for export
  6580. // (#1529)
  6581. hrefAttribute = parseAttribute(span, 'href');
  6582. if (hrefAttribute && !forExport) {
  6583. if (
  6584. // Stop JavaScript links, vulnerable to XSS
  6585. hrefAttribute.split(':')[0].toLowerCase()
  6586. .indexOf('javascript') === -1) {
  6587. a = doc.createElementNS(renderer.SVG_NS, 'a');
  6588. attr(a, 'href', hrefAttribute);
  6589. attr(tspan, 'class', 'highcharts-anchor');
  6590. a.appendChild(tspan);
  6591. if (!renderer.styledMode) {
  6592. css(tspan, { cursor: 'pointer' });
  6593. }
  6594. }
  6595. }
  6596. // Strip away unsupported HTML tags (#7126)
  6597. span = unescapeEntities(span.replace(/<[a-zA-Z\/](.|\n)*?>/g, '') || ' ');
  6598. // Nested tags aren't supported, and cause crash in
  6599. // Safari (#1596)
  6600. if (span !== ' ') {
  6601. // add the text node
  6602. tspan.appendChild(doc.createTextNode(span));
  6603. // First span in a line, align it to the left
  6604. if (!spanNo) {
  6605. if (lineNo && parentX !== null) {
  6606. attributes.x = parentX;
  6607. }
  6608. }
  6609. else {
  6610. attributes.dx = 0; // #16
  6611. }
  6612. // add attributes
  6613. attr(tspan, attributes);
  6614. // Append it
  6615. textNode.appendChild(a || tspan);
  6616. // first span on subsequent line, add the line
  6617. // height
  6618. if (!spanNo && isSubsequentLine) {
  6619. // allow getting the right offset height in
  6620. // exporting in IE
  6621. if (!svg && forExport) {
  6622. css(tspan, { display: 'block' });
  6623. }
  6624. // Set the line height based on the font size of
  6625. // either the text element or the tspan element
  6626. attr(tspan, 'dy', getLineHeight(tspan));
  6627. }
  6628. // Check width and apply soft breaks or ellipsis
  6629. if (width) {
  6630. var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
  6631. hasWhiteSpace = !noWrap && (spans.length > 1 ||
  6632. lineNo ||
  6633. words.length > 1), wrapLineNo = 0, dy = getLineHeight(tspan);
  6634. if (ellipsis) {
  6635. truncated = renderer.truncate(wrapper, tspan, span, void 0, 0,
  6636. // Target width
  6637. Math.max(0,
  6638. // Substract the font face to make
  6639. // room for the ellipsis itself
  6640. width - parseInt(fontSize || 12, 10)),
  6641. // Build the text to test for
  6642. function (text, currentIndex) {
  6643. return text.substring(0, currentIndex) + '\u2026';
  6644. });
  6645. }
  6646. else if (hasWhiteSpace) {
  6647. while (words.length) {
  6648. // For subsequent lines, create tspans
  6649. // with the same style attributes as the
  6650. // parent text node.
  6651. if (words.length &&
  6652. !noWrap &&
  6653. wrapLineNo > 0) {
  6654. tspan = doc.createElementNS(SVG_NS, 'tspan');
  6655. attr(tspan, {
  6656. dy: dy,
  6657. x: parentX
  6658. });
  6659. if (styleAttribute) { // #390
  6660. attr(tspan, 'style', styleAttribute);
  6661. }
  6662. // Start by appending the full
  6663. // remaining text
  6664. tspan.appendChild(doc.createTextNode(words.join(' ')
  6665. .replace(/- /g, '-')));
  6666. textNode.appendChild(tspan);
  6667. }
  6668. // For each line, truncate the remaining
  6669. // words into the line length.
  6670. renderer.truncate(wrapper, tspan, null, words, wrapLineNo === 0 ? lineLength : 0, width,
  6671. // Build the text to test for
  6672. function (text, currentIndex) {
  6673. return words
  6674. .slice(0, currentIndex)
  6675. .join(' ')
  6676. .replace(/- /g, '-');
  6677. });
  6678. lineLength = wrapper.actualWidth;
  6679. wrapLineNo++;
  6680. }
  6681. }
  6682. }
  6683. spanNo++;
  6684. }
  6685. }
  6686. });
  6687. // To avoid beginning lines that doesn't add to the textNode
  6688. // (#6144)
  6689. isSubsequentLine = (isSubsequentLine ||
  6690. textNode.childNodes.length);
  6691. });
  6692. if (ellipsis && truncated) {
  6693. wrapper.attr('title', unescapeEntities(wrapper.textStr || '', ['&lt;', '&gt;']) // #7179
  6694. );
  6695. }
  6696. if (tempParent) {
  6697. tempParent.removeChild(textNode);
  6698. }
  6699. // Apply the text outline
  6700. if (isString(textOutline) && wrapper.applyTextOutline) {
  6701. wrapper.applyTextOutline(textOutline);
  6702. }
  6703. }
  6704. };
  6705. /**
  6706. * Returns white for dark colors and black for bright colors.
  6707. *
  6708. * @function Highcharts.SVGRenderer#getContrast
  6709. *
  6710. * @param {Highcharts.ColorString} rgba
  6711. * The color to get the contrast for.
  6712. *
  6713. * @return {Highcharts.ColorString}
  6714. * The contrast color, either `#000000` or `#FFFFFF`.
  6715. */
  6716. SVGRenderer.prototype.getContrast = function (rgba) {
  6717. rgba = Color.parse(rgba).rgba;
  6718. // The threshold may be discussed. Here's a proposal for adding
  6719. // different weight to the color channels (#6216)
  6720. rgba[0] *= 1; // red
  6721. rgba[1] *= 1.2; // green
  6722. rgba[2] *= 0.5; // blue
  6723. return rgba[0] + rgba[1] + rgba[2] >
  6724. 1.8 * 255 ?
  6725. '#000000' :
  6726. '#FFFFFF';
  6727. };
  6728. /**
  6729. * Create a button with preset states.
  6730. *
  6731. * @function Highcharts.SVGRenderer#button
  6732. *
  6733. * @param {string} text
  6734. * The text or HTML to draw.
  6735. *
  6736. * @param {number} x
  6737. * The x position of the button's left side.
  6738. *
  6739. * @param {number} y
  6740. * The y position of the button's top side.
  6741. *
  6742. * @param {Highcharts.EventCallbackFunction<Highcharts.SVGElement>} callback
  6743. * The function to execute on button click or touch.
  6744. *
  6745. * @param {Highcharts.SVGAttributes} [normalState]
  6746. * SVG attributes for the normal state.
  6747. *
  6748. * @param {Highcharts.SVGAttributes} [hoverState]
  6749. * SVG attributes for the hover state.
  6750. *
  6751. * @param {Highcharts.SVGAttributes} [pressedState]
  6752. * SVG attributes for the pressed state.
  6753. *
  6754. * @param {Highcharts.SVGAttributes} [disabledState]
  6755. * SVG attributes for the disabled state.
  6756. *
  6757. * @param {Highcharts.SymbolKeyValue} [shape=rect]
  6758. * The shape type.
  6759. *
  6760. * @param {boolean} [useHTML=false]
  6761. * Wether to use HTML to render the label.
  6762. *
  6763. * @return {Highcharts.SVGElement}
  6764. * The button element.
  6765. */
  6766. SVGRenderer.prototype.button = function (text, x, y, callback, normalState, hoverState, pressedState, disabledState, shape, useHTML) {
  6767. var label = this.label(text, x, y, shape, void 0, void 0, useHTML, void 0, 'button'), curState = 0, styledMode = this.styledMode, userNormalStyle = normalState && normalState.style || {};
  6768. // Remove stylable attributes
  6769. if (normalState && normalState.style) {
  6770. delete normalState.style;
  6771. }
  6772. // Default, non-stylable attributes
  6773. label.attr(merge({ padding: 8, r: 2 }, normalState));
  6774. if (!styledMode) {
  6775. // Presentational
  6776. var normalStyle, hoverStyle, pressedStyle, disabledStyle;
  6777. // Normal state - prepare the attributes
  6778. normalState = merge({
  6779. fill: '#f7f7f7',
  6780. stroke: '#cccccc',
  6781. 'stroke-width': 1,
  6782. style: {
  6783. color: '#333333',
  6784. cursor: 'pointer',
  6785. fontWeight: 'normal'
  6786. }
  6787. }, {
  6788. style: userNormalStyle
  6789. }, normalState);
  6790. normalStyle = normalState.style;
  6791. delete normalState.style;
  6792. // Hover state
  6793. hoverState = merge(normalState, {
  6794. fill: '#e6e6e6'
  6795. }, hoverState);
  6796. hoverStyle = hoverState.style;
  6797. delete hoverState.style;
  6798. // Pressed state
  6799. pressedState = merge(normalState, {
  6800. fill: '#e6ebf5',
  6801. style: {
  6802. color: '#000000',
  6803. fontWeight: 'bold'
  6804. }
  6805. }, pressedState);
  6806. pressedStyle = pressedState.style;
  6807. delete pressedState.style;
  6808. // Disabled state
  6809. disabledState = merge(normalState, {
  6810. style: {
  6811. color: '#cccccc'
  6812. }
  6813. }, disabledState);
  6814. disabledStyle = disabledState.style;
  6815. delete disabledState.style;
  6816. }
  6817. // Add the events. IE9 and IE10 need mouseover and mouseout to funciton
  6818. // (#667).
  6819. addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
  6820. if (curState !== 3) {
  6821. label.setState(1);
  6822. }
  6823. });
  6824. addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
  6825. if (curState !== 3) {
  6826. label.setState(curState);
  6827. }
  6828. });
  6829. label.setState = function (state) {
  6830. // Hover state is temporary, don't record it
  6831. if (state !== 1) {
  6832. label.state = curState = state;
  6833. }
  6834. // Update visuals
  6835. label
  6836. .removeClass(/highcharts-button-(normal|hover|pressed|disabled)/)
  6837. .addClass('highcharts-button-' +
  6838. ['normal', 'hover', 'pressed', 'disabled'][state || 0]);
  6839. if (!styledMode) {
  6840. label
  6841. .attr([
  6842. normalState,
  6843. hoverState,
  6844. pressedState,
  6845. disabledState
  6846. ][state || 0])
  6847. .css([
  6848. normalStyle,
  6849. hoverStyle,
  6850. pressedStyle,
  6851. disabledStyle
  6852. ][state || 0]);
  6853. }
  6854. };
  6855. // Presentational attributes
  6856. if (!styledMode) {
  6857. label
  6858. .attr(normalState)
  6859. .css(extend({ cursor: 'default' }, normalStyle));
  6860. }
  6861. return label
  6862. .on('click', function (e) {
  6863. if (curState !== 3) {
  6864. callback.call(label, e);
  6865. }
  6866. });
  6867. };
  6868. /**
  6869. * Make a straight line crisper by not spilling out to neighbour pixels.
  6870. *
  6871. * @function Highcharts.SVGRenderer#crispLine
  6872. *
  6873. * @param {Highcharts.SVGPathArray} points
  6874. * The original points on the format `[['M', 0, 0], ['L', 100, 0]]`.
  6875. *
  6876. * @param {number} width
  6877. * The width of the line.
  6878. *
  6879. * @param {string} roundingFunction
  6880. * The rounding function name on the `Math` object, can be one of
  6881. * `round`, `floor` or `ceil`.
  6882. *
  6883. * @return {Highcharts.SVGPathArray}
  6884. * The original points array, but modified to render crisply.
  6885. */
  6886. SVGRenderer.prototype.crispLine = function (points, width, roundingFunction) {
  6887. if (roundingFunction === void 0) { roundingFunction = 'round'; }
  6888. var start = points[0];
  6889. var end = points[1];
  6890. // Normalize to a crisp line
  6891. if (start[1] === end[1]) {
  6892. // Substract due to #1129. Now bottom and left axis gridlines behave
  6893. // the same.
  6894. start[1] = end[1] =
  6895. Math[roundingFunction](start[1]) - (width % 2 / 2);
  6896. }
  6897. if (start[2] === end[2]) {
  6898. start[2] = end[2] =
  6899. Math[roundingFunction](start[2]) + (width % 2 / 2);
  6900. }
  6901. return points;
  6902. };
  6903. /**
  6904. * Draw a path, wraps the SVG `path` element.
  6905. *
  6906. * @sample highcharts/members/renderer-path-on-chart/
  6907. * Draw a path in a chart
  6908. * @sample highcharts/members/renderer-path/
  6909. * Draw a path independent from a chart
  6910. *
  6911. * @example
  6912. * var path = renderer.path(['M', 10, 10, 'L', 30, 30, 'z'])
  6913. * .attr({ stroke: '#ff00ff' })
  6914. * .add();
  6915. *
  6916. * @function Highcharts.SVGRenderer#path
  6917. *
  6918. * @param {Highcharts.SVGPathArray} [path]
  6919. * An SVG path definition in array form.
  6920. *
  6921. * @return {Highcharts.SVGElement}
  6922. * The generated wrapper element.
  6923. *
  6924. */ /**
  6925. * Draw a path, wraps the SVG `path` element.
  6926. *
  6927. * @function Highcharts.SVGRenderer#path
  6928. *
  6929. * @param {Highcharts.SVGAttributes} [attribs]
  6930. * The initial attributes.
  6931. *
  6932. * @return {Highcharts.SVGElement}
  6933. * The generated wrapper element.
  6934. */
  6935. SVGRenderer.prototype.path = function (path) {
  6936. var attribs = (this.styledMode ? {} : {
  6937. fill: 'none'
  6938. });
  6939. if (isArray(path)) {
  6940. attribs.d = path;
  6941. }
  6942. else if (isObject(path)) { // attributes
  6943. extend(attribs, path);
  6944. }
  6945. return this.createElement('path').attr(attribs);
  6946. };
  6947. /**
  6948. * Draw a circle, wraps the SVG `circle` element.
  6949. *
  6950. * @sample highcharts/members/renderer-circle/
  6951. * Drawing a circle
  6952. *
  6953. * @function Highcharts.SVGRenderer#circle
  6954. *
  6955. * @param {number} [x]
  6956. * The center x position.
  6957. *
  6958. * @param {number} [y]
  6959. * The center y position.
  6960. *
  6961. * @param {number} [r]
  6962. * The radius.
  6963. *
  6964. * @return {Highcharts.SVGElement}
  6965. * The generated wrapper element.
  6966. */ /**
  6967. * Draw a circle, wraps the SVG `circle` element.
  6968. *
  6969. * @function Highcharts.SVGRenderer#circle
  6970. *
  6971. * @param {Highcharts.SVGAttributes} [attribs]
  6972. * The initial attributes.
  6973. *
  6974. * @return {Highcharts.SVGElement}
  6975. * The generated wrapper element.
  6976. */
  6977. SVGRenderer.prototype.circle = function (x, y, r) {
  6978. var attribs = (isObject(x) ?
  6979. x :
  6980. typeof x === 'undefined' ? {} : { x: x, y: y, r: r }), wrapper = this.createElement('circle');
  6981. // Setting x or y translates to cx and cy
  6982. wrapper.xSetter = wrapper.ySetter = function (value, key, element) {
  6983. element.setAttribute('c' + key, value);
  6984. };
  6985. return wrapper.attr(attribs);
  6986. };
  6987. /**
  6988. * Draw and return an arc.
  6989. *
  6990. * @sample highcharts/members/renderer-arc/
  6991. * Drawing an arc
  6992. *
  6993. * @function Highcharts.SVGRenderer#arc
  6994. *
  6995. * @param {number} [x=0]
  6996. * Center X position.
  6997. *
  6998. * @param {number} [y=0]
  6999. * Center Y position.
  7000. *
  7001. * @param {number} [r=0]
  7002. * The outer radius' of the arc.
  7003. *
  7004. * @param {number} [innerR=0]
  7005. * Inner radius like used in donut charts.
  7006. *
  7007. * @param {number} [start=0]
  7008. * The starting angle of the arc in radians, where 0 is to the right and
  7009. * `-Math.PI/2` is up.
  7010. *
  7011. * @param {number} [end=0]
  7012. * The ending angle of the arc in radians, where 0 is to the right and
  7013. * `-Math.PI/2` is up.
  7014. *
  7015. * @return {Highcharts.SVGElement}
  7016. * The generated wrapper element.
  7017. */ /**
  7018. * Draw and return an arc. Overloaded function that takes arguments object.
  7019. *
  7020. * @function Highcharts.SVGRenderer#arc
  7021. *
  7022. * @param {Highcharts.SVGAttributes} attribs
  7023. * Initial SVG attributes.
  7024. *
  7025. * @return {Highcharts.SVGElement}
  7026. * The generated wrapper element.
  7027. */
  7028. SVGRenderer.prototype.arc = function (x, y, r, innerR, start, end) {
  7029. var arc, options;
  7030. if (isObject(x)) {
  7031. options = x;
  7032. y = options.y;
  7033. r = options.r;
  7034. innerR = options.innerR;
  7035. start = options.start;
  7036. end = options.end;
  7037. x = options.x;
  7038. }
  7039. else {
  7040. options = {
  7041. innerR: innerR,
  7042. start: start,
  7043. end: end
  7044. };
  7045. }
  7046. // Arcs are defined as symbols for the ability to set
  7047. // attributes in attr and animate
  7048. arc = this.symbol('arc', x, y, r, r, options);
  7049. arc.r = r; // #959
  7050. return arc;
  7051. };
  7052. /**
  7053. * Draw and return a rectangle.
  7054. *
  7055. * @function Highcharts.SVGRenderer#rect
  7056. *
  7057. * @param {number} [x]
  7058. * Left position.
  7059. *
  7060. * @param {number} [y]
  7061. * Top position.
  7062. *
  7063. * @param {number} [width]
  7064. * Width of the rectangle.
  7065. *
  7066. * @param {number} [height]
  7067. * Height of the rectangle.
  7068. *
  7069. * @param {number} [r]
  7070. * Border corner radius.
  7071. *
  7072. * @param {number} [strokeWidth]
  7073. * A stroke width can be supplied to allow crisp drawing.
  7074. *
  7075. * @return {Highcharts.SVGElement}
  7076. * The generated wrapper element.
  7077. */ /**
  7078. * Draw and return a rectangle.
  7079. *
  7080. * @sample highcharts/members/renderer-rect-on-chart/
  7081. * Draw a rectangle in a chart
  7082. * @sample highcharts/members/renderer-rect/
  7083. * Draw a rectangle independent from a chart
  7084. *
  7085. * @function Highcharts.SVGRenderer#rect
  7086. *
  7087. * @param {Highcharts.SVGAttributes} [attributes]
  7088. * General SVG attributes for the rectangle.
  7089. *
  7090. * @return {Highcharts.SVGElement}
  7091. * The generated wrapper element.
  7092. */
  7093. SVGRenderer.prototype.rect = function (x, y, width, height, r, strokeWidth) {
  7094. r = isObject(x) ? x.r : r;
  7095. var wrapper = this.createElement('rect'), attribs = isObject(x) ?
  7096. x :
  7097. typeof x === 'undefined' ?
  7098. {} :
  7099. {
  7100. x: x,
  7101. y: y,
  7102. width: Math.max(width, 0),
  7103. height: Math.max(height, 0)
  7104. };
  7105. if (!this.styledMode) {
  7106. if (typeof strokeWidth !== 'undefined') {
  7107. attribs.strokeWidth = strokeWidth;
  7108. attribs = wrapper.crisp(attribs);
  7109. }
  7110. attribs.fill = 'none';
  7111. }
  7112. if (r) {
  7113. attribs.r = r;
  7114. }
  7115. wrapper.rSetter = function (value, key, element) {
  7116. wrapper.r = value;
  7117. attr(element, {
  7118. rx: value,
  7119. ry: value
  7120. });
  7121. };
  7122. wrapper.rGetter = function () {
  7123. return wrapper.r;
  7124. };
  7125. return wrapper.attr(attribs);
  7126. };
  7127. /**
  7128. * Resize the {@link SVGRenderer#box} and re-align all aligned child
  7129. * elements.
  7130. *
  7131. * @sample highcharts/members/renderer-g/
  7132. * Show and hide grouped objects
  7133. *
  7134. * @function Highcharts.SVGRenderer#setSize
  7135. *
  7136. * @param {number} width
  7137. * The new pixel width.
  7138. *
  7139. * @param {number} height
  7140. * The new pixel height.
  7141. *
  7142. * @param {boolean|Highcharts.AnimationOptionsObject} [animate=true]
  7143. * Whether and how to animate.
  7144. */
  7145. SVGRenderer.prototype.setSize = function (width, height, animate) {
  7146. var renderer = this, alignedObjects = renderer.alignedObjects, i = alignedObjects.length;
  7147. renderer.width = width;
  7148. renderer.height = height;
  7149. renderer.boxWrapper.animate({
  7150. width: width,
  7151. height: height
  7152. }, {
  7153. step: function () {
  7154. this.attr({
  7155. viewBox: '0 0 ' + this.attr('width') + ' ' +
  7156. this.attr('height')
  7157. });
  7158. },
  7159. duration: pick(animate, true) ? void 0 : 0
  7160. });
  7161. while (i--) {
  7162. alignedObjects[i].align();
  7163. }
  7164. };
  7165. /**
  7166. * Create and return an svg group element. Child
  7167. * {@link Highcharts.SVGElement} objects are added to the group by using the
  7168. * group as the first parameter in {@link Highcharts.SVGElement#add|add()}.
  7169. *
  7170. * @function Highcharts.SVGRenderer#g
  7171. *
  7172. * @param {string} [name]
  7173. * The group will be given a class name of `highcharts-{name}`. This
  7174. * can be used for styling and scripting.
  7175. *
  7176. * @return {Highcharts.SVGElement}
  7177. * The generated wrapper element.
  7178. */
  7179. SVGRenderer.prototype.g = function (name) {
  7180. var elem = this.createElement('g');
  7181. return name ?
  7182. elem.attr({ 'class': 'highcharts-' + name }) :
  7183. elem;
  7184. };
  7185. /**
  7186. * Display an image.
  7187. *
  7188. * @sample highcharts/members/renderer-image-on-chart/
  7189. * Add an image in a chart
  7190. * @sample highcharts/members/renderer-image/
  7191. * Add an image independent of a chart
  7192. *
  7193. * @function Highcharts.SVGRenderer#image
  7194. *
  7195. * @param {string} src
  7196. * The image source.
  7197. *
  7198. * @param {number} [x]
  7199. * The X position.
  7200. *
  7201. * @param {number} [y]
  7202. * The Y position.
  7203. *
  7204. * @param {number} [width]
  7205. * The image width. If omitted, it defaults to the image file width.
  7206. *
  7207. * @param {number} [height]
  7208. * The image height. If omitted it defaults to the image file
  7209. * height.
  7210. *
  7211. * @param {Function} [onload]
  7212. * Event handler for image load.
  7213. *
  7214. * @return {Highcharts.SVGElement}
  7215. * The generated wrapper element.
  7216. */
  7217. SVGRenderer.prototype.image = function (src, x, y, width, height, onload) {
  7218. var attribs = { preserveAspectRatio: 'none' }, elemWrapper, dummy, setSVGImageSource = function (el, src) {
  7219. // Set the href in the xlink namespace
  7220. if (el.setAttributeNS) {
  7221. el.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src);
  7222. }
  7223. else {
  7224. // could be exporting in IE
  7225. // using href throws "not supported" in ie7 and under,
  7226. // requries regex shim to fix later
  7227. el.setAttribute('hc-svg-href', src);
  7228. }
  7229. }, onDummyLoad = function (e) {
  7230. setSVGImageSource(elemWrapper.element, src);
  7231. onload.call(elemWrapper, e);
  7232. };
  7233. // optional properties
  7234. if (arguments.length > 1) {
  7235. extend(attribs, {
  7236. x: x,
  7237. y: y,
  7238. width: width,
  7239. height: height
  7240. });
  7241. }
  7242. elemWrapper = this.createElement('image').attr(attribs);
  7243. // Add load event if supplied
  7244. if (onload) {
  7245. // We have to use a dummy HTML image since IE support for SVG image
  7246. // load events is very buggy. First set a transparent src, wait for
  7247. // dummy to load, and then add the real src to the SVG image.
  7248. setSVGImageSource(elemWrapper.element, 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' /* eslint-disable-line */);
  7249. dummy = new win.Image();
  7250. addEvent(dummy, 'load', onDummyLoad);
  7251. dummy.src = src;
  7252. if (dummy.complete) {
  7253. onDummyLoad({});
  7254. }
  7255. }
  7256. else {
  7257. setSVGImageSource(elemWrapper.element, src);
  7258. }
  7259. return elemWrapper;
  7260. };
  7261. /**
  7262. * Draw a symbol out of pre-defined shape paths from
  7263. * {@link SVGRenderer#symbols}.
  7264. * It is used in Highcharts for point makers, which cake a `symbol` option,
  7265. * and label and button backgrounds like in the tooltip and stock flags.
  7266. *
  7267. * @function Highcharts.SVGRenderer#symbol
  7268. *
  7269. * @param {string} symbol
  7270. * The symbol name.
  7271. *
  7272. * @param {number} [x]
  7273. * The X coordinate for the top left position.
  7274. *
  7275. * @param {number} [y]
  7276. * The Y coordinate for the top left position.
  7277. *
  7278. * @param {number} [width]
  7279. * The pixel width.
  7280. *
  7281. * @param {number} [height]
  7282. * The pixel height.
  7283. *
  7284. * @param {Highcharts.SymbolOptionsObject} [options]
  7285. * Additional options, depending on the actual symbol drawn.
  7286. *
  7287. * @return {Highcharts.SVGElement}
  7288. */
  7289. SVGRenderer.prototype.symbol = function (symbol, x, y, width, height, options) {
  7290. var ren = this, obj, imageRegex = /^url\((.*?)\)$/, isImage = imageRegex.test(symbol), sym = (!isImage && (this.symbols[symbol] ? symbol : 'circle')),
  7291. // get the symbol definition function
  7292. symbolFn = (sym && this.symbols[sym]), path, imageSrc, centerImage;
  7293. if (symbolFn) {
  7294. // Check if there's a path defined for this symbol
  7295. if (typeof x === 'number') {
  7296. path = symbolFn.call(this.symbols, Math.round(x || 0), Math.round(y || 0), width || 0, height || 0, options);
  7297. }
  7298. obj = this.path(path);
  7299. if (!ren.styledMode) {
  7300. obj.attr('fill', 'none');
  7301. }
  7302. // expando properties for use in animate and attr
  7303. extend(obj, {
  7304. symbolName: sym,
  7305. x: x,
  7306. y: y,
  7307. width: width,
  7308. height: height
  7309. });
  7310. if (options) {
  7311. extend(obj, options);
  7312. }
  7313. // Image symbols
  7314. }
  7315. else if (isImage) {
  7316. imageSrc = symbol.match(imageRegex)[1];
  7317. // Create the image synchronously, add attribs async
  7318. obj = this.image(imageSrc);
  7319. // The image width is not always the same as the symbol width. The
  7320. // image may be centered within the symbol, as is the case when
  7321. // image shapes are used as label backgrounds, for example in flags.
  7322. obj.imgwidth = pick(symbolSizes[imageSrc] && symbolSizes[imageSrc].width, options && options.width);
  7323. obj.imgheight = pick(symbolSizes[imageSrc] && symbolSizes[imageSrc].height, options && options.height);
  7324. /**
  7325. * Set the size and position
  7326. */
  7327. centerImage = function () {
  7328. obj.attr({
  7329. width: obj.width,
  7330. height: obj.height
  7331. });
  7332. };
  7333. /**
  7334. * Width and height setters that take both the image's physical size
  7335. * and the label size into consideration, and translates the image
  7336. * to center within the label.
  7337. */
  7338. ['width', 'height'].forEach(function (key) {
  7339. obj[key + 'Setter'] = function (value, key) {
  7340. var attribs = {}, imgSize = this['img' + key], trans = key === 'width' ? 'translateX' : 'translateY';
  7341. this[key] = value;
  7342. if (defined(imgSize)) {
  7343. // Scale and center the image within its container.
  7344. // The name `backgroundSize` is taken from the CSS spec,
  7345. // but the value `within` is made up. Other possible
  7346. // values in the spec, `cover` and `contain`, can be
  7347. // implemented if needed.
  7348. if (options &&
  7349. options.backgroundSize === 'within' &&
  7350. this.width &&
  7351. this.height) {
  7352. imgSize = Math.round(imgSize * Math.min(this.width / this.imgwidth, this.height / this.imgheight));
  7353. }
  7354. if (this.element) {
  7355. this.element.setAttribute(key, imgSize);
  7356. }
  7357. if (!this.alignByTranslate) {
  7358. attribs[trans] = ((this[key] || 0) - imgSize) / 2;
  7359. this.attr(attribs);
  7360. }
  7361. }
  7362. };
  7363. });
  7364. if (defined(x)) {
  7365. obj.attr({
  7366. x: x,
  7367. y: y
  7368. });
  7369. }
  7370. obj.isImg = true;
  7371. if (defined(obj.imgwidth) && defined(obj.imgheight)) {
  7372. centerImage();
  7373. }
  7374. else {
  7375. // Initialize image to be 0 size so export will still function
  7376. // if there's no cached sizes.
  7377. obj.attr({ width: 0, height: 0 });
  7378. // Create a dummy JavaScript image to get the width and height.
  7379. createElement('img', {
  7380. onload: function () {
  7381. var chart = charts[ren.chartIndex];
  7382. // Special case for SVGs on IE11, the width is not
  7383. // accessible until the image is part of the DOM
  7384. // (#2854).
  7385. if (this.width === 0) {
  7386. css(this, {
  7387. position: 'absolute',
  7388. top: '-999em'
  7389. });
  7390. doc.body.appendChild(this);
  7391. }
  7392. // Center the image
  7393. symbolSizes[imageSrc] = {
  7394. width: this.width,
  7395. height: this.height
  7396. };
  7397. obj.imgwidth = this.width;
  7398. obj.imgheight = this.height;
  7399. if (obj.element) {
  7400. centerImage();
  7401. }
  7402. // Clean up after #2854 workaround.
  7403. if (this.parentNode) {
  7404. this.parentNode.removeChild(this);
  7405. }
  7406. // Fire the load event when all external images are
  7407. // loaded
  7408. ren.imgCount--;
  7409. if (!ren.imgCount && chart && !chart.hasLoaded) {
  7410. chart.onload();
  7411. }
  7412. },
  7413. src: imageSrc
  7414. });
  7415. this.imgCount++;
  7416. }
  7417. }
  7418. return obj;
  7419. };
  7420. /**
  7421. * Define a clipping rectangle. The clipping rectangle is later applied
  7422. * to {@link SVGElement} objects through the {@link SVGElement#clip}
  7423. * function.
  7424. *
  7425. * @example
  7426. * var circle = renderer.circle(100, 100, 100)
  7427. * .attr({ fill: 'red' })
  7428. * .add();
  7429. * var clipRect = renderer.clipRect(100, 100, 100, 100);
  7430. *
  7431. * // Leave only the lower right quarter visible
  7432. * circle.clip(clipRect);
  7433. *
  7434. * @function Highcharts.SVGRenderer#clipRect
  7435. *
  7436. * @param {number} [x]
  7437. *
  7438. * @param {number} [y]
  7439. *
  7440. * @param {number} [width]
  7441. *
  7442. * @param {number} [height]
  7443. *
  7444. * @return {Highcharts.ClipRectElement}
  7445. * A clipping rectangle.
  7446. */
  7447. SVGRenderer.prototype.clipRect = function (x, y, width, height) {
  7448. var wrapper,
  7449. // Add a hyphen at the end to avoid confusion in testing indexes
  7450. // -1 and -10, -11 etc (#6550)
  7451. id = uniqueKey() + '-', clipPath = this.createElement('clipPath').attr({
  7452. id: id
  7453. }).add(this.defs);
  7454. wrapper = this.rect(x, y, width, height, 0).add(clipPath);
  7455. wrapper.id = id;
  7456. wrapper.clipPath = clipPath;
  7457. wrapper.count = 0;
  7458. return wrapper;
  7459. };
  7460. /**
  7461. * Draw text. The text can contain a subset of HTML, like spans and anchors
  7462. * and some basic text styling of these. For more advanced features like
  7463. * border and background, use {@link Highcharts.SVGRenderer#label} instead.
  7464. * To update the text after render, run `text.attr({ text: 'New text' })`.
  7465. *
  7466. * @sample highcharts/members/renderer-text-on-chart/
  7467. * Annotate the chart freely
  7468. * @sample highcharts/members/renderer-on-chart/
  7469. * Annotate with a border and in response to the data
  7470. * @sample highcharts/members/renderer-text/
  7471. * Formatted text
  7472. *
  7473. * @function Highcharts.SVGRenderer#text
  7474. *
  7475. * @param {string} [str]
  7476. * The text of (subset) HTML to draw.
  7477. *
  7478. * @param {number} [x]
  7479. * The x position of the text's lower left corner.
  7480. *
  7481. * @param {number} [y]
  7482. * The y position of the text's lower left corner.
  7483. *
  7484. * @param {boolean} [useHTML=false]
  7485. * Use HTML to render the text.
  7486. *
  7487. * @return {Highcharts.SVGElement}
  7488. * The text object.
  7489. */
  7490. SVGRenderer.prototype.text = function (str, x, y, useHTML) {
  7491. // declare variables
  7492. var renderer = this, wrapper, attribs = {};
  7493. if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
  7494. return renderer.html(str, x, y);
  7495. }
  7496. attribs.x = Math.round(x || 0); // X always needed for line-wrap logic
  7497. if (y) {
  7498. attribs.y = Math.round(y);
  7499. }
  7500. if (defined(str)) {
  7501. attribs.text = str;
  7502. }
  7503. wrapper = renderer.createElement('text')
  7504. .attr(attribs);
  7505. if (!useHTML) {
  7506. wrapper.xSetter = function (value, key, element) {
  7507. var tspans = element.getElementsByTagName('tspan'), tspan, parentVal = element.getAttribute(key), i;
  7508. for (i = 0; i < tspans.length; i++) {
  7509. tspan = tspans[i];
  7510. // If the x values are equal, the tspan represents a
  7511. // linebreak
  7512. if (tspan.getAttribute(key) === parentVal) {
  7513. tspan.setAttribute(key, value);
  7514. }
  7515. }
  7516. element.setAttribute(key, value);
  7517. };
  7518. }
  7519. return wrapper;
  7520. };
  7521. /**
  7522. * Utility to return the baseline offset and total line height from the font
  7523. * size.
  7524. *
  7525. * @function Highcharts.SVGRenderer#fontMetrics
  7526. *
  7527. * @param {number|string} [fontSize]
  7528. * The current font size to inspect. If not given, the font size
  7529. * will be found from the DOM element.
  7530. *
  7531. * @param {Highcharts.SVGElement|Highcharts.SVGDOMElement} [elem]
  7532. * The element to inspect for a current font size.
  7533. *
  7534. * @return {Highcharts.FontMetricsObject}
  7535. * The font metrics.
  7536. */
  7537. SVGRenderer.prototype.fontMetrics = function (fontSize, elem) {
  7538. var lineHeight, baseline;
  7539. if ((this.styledMode || !/px/.test(fontSize)) &&
  7540. win.getComputedStyle // old IE doesn't support it
  7541. ) {
  7542. fontSize = elem && SVGElement.prototype.getStyle.call(elem, 'font-size');
  7543. }
  7544. else {
  7545. fontSize = fontSize ||
  7546. // When the elem is a DOM element (#5932)
  7547. (elem && elem.style && elem.style.fontSize) ||
  7548. // Fall back on the renderer style default
  7549. (this.style && this.style.fontSize);
  7550. }
  7551. // Handle different units
  7552. if (/px/.test(fontSize)) {
  7553. fontSize = pInt(fontSize);
  7554. }
  7555. else {
  7556. fontSize = 12;
  7557. }
  7558. // Empirical values found by comparing font size and bounding box
  7559. // height. Applies to the default font family.
  7560. // https://jsfiddle.net/highcharts/7xvn7/
  7561. lineHeight = fontSize < 24 ? fontSize + 3 : Math.round(fontSize * 1.2);
  7562. baseline = Math.round(lineHeight * 0.8);
  7563. return {
  7564. h: lineHeight,
  7565. b: baseline,
  7566. f: fontSize
  7567. };
  7568. };
  7569. /**
  7570. * Correct X and Y positioning of a label for rotation (#1764).
  7571. *
  7572. * @private
  7573. * @function Highcharts.SVGRenderer#rotCorr
  7574. *
  7575. * @param {number} baseline
  7576. *
  7577. * @param {number} rotation
  7578. *
  7579. * @param {boolean} [alterY]
  7580. *
  7581. * @param {Highcharts.PositionObject}
  7582. */
  7583. SVGRenderer.prototype.rotCorr = function (baseline, rotation, alterY) {
  7584. var y = baseline;
  7585. if (rotation && alterY) {
  7586. y = Math.max(y * Math.cos(rotation * deg2rad), 4);
  7587. }
  7588. return {
  7589. x: (-baseline / 3) * Math.sin(rotation * deg2rad),
  7590. y: y
  7591. };
  7592. };
  7593. /**
  7594. * Compatibility function to convert the legacy one-dimensional path array
  7595. * into an array of segments.
  7596. *
  7597. * It is used in maps to parse the `path` option, and in SVGRenderer.dSetter
  7598. * to support legacy paths from demos.
  7599. *
  7600. * @private
  7601. * @function Highcharts.SVGRenderer#pathToSegments
  7602. */
  7603. SVGRenderer.prototype.pathToSegments = function (path) {
  7604. var ret = [];
  7605. var segment = [];
  7606. var commandLength = {
  7607. A: 8,
  7608. C: 7,
  7609. H: 2,
  7610. L: 3,
  7611. M: 3,
  7612. Q: 5,
  7613. S: 5,
  7614. T: 3,
  7615. V: 2
  7616. };
  7617. // Short, non-typesafe parsing of the one-dimensional array. It splits
  7618. // the path on any string. This is not type checked against the tuple
  7619. // types, but is shorter, and doesn't require specific checks for any
  7620. // command type in SVG.
  7621. for (var i = 0; i < path.length; i++) {
  7622. // Command skipped, repeat previous or insert L/l for M/m
  7623. if (isString(segment[0]) &&
  7624. isNumber(path[i]) &&
  7625. segment.length === commandLength[(segment[0].toUpperCase())]) {
  7626. path.splice(i, 0, segment[0].replace('M', 'L').replace('m', 'l'));
  7627. }
  7628. // Split on string
  7629. if (typeof path[i] === 'string') {
  7630. if (segment.length) {
  7631. ret.push(segment.slice(0));
  7632. }
  7633. segment.length = 0;
  7634. }
  7635. segment.push(path[i]);
  7636. }
  7637. ret.push(segment.slice(0));
  7638. return ret;
  7639. /*
  7640. // Fully type-safe version where each tuple type is checked. The
  7641. // downside is filesize and a lack of flexibility for unsupported
  7642. // commands
  7643. const ret: SVGPath = [],
  7644. commands = {
  7645. A: 7,
  7646. C: 6,
  7647. H: 1,
  7648. L: 2,
  7649. M: 2,
  7650. Q: 4,
  7651. S: 4,
  7652. T: 2,
  7653. V: 1,
  7654. Z: 0
  7655. };
  7656. let i = 0,
  7657. lastI = 0,
  7658. lastCommand;
  7659. while (i < path.length) {
  7660. const item = path[i];
  7661. let command;
  7662. if (typeof item === 'string') {
  7663. command = item;
  7664. i += 1;
  7665. } else {
  7666. command = lastCommand || 'M';
  7667. }
  7668. // Upper case
  7669. const commandUC = command.toUpperCase();
  7670. if (commandUC in commands) {
  7671. // No numeric parameters
  7672. if (command === 'Z' || command === 'z') {
  7673. ret.push([command]);
  7674. // One numeric parameter
  7675. } else {
  7676. const val0 = path[i];
  7677. if (typeof val0 === 'number') {
  7678. // Horizontal line to
  7679. if (command === 'H' || command === 'h') {
  7680. ret.push([command, val0]);
  7681. i += 1;
  7682. // Vertical line to
  7683. } else if (command === 'V' || command === 'v') {
  7684. ret.push([command, val0]);
  7685. i += 1;
  7686. // Two numeric parameters
  7687. } else {
  7688. const val1 = path[i + 1];
  7689. if (typeof val1 === 'number') {
  7690. // lineTo
  7691. if (command === 'L' || command === 'l') {
  7692. ret.push([command, val0, val1]);
  7693. i += 2;
  7694. // moveTo
  7695. } else if (command === 'M' || command === 'm') {
  7696. ret.push([command, val0, val1]);
  7697. i += 2;
  7698. // Smooth quadratic bezier
  7699. } else if (command === 'T' || command === 't') {
  7700. ret.push([command, val0, val1]);
  7701. i += 2;
  7702. // Four numeric parameters
  7703. } else {
  7704. const val2 = path[i + 2],
  7705. val3 = path[i + 3];
  7706. if (
  7707. typeof val2 === 'number' &&
  7708. typeof val3 === 'number'
  7709. ) {
  7710. // Quadratic bezier to
  7711. if (
  7712. command === 'Q' ||
  7713. command === 'q'
  7714. ) {
  7715. ret.push([
  7716. command,
  7717. val0,
  7718. val1,
  7719. val2,
  7720. val3
  7721. ]);
  7722. i += 4;
  7723. // Smooth cubic bezier to
  7724. } else if (
  7725. command === 'S' ||
  7726. command === 's'
  7727. ) {
  7728. ret.push([
  7729. command,
  7730. val0,
  7731. val1,
  7732. val2,
  7733. val3
  7734. ]);
  7735. i += 4;
  7736. // Six numeric parameters
  7737. } else {
  7738. const val4 = path[i + 4],
  7739. val5 = path[i + 5];
  7740. if (
  7741. typeof val4 === 'number' &&
  7742. typeof val5 === 'number'
  7743. ) {
  7744. // Curve to
  7745. if (
  7746. command === 'C' ||
  7747. command === 'c'
  7748. ) {
  7749. ret.push([
  7750. command,
  7751. val0,
  7752. val1,
  7753. val2,
  7754. val3,
  7755. val4,
  7756. val5
  7757. ]);
  7758. i += 6;
  7759. // Seven numeric parameters
  7760. } else {
  7761. const val6 = path[i + 6];
  7762. // Arc to
  7763. if (
  7764. typeof val6 ===
  7765. 'number' &&
  7766. (
  7767. command === 'A' ||
  7768. command === 'a'
  7769. )
  7770. ) {
  7771. ret.push([
  7772. command,
  7773. val0,
  7774. val1,
  7775. val2,
  7776. val3,
  7777. val4,
  7778. val5,
  7779. val6
  7780. ]);
  7781. i += 7;
  7782. }
  7783. }
  7784. }
  7785. }
  7786. }
  7787. }
  7788. }
  7789. }
  7790. }
  7791. }
  7792. }
  7793. // An unmarked command following a moveTo is a lineTo
  7794. lastCommand = command === 'M' ? 'L' : command;
  7795. if (i === lastI) {
  7796. break;
  7797. }
  7798. lastI = i;
  7799. }
  7800. return ret;
  7801. */
  7802. };
  7803. /**
  7804. * Draw a label, which is an extended text element with support for border
  7805. * and background. Highcharts creates a `g` element with a text and a `path`
  7806. * or `rect` inside, to make it behave somewhat like a HTML div. Border and
  7807. * background are set through `stroke`, `stroke-width` and `fill` attributes
  7808. * using the {@link Highcharts.SVGElement#attr|attr} method. To update the
  7809. * text after render, run `label.attr({ text: 'New text' })`.
  7810. *
  7811. * @sample highcharts/members/renderer-label-on-chart/
  7812. * A label on the chart
  7813. *
  7814. * @function Highcharts.SVGRenderer#label
  7815. *
  7816. * @param {string} str
  7817. * The initial text string or (subset) HTML to render.
  7818. *
  7819. * @param {number} x
  7820. * The x position of the label's left side.
  7821. *
  7822. * @param {number} [y]
  7823. * The y position of the label's top side or baseline, depending on
  7824. * the `baseline` parameter.
  7825. *
  7826. * @param {string} [shape='rect']
  7827. * The shape of the label's border/background, if any. Defaults to
  7828. * `rect`. Other possible values are `callout` or other shapes
  7829. * defined in {@link Highcharts.SVGRenderer#symbols}.
  7830. *
  7831. * @param {number} [anchorX]
  7832. * In case the `shape` has a pointer, like a flag, this is the
  7833. * coordinates it should be pinned to.
  7834. *
  7835. * @param {number} [anchorY]
  7836. * In case the `shape` has a pointer, like a flag, this is the
  7837. * coordinates it should be pinned to.
  7838. *
  7839. * @param {boolean} [useHTML=false]
  7840. * Wether to use HTML to render the label.
  7841. *
  7842. * @param {boolean} [baseline=false]
  7843. * Whether to position the label relative to the text baseline,
  7844. * like {@link Highcharts.SVGRenderer#text|renderer.text}, or to the
  7845. * upper border of the rectangle.
  7846. *
  7847. * @param {string} [className]
  7848. * Class name for the group.
  7849. *
  7850. * @return {Highcharts.SVGElement}
  7851. * The generated label.
  7852. */
  7853. SVGRenderer.prototype.label = function (str, x, y, shape, anchorX, anchorY, useHTML, baseline, className) {
  7854. return new SVGLabel(this, str, x, y, shape, anchorX, anchorY, useHTML, baseline, className);
  7855. };
  7856. return SVGRenderer;
  7857. }());
  7858. /**
  7859. * A pointer to the renderer's associated Element class. The VMLRenderer
  7860. * will have a pointer to VMLElement here.
  7861. *
  7862. * @name Highcharts.SVGRenderer#Element
  7863. * @type {Highcharts.SVGElement}
  7864. */
  7865. SVGRenderer.prototype.Element = SVGElement;
  7866. /**
  7867. * @private
  7868. */
  7869. SVGRenderer.prototype.SVG_NS = SVG_NS;
  7870. /**
  7871. * Dummy function for plugins, called every time the renderer is updated.
  7872. * Prior to Highcharts 5, this was used for the canvg renderer.
  7873. *
  7874. * @deprecated
  7875. * @function Highcharts.SVGRenderer#draw
  7876. */
  7877. SVGRenderer.prototype.draw = noop;
  7878. /**
  7879. * A collection of characters mapped to HTML entities. When `useHTML` on an
  7880. * element is true, these entities will be rendered correctly by HTML. In
  7881. * the SVG pseudo-HTML, they need to be unescaped back to simple characters,
  7882. * so for example `&lt;` will render as `<`.
  7883. *
  7884. * @example
  7885. * // Add support for unescaping quotes
  7886. * Highcharts.SVGRenderer.prototype.escapes['"'] = '&quot;';
  7887. *
  7888. * @name Highcharts.SVGRenderer#escapes
  7889. * @type {Highcharts.Dictionary<string>}
  7890. */
  7891. SVGRenderer.prototype.escapes = {
  7892. '&': '&amp;',
  7893. '<': '&lt;',
  7894. '>': '&gt;',
  7895. "'": '&#39;',
  7896. '"': '&quot;'
  7897. };
  7898. /**
  7899. * An extendable collection of functions for defining symbol paths.
  7900. *
  7901. * @name Highcharts.SVGRenderer#symbols
  7902. * @type {Highcharts.SymbolDictionary}
  7903. */
  7904. SVGRenderer.prototype.symbols = {
  7905. circle: function (x, y, w, h) {
  7906. // Return a full arc
  7907. return this.arc(x + w / 2, y + h / 2, w / 2, h / 2, {
  7908. start: Math.PI * 0.5,
  7909. end: Math.PI * 2.5,
  7910. open: false
  7911. });
  7912. },
  7913. square: function (x, y, w, h) {
  7914. return [
  7915. ['M', x, y],
  7916. ['L', x + w, y],
  7917. ['L', x + w, y + h],
  7918. ['L', x, y + h],
  7919. ['Z']
  7920. ];
  7921. },
  7922. triangle: function (x, y, w, h) {
  7923. return [
  7924. ['M', x + w / 2, y],
  7925. ['L', x + w, y + h],
  7926. ['L', x, y + h],
  7927. ['Z']
  7928. ];
  7929. },
  7930. 'triangle-down': function (x, y, w, h) {
  7931. return [
  7932. ['M', x, y],
  7933. ['L', x + w, y],
  7934. ['L', x + w / 2, y + h],
  7935. ['Z']
  7936. ];
  7937. },
  7938. diamond: function (x, y, w, h) {
  7939. return [
  7940. ['M', x + w / 2, y],
  7941. ['L', x + w, y + h / 2],
  7942. ['L', x + w / 2, y + h],
  7943. ['L', x, y + h / 2],
  7944. ['Z']
  7945. ];
  7946. },
  7947. arc: function (x, y, w, h, options) {
  7948. var arc = [];
  7949. if (options) {
  7950. var start = options.start || 0, end = options.end || 0, rx = options.r || w, ry = options.r || h || w, proximity = 0.001, fullCircle = Math.abs(end - start - 2 * Math.PI) <
  7951. proximity,
  7952. // Substract a small number to prevent cos and sin of start and
  7953. // end from becoming equal on 360 arcs (related: #1561)
  7954. end = end - 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),
  7955. // Proximity takes care of rounding errors around PI (#6971)
  7956. longArc = pick(options.longArc, end - start - Math.PI < proximity ? 0 : 1);
  7957. arc.push([
  7958. 'M',
  7959. x + rx * cosStart,
  7960. y + ry * sinStart
  7961. ], [
  7962. 'A',
  7963. rx,
  7964. ry,
  7965. 0,
  7966. longArc,
  7967. pick(options.clockwise, 1),
  7968. x + rx * cosEnd,
  7969. y + ry * sinEnd
  7970. ]);
  7971. if (defined(innerRadius)) {
  7972. arc.push(open ?
  7973. [
  7974. 'M',
  7975. x + innerRadius * cosEnd,
  7976. y + innerRadius * sinEnd
  7977. ] : [
  7978. 'L',
  7979. x + innerRadius * cosEnd,
  7980. y + innerRadius * sinEnd
  7981. ], [
  7982. 'A',
  7983. innerRadius,
  7984. innerRadius,
  7985. 0,
  7986. longArc,
  7987. // Clockwise - opposite to the outer arc clockwise
  7988. defined(options.clockwise) ? 1 - options.clockwise : 0,
  7989. x + innerRadius * cosStart,
  7990. y + innerRadius * sinStart
  7991. ]);
  7992. }
  7993. if (!open) {
  7994. arc.push(['Z']);
  7995. }
  7996. }
  7997. return arc;
  7998. },
  7999. /**
  8000. * Callout shape used for default tooltips, also used for rounded
  8001. * rectangles in VML
  8002. */
  8003. callout: function (x, y, w, h, options) {
  8004. var arrowLength = 6, halfDistance = 6, r = Math.min((options && options.r) || 0, w, h), safeDistance = r + halfDistance, anchorX = options && options.anchorX || 0, anchorY = options && options.anchorY || 0, path;
  8005. path = [
  8006. ['M', x + r, y],
  8007. ['L', x + w - r, y],
  8008. ['C', x + w, y, x + w, y, x + w, y + r],
  8009. ['L', x + w, y + h - r],
  8010. ['C', x + w, y + h, x + w, y + h, x + w - r, y + h],
  8011. ['L', x + r, y + h],
  8012. ['C', x, y + h, x, y + h, x, y + h - r],
  8013. ['L', x, y + r],
  8014. ['C', x, y, x, y, x + r, y] // top-left corner
  8015. ];
  8016. // Anchor on right side
  8017. if (anchorX && anchorX > w) {
  8018. // Chevron
  8019. if (anchorY > y + safeDistance &&
  8020. anchorY < y + h - safeDistance) {
  8021. 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]);
  8022. // Simple connector
  8023. }
  8024. else {
  8025. path.splice(3, 1, ['L', x + w, h / 2], ['L', anchorX, anchorY], ['L', x + w, h / 2], ['L', x + w, y + h - r]);
  8026. }
  8027. // Anchor on left side
  8028. }
  8029. else if (anchorX && anchorX < 0) {
  8030. // Chevron
  8031. if (anchorY > y + safeDistance &&
  8032. anchorY < y + h - safeDistance) {
  8033. path.splice(7, 1, ['L', x, anchorY + halfDistance], ['L', x - arrowLength, anchorY], ['L', x, anchorY - halfDistance], ['L', x, y + r]);
  8034. // Simple connector
  8035. }
  8036. else {
  8037. path.splice(7, 1, ['L', x, h / 2], ['L', anchorX, anchorY], ['L', x, h / 2], ['L', x, y + r]);
  8038. }
  8039. }
  8040. else if ( // replace bottom
  8041. anchorY &&
  8042. anchorY > h &&
  8043. anchorX > x + safeDistance &&
  8044. anchorX < x + w - safeDistance) {
  8045. path.splice(5, 1, ['L', anchorX + halfDistance, y + h], ['L', anchorX, y + h + arrowLength], ['L', anchorX - halfDistance, y + h], ['L', x + r, y + h]);
  8046. }
  8047. else if ( // replace top
  8048. anchorY &&
  8049. anchorY < 0 &&
  8050. anchorX > x + safeDistance &&
  8051. anchorX < x + w - safeDistance) {
  8052. path.splice(1, 1, ['L', anchorX - halfDistance, y], ['L', anchorX, y - arrowLength], ['L', anchorX + halfDistance, y], ['L', w - r, y]);
  8053. }
  8054. return path;
  8055. }
  8056. };
  8057. H.SVGRenderer = SVGRenderer;
  8058. H.Renderer = H.SVGRenderer;
  8059. return H.Renderer;
  8060. });
  8061. _registerModule(_modules, 'parts/Html.js', [_modules['parts/Globals.js'], _modules['parts/SVGElement.js'], _modules['parts/SVGRenderer.js'], _modules['parts/Utilities.js']], function (H, SVGElement, SVGRenderer, U) {
  8062. /* *
  8063. *
  8064. * (c) 2010-2020 Torstein Honsi
  8065. *
  8066. * License: www.highcharts.com/license
  8067. *
  8068. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8069. *
  8070. * */
  8071. var attr = U.attr, createElement = U.createElement, css = U.css, defined = U.defined, extend = U.extend, pick = U.pick, pInt = U.pInt;
  8072. var isFirefox = H.isFirefox, isMS = H.isMS, isWebKit = H.isWebKit, win = H.win;
  8073. /* eslint-disable valid-jsdoc */
  8074. // Extend SvgElement for useHTML option.
  8075. extend(SVGElement.prototype, /** @lends SVGElement.prototype */ {
  8076. /**
  8077. * Apply CSS to HTML elements. This is used in text within SVG rendering and
  8078. * by the VML renderer
  8079. *
  8080. * @private
  8081. * @function Highcharts.SVGElement#htmlCss
  8082. *
  8083. * @param {Highcharts.CSSObject} styles
  8084. *
  8085. * @return {Highcharts.SVGElement}
  8086. */
  8087. htmlCss: function (styles) {
  8088. var wrapper = this, element = wrapper.element,
  8089. // When setting or unsetting the width style, we need to update
  8090. // transform (#8809)
  8091. isSettingWidth = (element.tagName === 'SPAN' &&
  8092. styles &&
  8093. 'width' in styles), textWidth = pick(isSettingWidth && styles.width, void 0), doTransform;
  8094. if (isSettingWidth) {
  8095. delete styles.width;
  8096. wrapper.textWidth = textWidth;
  8097. doTransform = true;
  8098. }
  8099. if (styles && styles.textOverflow === 'ellipsis') {
  8100. styles.whiteSpace = 'nowrap';
  8101. styles.overflow = 'hidden';
  8102. }
  8103. wrapper.styles = extend(wrapper.styles, styles);
  8104. css(wrapper.element, styles);
  8105. // Now that all styles are applied, to the transform
  8106. if (doTransform) {
  8107. wrapper.htmlUpdateTransform();
  8108. }
  8109. return wrapper;
  8110. },
  8111. /**
  8112. * VML and useHTML method for calculating the bounding box based on offsets.
  8113. *
  8114. * @private
  8115. * @function Highcharts.SVGElement#htmlGetBBox
  8116. *
  8117. * @param {boolean} refresh
  8118. * Whether to force a fresh value from the DOM or to use the cached
  8119. * value.
  8120. *
  8121. * @return {Highcharts.BBoxObject}
  8122. * A hash containing values for x, y, width and height.
  8123. */
  8124. htmlGetBBox: function () {
  8125. var wrapper = this, element = wrapper.element;
  8126. return {
  8127. x: element.offsetLeft,
  8128. y: element.offsetTop,
  8129. width: element.offsetWidth,
  8130. height: element.offsetHeight
  8131. };
  8132. },
  8133. /**
  8134. * VML override private method to update elements based on internal
  8135. * properties based on SVG transform.
  8136. *
  8137. * @private
  8138. * @function Highcharts.SVGElement#htmlUpdateTransform
  8139. * @return {void}
  8140. */
  8141. htmlUpdateTransform: function () {
  8142. // aligning non added elements is expensive
  8143. if (!this.added) {
  8144. this.alignOnAdd = true;
  8145. return;
  8146. }
  8147. var 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 = {
  8148. left: 0, center: 0.5, right: 1
  8149. }[align], styles = wrapper.styles, whiteSpace = styles && styles.whiteSpace;
  8150. /**
  8151. * @private
  8152. * @return {number}
  8153. */
  8154. function getTextPxLength() {
  8155. // Reset multiline/ellipsis in order to read width (#4928,
  8156. // #5417)
  8157. css(elem, {
  8158. width: '',
  8159. whiteSpace: whiteSpace || 'nowrap'
  8160. });
  8161. return elem.offsetWidth;
  8162. }
  8163. // apply translate
  8164. css(elem, {
  8165. marginLeft: translateX,
  8166. marginTop: translateY
  8167. });
  8168. if (!renderer.styledMode && wrapper.shadows) { // used in labels/tooltip
  8169. wrapper.shadows.forEach(function (shadow) {
  8170. css(shadow, {
  8171. marginLeft: translateX + 1,
  8172. marginTop: translateY + 1
  8173. });
  8174. });
  8175. }
  8176. // apply inversion
  8177. if (wrapper.inverted) { // wrapper is a group
  8178. [].forEach.call(elem.childNodes, function (child) {
  8179. renderer.invertChild(child, elem);
  8180. });
  8181. }
  8182. if (elem.tagName === 'SPAN') {
  8183. var rotation = wrapper.rotation, baseline, textWidth = wrapper.textWidth && pInt(wrapper.textWidth), currentTextTransform = [
  8184. rotation,
  8185. align,
  8186. elem.innerHTML,
  8187. wrapper.textWidth,
  8188. wrapper.textAlign
  8189. ].join(',');
  8190. // Update textWidth. Use the memoized textPxLength if possible, to
  8191. // avoid the getTextPxLength function using elem.offsetWidth.
  8192. // Calling offsetWidth affects rendering time as it forces layout
  8193. // (#7656).
  8194. if (textWidth !== wrapper.oldTextWidth &&
  8195. ((textWidth > wrapper.oldTextWidth) ||
  8196. (wrapper.textPxLength || getTextPxLength()) > textWidth) && (
  8197. // Only set the width if the text is able to word-wrap, or
  8198. // text-overflow is ellipsis (#9537)
  8199. /[ \-]/.test(elem.textContent || elem.innerText) ||
  8200. elem.style.textOverflow === 'ellipsis')) { // #983, #1254
  8201. css(elem, {
  8202. width: textWidth + 'px',
  8203. display: 'block',
  8204. whiteSpace: whiteSpace || 'normal' // #3331
  8205. });
  8206. wrapper.oldTextWidth = textWidth;
  8207. wrapper.hasBoxWidthChanged = true; // #8159
  8208. }
  8209. else {
  8210. wrapper.hasBoxWidthChanged = false; // #8159
  8211. }
  8212. // Do the calculations and DOM access only if properties changed
  8213. if (currentTextTransform !== wrapper.cTT) {
  8214. baseline = renderer.fontMetrics(elem.style.fontSize, elem).b;
  8215. // Renderer specific handling of span rotation, but only if we
  8216. // have something to update.
  8217. if (defined(rotation) &&
  8218. ((rotation !== (wrapper.oldRotation || 0)) ||
  8219. (align !== wrapper.oldAlign))) {
  8220. wrapper.setSpanRotation(rotation, alignCorrection, baseline);
  8221. }
  8222. wrapper.getSpanCorrection(
  8223. // Avoid elem.offsetWidth if we can, it affects rendering
  8224. // time heavily (#7656)
  8225. ((!defined(rotation) && wrapper.textPxLength) || // #7920
  8226. elem.offsetWidth), baseline, alignCorrection, rotation, align);
  8227. }
  8228. // apply position with correction
  8229. css(elem, {
  8230. left: (x + (wrapper.xCorr || 0)) + 'px',
  8231. top: (y + (wrapper.yCorr || 0)) + 'px'
  8232. });
  8233. // record current text transform
  8234. wrapper.cTT = currentTextTransform;
  8235. wrapper.oldRotation = rotation;
  8236. wrapper.oldAlign = align;
  8237. }
  8238. },
  8239. /**
  8240. * Set the rotation of an individual HTML span.
  8241. *
  8242. * @private
  8243. * @function Highcharts.SVGElement#setSpanRotation
  8244. * @param {number} rotation
  8245. * @param {number} alignCorrection
  8246. * @param {number} baseline
  8247. * @return {void}
  8248. */
  8249. setSpanRotation: function (rotation, alignCorrection, baseline) {
  8250. var rotationStyle = {}, cssTransformKey = this.renderer.getTransformKey();
  8251. rotationStyle[cssTransformKey] = rotationStyle.transform =
  8252. 'rotate(' + rotation + 'deg)';
  8253. rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] =
  8254. rotationStyle.transformOrigin =
  8255. (alignCorrection * 100) + '% ' + baseline + 'px';
  8256. css(this.element, rotationStyle);
  8257. },
  8258. /**
  8259. * Get the correction in X and Y positioning as the element is rotated.
  8260. *
  8261. * @private
  8262. * @function Highcharts.SVGElement#getSpanCorrection
  8263. * @param {number} width
  8264. * @param {number} baseline
  8265. * @param {number} alignCorrection
  8266. * @return {void}
  8267. */
  8268. getSpanCorrection: function (width, baseline, alignCorrection) {
  8269. this.xCorr = -width * alignCorrection;
  8270. this.yCorr = -baseline;
  8271. }
  8272. });
  8273. // Extend SvgRenderer for useHTML option.
  8274. extend(SVGRenderer.prototype, /** @lends SVGRenderer.prototype */ {
  8275. /**
  8276. * @private
  8277. * @function Highcharts.SVGRenderer#getTransformKey
  8278. *
  8279. * @return {string}
  8280. */
  8281. getTransformKey: function () {
  8282. return isMS && !/Edge/.test(win.navigator.userAgent) ?
  8283. '-ms-transform' :
  8284. isWebKit ?
  8285. '-webkit-transform' :
  8286. isFirefox ?
  8287. 'MozTransform' :
  8288. win.opera ?
  8289. '-o-transform' :
  8290. '';
  8291. },
  8292. /**
  8293. * Create HTML text node. This is used by the VML renderer as well as the
  8294. * SVG renderer through the useHTML option.
  8295. *
  8296. * @private
  8297. * @function Highcharts.SVGRenderer#html
  8298. *
  8299. * @param {string} str
  8300. * The text of (subset) HTML to draw.
  8301. *
  8302. * @param {number} x
  8303. * The x position of the text's lower left corner.
  8304. *
  8305. * @param {number} y
  8306. * The y position of the text's lower left corner.
  8307. *
  8308. * @return {Highcharts.HTMLDOMElement}
  8309. */
  8310. html: function (str, x, y) {
  8311. var wrapper = this.createElement('span'), element = wrapper.element, renderer = wrapper.renderer, isSVG = renderer.isSVG, addSetters = function (gWrapper, style) {
  8312. // These properties are set as attributes on the SVG group, and
  8313. // as identical CSS properties on the div. (#3542)
  8314. ['opacity', 'visibility'].forEach(function (prop) {
  8315. gWrapper[prop + 'Setter'] = function (value, key, elem) {
  8316. var styleObject = gWrapper.div ?
  8317. gWrapper.div.style :
  8318. style;
  8319. SVGElement.prototype[prop + 'Setter']
  8320. .call(this, value, key, elem);
  8321. if (styleObject) {
  8322. styleObject[key] = value;
  8323. }
  8324. };
  8325. });
  8326. gWrapper.addedSetters = true;
  8327. };
  8328. // Text setter
  8329. wrapper.textSetter = function (value) {
  8330. if (value !== element.innerHTML) {
  8331. delete this.bBox;
  8332. delete this.oldTextWidth;
  8333. }
  8334. this.textStr = value;
  8335. element.innerHTML = pick(value, '');
  8336. wrapper.doTransform = true;
  8337. };
  8338. // Add setters for the element itself (#4938)
  8339. if (isSVG) { // #4938, only for HTML within SVG
  8340. addSetters(wrapper, wrapper.element.style);
  8341. }
  8342. // Various setters which rely on update transform
  8343. wrapper.xSetter =
  8344. wrapper.ySetter =
  8345. wrapper.alignSetter =
  8346. wrapper.rotationSetter =
  8347. function (value, key) {
  8348. if (key === 'align') {
  8349. // Do not overwrite the SVGElement.align method. Same as VML.
  8350. key = 'textAlign';
  8351. }
  8352. wrapper[key] = value;
  8353. wrapper.doTransform = true;
  8354. };
  8355. // Runs at the end of .attr()
  8356. wrapper.afterSetters = function () {
  8357. // Update transform. Do this outside the loop to prevent redundant
  8358. // updating for batch setting of attributes.
  8359. if (this.doTransform) {
  8360. this.htmlUpdateTransform();
  8361. this.doTransform = false;
  8362. }
  8363. };
  8364. // Set the default attributes
  8365. wrapper
  8366. .attr({
  8367. text: str,
  8368. x: Math.round(x),
  8369. y: Math.round(y)
  8370. })
  8371. .css({
  8372. position: 'absolute'
  8373. });
  8374. if (!renderer.styledMode) {
  8375. wrapper.css({
  8376. fontFamily: this.style.fontFamily,
  8377. fontSize: this.style.fontSize
  8378. });
  8379. }
  8380. // Keep the whiteSpace style outside the wrapper.styles collection
  8381. element.style.whiteSpace = 'nowrap';
  8382. // Use the HTML specific .css method
  8383. wrapper.css = wrapper.htmlCss;
  8384. // This is specific for HTML within SVG
  8385. if (isSVG) {
  8386. wrapper.add = function (svgGroupWrapper) {
  8387. var htmlGroup, container = renderer.box.parentNode, parentGroup, parents = [];
  8388. this.parentGroup = svgGroupWrapper;
  8389. // Create a mock group to hold the HTML elements
  8390. if (svgGroupWrapper) {
  8391. htmlGroup = svgGroupWrapper.div;
  8392. if (!htmlGroup) {
  8393. // Read the parent chain into an array and read from top
  8394. // down
  8395. parentGroup = svgGroupWrapper;
  8396. while (parentGroup) {
  8397. parents.push(parentGroup);
  8398. // Move up to the next parent group
  8399. parentGroup = parentGroup.parentGroup;
  8400. }
  8401. // Ensure dynamically updating position when any parent
  8402. // is translated
  8403. parents.reverse().forEach(function (parentGroup) {
  8404. var htmlGroupStyle, cls = attr(parentGroup.element, 'class');
  8405. /**
  8406. * Common translate setter for X and Y on the HTML
  8407. * group. Reverted the fix for #6957 du to
  8408. * positioning problems and offline export (#7254,
  8409. * #7280, #7529)
  8410. * @private
  8411. * @param {*} value
  8412. * @param {string} key
  8413. * @return {void}
  8414. */
  8415. function translateSetter(value, key) {
  8416. parentGroup[key] = value;
  8417. if (key === 'translateX') {
  8418. htmlGroupStyle.left = value + 'px';
  8419. }
  8420. else {
  8421. htmlGroupStyle.top = value + 'px';
  8422. }
  8423. parentGroup.doTransform = true;
  8424. }
  8425. // Create a HTML div and append it to the parent div
  8426. // to emulate the SVG group structure
  8427. htmlGroup =
  8428. parentGroup.div =
  8429. parentGroup.div || createElement('div', cls ? { className: cls } : void 0, {
  8430. position: 'absolute',
  8431. left: (parentGroup.translateX || 0) + 'px',
  8432. top: (parentGroup.translateY || 0) + 'px',
  8433. display: parentGroup.display,
  8434. opacity: parentGroup.opacity,
  8435. pointerEvents: (parentGroup.styles &&
  8436. parentGroup.styles.pointerEvents) // #5595
  8437. // the top group is appended to container
  8438. }, htmlGroup || container);
  8439. // Shortcut
  8440. htmlGroupStyle = htmlGroup.style;
  8441. // Set listeners to update the HTML div's position
  8442. // whenever the SVG group position is changed.
  8443. extend(parentGroup, {
  8444. // (#7287) Pass htmlGroup to use
  8445. // the related group
  8446. classSetter: (function (htmlGroup) {
  8447. return function (value) {
  8448. this.element.setAttribute('class', value);
  8449. htmlGroup.className = value;
  8450. };
  8451. }(htmlGroup)),
  8452. on: function () {
  8453. if (parents[0].div) { // #6418
  8454. wrapper.on.apply({ element: parents[0].div }, arguments);
  8455. }
  8456. return parentGroup;
  8457. },
  8458. translateXSetter: translateSetter,
  8459. translateYSetter: translateSetter
  8460. });
  8461. if (!parentGroup.addedSetters) {
  8462. addSetters(parentGroup);
  8463. }
  8464. });
  8465. }
  8466. }
  8467. else {
  8468. htmlGroup = container;
  8469. }
  8470. htmlGroup.appendChild(element);
  8471. // Shared with VML:
  8472. wrapper.added = true;
  8473. if (wrapper.alignOnAdd) {
  8474. wrapper.htmlUpdateTransform();
  8475. }
  8476. return wrapper;
  8477. };
  8478. }
  8479. return wrapper;
  8480. }
  8481. });
  8482. });
  8483. _registerModule(_modules, 'parts/Tick.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  8484. /* *
  8485. *
  8486. * (c) 2010-2020 Torstein Honsi
  8487. *
  8488. * License: www.highcharts.com/license
  8489. *
  8490. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8491. *
  8492. * */
  8493. /**
  8494. * Optional parameters for the tick.
  8495. * @private
  8496. * @interface Highcharts.TickParametersObject
  8497. */ /**
  8498. * Set category for the tick.
  8499. * @name Highcharts.TickParametersObject#category
  8500. * @type {string|undefined}
  8501. */ /**
  8502. * @name Highcharts.TickParametersObject#options
  8503. * @type {Highcharts.Dictionary<any>|undefined}
  8504. */ /**
  8505. * Set tickmarkOffset for the tick.
  8506. * @name Highcharts.TickParametersObject#tickmarkOffset
  8507. * @type {number|undefined}
  8508. */
  8509. var clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, extend = U.extend, fireEvent = U.fireEvent, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick;
  8510. var deg2rad = H.deg2rad;
  8511. /* eslint-disable no-invalid-this, valid-jsdoc */
  8512. /**
  8513. * The Tick class.
  8514. *
  8515. * @class
  8516. * @name Highcharts.Tick
  8517. *
  8518. * @param {Highcharts.Axis} axis
  8519. * The axis of the tick.
  8520. *
  8521. * @param {number} pos
  8522. * The position of the tick on the axis in terms of axis values.
  8523. *
  8524. * @param {string} [type]
  8525. * The type of tick, either 'minor' or an empty string
  8526. *
  8527. * @param {boolean} [noLabel=false]
  8528. * Whether to disable the label or not. Defaults to false.
  8529. *
  8530. * @param {object} [parameters]
  8531. * Optional parameters for the tick.
  8532. */
  8533. var Tick = /** @class */ (function () {
  8534. /* *
  8535. *
  8536. * Constructors
  8537. *
  8538. * */
  8539. function Tick(axis, pos, type, noLabel, parameters) {
  8540. this.isNew = true;
  8541. this.isNewLabel = true;
  8542. /**
  8543. * The related axis of the tick.
  8544. * @name Highcharts.Tick#axis
  8545. * @type {Highcharts.Axis}
  8546. */
  8547. this.axis = axis;
  8548. /**
  8549. * The logical position of the tick on the axis in terms of axis values.
  8550. * @name Highcharts.Tick#pos
  8551. * @type {number}
  8552. */
  8553. this.pos = pos;
  8554. /**
  8555. * The tick type, which can be `"minor"`, or an empty string.
  8556. * @name Highcharts.Tick#type
  8557. * @type {string}
  8558. */
  8559. this.type = type || '';
  8560. this.parameters = parameters || {};
  8561. /**
  8562. * The mark offset of the tick on the axis. Usually `undefined`, numeric
  8563. * for grid axes.
  8564. * @name Highcharts.Tick#tickmarkOffset
  8565. * @type {number|undefined}
  8566. */
  8567. this.tickmarkOffset = this.parameters.tickmarkOffset;
  8568. this.options = this.parameters.options;
  8569. fireEvent(this, 'init');
  8570. if (!type && !noLabel) {
  8571. this.addLabel();
  8572. }
  8573. }
  8574. /* *
  8575. *
  8576. * Functions
  8577. *
  8578. * */
  8579. /**
  8580. * Write the tick label.
  8581. *
  8582. * @private
  8583. * @function Highcharts.Tick#addLabel
  8584. * @return {void}
  8585. */
  8586. Tick.prototype.addLabel = function () {
  8587. var 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), str, tickPositions = axis.tickPositions, isFirst = pos === tickPositions[0], isLast = pos === tickPositions[tickPositions.length - 1], value = this.parameters.category || (categories ?
  8588. pick(categories[pos], names[pos], pos) :
  8589. pos), label = tick.label, animateLabels = (!labelOptions.step || labelOptions.step === 1) &&
  8590. axis.tickInterval === 1, tickPositionInfo = tickPositions.info, dateTimeLabelFormat, dateTimeLabelFormats, i, list;
  8591. // Set the datetime label format. If a higher rank is set for this
  8592. // position, use that. If not, use the general format.
  8593. if (axis.dateTime && tickPositionInfo) {
  8594. dateTimeLabelFormats = chart.time.resolveDTLFormat(options.dateTimeLabelFormats[(!options.grid &&
  8595. tickPositionInfo.higherRanks[pos]) ||
  8596. tickPositionInfo.unitName]);
  8597. dateTimeLabelFormat = dateTimeLabelFormats.main;
  8598. }
  8599. // set properties for access in render method
  8600. /**
  8601. * True if the tick is the first one on the axis.
  8602. * @name Highcharts.Tick#isFirst
  8603. * @readonly
  8604. * @type {boolean|undefined}
  8605. */
  8606. tick.isFirst = isFirst;
  8607. /**
  8608. * True if the tick is the last one on the axis.
  8609. * @name Highcharts.Tick#isLast
  8610. * @readonly
  8611. * @type {boolean|undefined}
  8612. */
  8613. tick.isLast = isLast;
  8614. // Get the string
  8615. tick.formatCtx = {
  8616. axis: axis,
  8617. chart: chart,
  8618. isFirst: isFirst,
  8619. isLast: isLast,
  8620. dateTimeLabelFormat: dateTimeLabelFormat,
  8621. tickPositionInfo: tickPositionInfo,
  8622. value: log ? correctFloat(log.lin2log(value)) : value,
  8623. pos: pos
  8624. };
  8625. str = axis.labelFormatter.call(tick.formatCtx, this.formatCtx);
  8626. // Set up conditional formatting based on the format list if existing.
  8627. list = dateTimeLabelFormats && dateTimeLabelFormats.list;
  8628. if (list) {
  8629. tick.shortenLabel = function () {
  8630. for (i = 0; i < list.length; i++) {
  8631. label.attr({
  8632. text: axis.labelFormatter.call(extend(tick.formatCtx, { dateTimeLabelFormat: list[i] }))
  8633. });
  8634. if (label.getBBox().width <
  8635. axis.getSlotWidth(tick) - 2 *
  8636. pick(labelOptions.padding, 5)) {
  8637. return;
  8638. }
  8639. }
  8640. label.attr({
  8641. text: ''
  8642. });
  8643. };
  8644. }
  8645. // Call only after first render
  8646. if (animateLabels && axis._addedPlotLB && axis.isXAxis) {
  8647. tick.moveLabel(str, labelOptions);
  8648. }
  8649. // First call
  8650. if (!defined(label) && !tick.movedLabel) {
  8651. /**
  8652. * The rendered text label of the tick.
  8653. * @name Highcharts.Tick#label
  8654. * @type {Highcharts.SVGElement|undefined}
  8655. */
  8656. tick.label = label = tick.createLabel({ x: 0, y: 0 }, str, labelOptions);
  8657. // Base value to detect change for new calls to getBBox
  8658. tick.rotation = 0;
  8659. // update
  8660. }
  8661. else if (label && label.textStr !== str && !animateLabels) {
  8662. // When resetting text, also reset the width if dynamically set
  8663. // (#8809)
  8664. if (label.textWidth &&
  8665. !(labelOptions.style && labelOptions.style.width) &&
  8666. !label.styles.width) {
  8667. label.css({ width: null });
  8668. }
  8669. label.attr({ text: str });
  8670. label.textPxLength = label.getBBox().width;
  8671. }
  8672. };
  8673. /**
  8674. * Render and return the label of the tick.
  8675. *
  8676. * @private
  8677. * @function Highcharts.Tick#createLabel
  8678. * @param {Highcharts.PositionObject} xy
  8679. * @param {string} str
  8680. * @param {Highcharts.XAxisLabelsOptions} labelOptions
  8681. * @return {Highcharts.SVGElement|undefined}
  8682. */
  8683. Tick.prototype.createLabel = function (xy, str, labelOptions) {
  8684. var axis = this.axis, chart = axis.chart, label = defined(str) && labelOptions.enabled ?
  8685. chart.renderer
  8686. .text(str, xy.x, xy.y, labelOptions.useHTML)
  8687. .add(axis.labelGroup) :
  8688. null;
  8689. // Un-rotated length
  8690. if (label) {
  8691. // Without position absolute, IE export sometimes is wrong
  8692. if (!chart.styledMode) {
  8693. label.css(merge(labelOptions.style));
  8694. }
  8695. label.textPxLength = label.getBBox().width;
  8696. }
  8697. return label;
  8698. };
  8699. /**
  8700. * Destructor for the tick prototype
  8701. *
  8702. * @private
  8703. * @function Highcharts.Tick#destroy
  8704. * @return {void}
  8705. */
  8706. Tick.prototype.destroy = function () {
  8707. destroyObjectProperties(this, this.axis);
  8708. };
  8709. /**
  8710. * Gets the x and y positions for ticks in terms of pixels.
  8711. *
  8712. * @private
  8713. * @function Highcharts.Tick#getPosition
  8714. *
  8715. * @param {boolean} horiz
  8716. * Whether the tick is on an horizontal axis or not.
  8717. *
  8718. * @param {number} tickPos
  8719. * Position of the tick.
  8720. *
  8721. * @param {number} tickmarkOffset
  8722. * Tickmark offset for all ticks.
  8723. *
  8724. * @param {boolean} [old]
  8725. * Whether the axis has changed or not.
  8726. *
  8727. * @return {Highcharts.PositionObject}
  8728. * The tick position.
  8729. *
  8730. * @fires Highcharts.Tick#event:afterGetPosition
  8731. */
  8732. Tick.prototype.getPosition = function (horiz, tickPos, tickmarkOffset, old) {
  8733. var axis = this.axis, chart = axis.chart, cHeight = (old && chart.oldChartHeight) || chart.chartHeight, pos;
  8734. pos = {
  8735. x: horiz ?
  8736. correctFloat(axis.translate(tickPos + tickmarkOffset, null, null, old) +
  8737. axis.transB) :
  8738. (axis.left +
  8739. axis.offset +
  8740. (axis.opposite ?
  8741. (((old && chart.oldChartWidth) ||
  8742. chart.chartWidth) -
  8743. axis.right -
  8744. axis.left) :
  8745. 0)),
  8746. y: horiz ?
  8747. (cHeight -
  8748. axis.bottom +
  8749. axis.offset -
  8750. (axis.opposite ? axis.height : 0)) :
  8751. correctFloat(cHeight -
  8752. axis.translate(tickPos + tickmarkOffset, null, null, old) -
  8753. axis.transB)
  8754. };
  8755. // Chrome workaround for #10516
  8756. pos.y = clamp(pos.y, -1e5, 1e5);
  8757. fireEvent(this, 'afterGetPosition', { pos: pos });
  8758. return pos;
  8759. };
  8760. /**
  8761. * Get the x, y position of the tick label
  8762. *
  8763. * @private
  8764. * @return {Highcharts.PositionObject}
  8765. */
  8766. Tick.prototype.getLabelPosition = function (x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  8767. var axis = this.axis, transA = axis.transA, reversed = ( // #7911
  8768. axis.isLinked && axis.linkedParent ?
  8769. axis.linkedParent.reversed :
  8770. axis.reversed), staggerLines = axis.staggerLines, rotCorr = axis.tickRotCorr || { x: 0, y: 0 }, yOffset = labelOptions.y,
  8771. // Adjust for label alignment if we use reserveSpace: true (#5286)
  8772. labelOffsetCorrection = (!horiz && !axis.reserveSpaceDefault ?
  8773. -axis.labelOffset * (axis.labelAlign === 'center' ? 0.5 : 1) :
  8774. 0), line, pos = {};
  8775. if (!defined(yOffset)) {
  8776. if (axis.side === 0) {
  8777. yOffset = label.rotation ? -8 : -label.getBBox().height;
  8778. }
  8779. else if (axis.side === 2) {
  8780. yOffset = rotCorr.y + 8;
  8781. }
  8782. else {
  8783. // #3140, #3140
  8784. yOffset = Math.cos(label.rotation * deg2rad) *
  8785. (rotCorr.y - label.getBBox(false, 0).height / 2);
  8786. }
  8787. }
  8788. x = x +
  8789. labelOptions.x +
  8790. labelOffsetCorrection +
  8791. rotCorr.x -
  8792. (tickmarkOffset && horiz ?
  8793. tickmarkOffset * transA * (reversed ? -1 : 1) :
  8794. 0);
  8795. y = y + yOffset - (tickmarkOffset && !horiz ?
  8796. tickmarkOffset * transA * (reversed ? 1 : -1) : 0);
  8797. // Correct for staggered labels
  8798. if (staggerLines) {
  8799. line = (index / (step || 1) % staggerLines);
  8800. if (axis.opposite) {
  8801. line = staggerLines - line - 1;
  8802. }
  8803. y += line * (axis.labelOffset / staggerLines);
  8804. }
  8805. pos.x = x;
  8806. pos.y = Math.round(y);
  8807. fireEvent(this, 'afterGetLabelPosition', { pos: pos, tickmarkOffset: tickmarkOffset, index: index });
  8808. return pos;
  8809. };
  8810. /**
  8811. * Get the offset height or width of the label
  8812. *
  8813. * @private
  8814. * @function Highcharts.Tick#getLabelSize
  8815. * @return {number}
  8816. */
  8817. Tick.prototype.getLabelSize = function () {
  8818. return this.label ?
  8819. this.label.getBBox()[this.axis.horiz ? 'height' : 'width'] :
  8820. 0;
  8821. };
  8822. /**
  8823. * Extendible method to return the path of the marker
  8824. *
  8825. * @private
  8826. *
  8827. */
  8828. Tick.prototype.getMarkPath = function (x, y, tickLength, tickWidth, horiz, renderer) {
  8829. return renderer.crispLine([[
  8830. 'M',
  8831. x,
  8832. y
  8833. ], [
  8834. 'L',
  8835. x + (horiz ? 0 : -tickLength),
  8836. y + (horiz ? tickLength : 0)
  8837. ]], tickWidth);
  8838. };
  8839. /**
  8840. * Handle the label overflow by adjusting the labels to the left and right
  8841. * edge, or hide them if they collide into the neighbour label.
  8842. *
  8843. * @private
  8844. * @function Highcharts.Tick#handleOverflow
  8845. * @param {Highcharts.PositionObject} xy
  8846. * @return {void}
  8847. */
  8848. Tick.prototype.handleOverflow = function (xy) {
  8849. var 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 = {
  8850. left: 0,
  8851. center: 0.5,
  8852. right: 1
  8853. }[axis.labelAlign || label.attr('align')], labelWidth = label.getBBox().width, slotWidth = axis.getSlotWidth(tick), modifiedSlotWidth = slotWidth, xCorrection = factor, goRight = 1, leftPos, rightPos, textWidth, css = {};
  8854. // Check if the label overshoots the chart spacing box. If it does, move
  8855. // it. If it now overshoots the slotWidth, add ellipsis.
  8856. if (!rotation &&
  8857. pick(labelOptions.overflow, 'justify') === 'justify') {
  8858. leftPos = pxPos - factor * labelWidth;
  8859. rightPos = pxPos + (1 - factor) * labelWidth;
  8860. if (leftPos < leftBound) {
  8861. modifiedSlotWidth =
  8862. xy.x + modifiedSlotWidth * (1 - factor) - leftBound;
  8863. }
  8864. else if (rightPos > rightBound) {
  8865. modifiedSlotWidth =
  8866. rightBound - xy.x + modifiedSlotWidth * factor;
  8867. goRight = -1;
  8868. }
  8869. modifiedSlotWidth = Math.min(slotWidth, modifiedSlotWidth); // #4177
  8870. if (modifiedSlotWidth < slotWidth && axis.labelAlign === 'center') {
  8871. xy.x += (goRight *
  8872. (slotWidth -
  8873. modifiedSlotWidth -
  8874. xCorrection * (slotWidth - Math.min(labelWidth, modifiedSlotWidth))));
  8875. }
  8876. // If the label width exceeds the available space, set a text width
  8877. // to be picked up below. Also, if a width has been set before, we
  8878. // need to set a new one because the reported labelWidth will be
  8879. // limited by the box (#3938).
  8880. if (labelWidth > modifiedSlotWidth ||
  8881. (axis.autoRotation && (label.styles || {}).width)) {
  8882. textWidth = modifiedSlotWidth;
  8883. }
  8884. // Add ellipsis to prevent rotated labels to be clipped against the edge
  8885. // of the chart
  8886. }
  8887. else if (rotation < 0 &&
  8888. pxPos - factor * labelWidth < leftBound) {
  8889. textWidth = Math.round(pxPos / Math.cos(rotation * deg2rad) - leftBound);
  8890. }
  8891. else if (rotation > 0 &&
  8892. pxPos + factor * labelWidth > rightBound) {
  8893. textWidth = Math.round((chartWidth - pxPos) /
  8894. Math.cos(rotation * deg2rad));
  8895. }
  8896. if (textWidth) {
  8897. if (tick.shortenLabel) {
  8898. tick.shortenLabel();
  8899. }
  8900. else {
  8901. css.width = Math.floor(textWidth) + 'px';
  8902. if (!(labelOptions.style || {}).textOverflow) {
  8903. css.textOverflow = 'ellipsis';
  8904. }
  8905. label.css(css);
  8906. }
  8907. }
  8908. };
  8909. /**
  8910. * Try to replace the label if the same one already exists.
  8911. *
  8912. * @private
  8913. * @function Highcharts.Tick#moveLabel
  8914. * @param {string} str
  8915. * @param {Highcharts.XAxisLabelsOptions} labelOptions
  8916. *
  8917. * @return {void}
  8918. */
  8919. Tick.prototype.moveLabel = function (str, labelOptions) {
  8920. var tick = this, label = tick.label, moved = false, xAxis = tick.axis, chart = xAxis.chart, labelPos, reversed = xAxis.reversed, inverted = chart.inverted, xPos, yPos;
  8921. if (label && label.textStr === str) {
  8922. tick.movedLabel = label;
  8923. moved = true;
  8924. delete tick.label;
  8925. }
  8926. else { // Find a label with the same string
  8927. objectEach(xAxis.ticks, function (currentTick) {
  8928. if (!moved &&
  8929. !currentTick.isNew &&
  8930. currentTick !== tick &&
  8931. currentTick.label &&
  8932. currentTick.label.textStr === str) {
  8933. tick.movedLabel = currentTick.label;
  8934. moved = true;
  8935. currentTick.labelPos = tick.movedLabel.xy;
  8936. delete currentTick.label;
  8937. }
  8938. });
  8939. }
  8940. // Create new label if the actual one is moved
  8941. if (!moved && (tick.labelPos || label)) {
  8942. labelPos = tick.labelPos || label.xy;
  8943. xPos = inverted ?
  8944. labelPos.x : (reversed ? 0 : xAxis.width + xAxis.left);
  8945. yPos = inverted ?
  8946. (reversed ? (xAxis.width + xAxis.left) : 0) : labelPos.y;
  8947. tick.movedLabel = tick.createLabel({ x: xPos, y: yPos }, str, labelOptions);
  8948. if (tick.movedLabel) {
  8949. tick.movedLabel.attr({ opacity: 0 });
  8950. }
  8951. }
  8952. };
  8953. /**
  8954. * Put everything in place
  8955. *
  8956. * @private
  8957. * @param {number} index
  8958. * @param {boolean} [old]
  8959. * Use old coordinates to prepare an animation into new position
  8960. * @param {number} [opacity]
  8961. * @return {voids}
  8962. */
  8963. Tick.prototype.render = function (index, old, opacity) {
  8964. var 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) ||
  8965. (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
  8966. opacity = pick(opacity, 1);
  8967. this.isActive = true;
  8968. // Create the grid line
  8969. this.renderGridLine(old, opacity, reverseCrisp);
  8970. // create the tick mark
  8971. this.renderMark(xy, opacity, reverseCrisp);
  8972. // the label is created on init - now move it into place
  8973. this.renderLabel(xy, old, opacity, index);
  8974. tick.isNew = false;
  8975. fireEvent(this, 'afterRender');
  8976. };
  8977. /**
  8978. * Renders the gridLine.
  8979. *
  8980. * @private
  8981. * @param {boolean} old Whether or not the tick is old
  8982. * @param {number} opacity The opacity of the grid line
  8983. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  8984. * @return {void}
  8985. */
  8986. Tick.prototype.renderGridLine = function (old, opacity, reverseCrisp) {
  8987. var tick = this, axis = tick.axis, options = axis.options, gridLine = tick.gridLine, gridLinePath, attribs = {}, pos = tick.pos, type = tick.type, tickmarkOffset = pick(tick.tickmarkOffset, axis.tickmarkOffset), renderer = axis.chart.renderer, gridPrefix = type ? type + 'Grid' : 'grid', gridLineWidth = options[gridPrefix + 'LineWidth'], gridLineColor = options[gridPrefix + 'LineColor'], dashStyle = options[gridPrefix + 'LineDashStyle'];
  8988. if (!gridLine) {
  8989. if (!axis.chart.styledMode) {
  8990. attribs.stroke = gridLineColor;
  8991. attribs['stroke-width'] = gridLineWidth;
  8992. if (dashStyle) {
  8993. attribs.dashstyle = dashStyle;
  8994. }
  8995. }
  8996. if (!type) {
  8997. attribs.zIndex = 1;
  8998. }
  8999. if (old) {
  9000. opacity = 0;
  9001. }
  9002. /**
  9003. * The rendered grid line of the tick.
  9004. * @name Highcharts.Tick#gridLine
  9005. * @type {Highcharts.SVGElement|undefined}
  9006. */
  9007. tick.gridLine = gridLine = renderer.path()
  9008. .attr(attribs)
  9009. .addClass('highcharts-' + (type ? type + '-' : '') + 'grid-line')
  9010. .add(axis.gridGroup);
  9011. }
  9012. if (gridLine) {
  9013. gridLinePath = axis.getPlotLinePath({
  9014. value: pos + tickmarkOffset,
  9015. lineWidth: gridLine.strokeWidth() * reverseCrisp,
  9016. force: 'pass',
  9017. old: old
  9018. });
  9019. // If the parameter 'old' is set, the current call will be followed
  9020. // by another call, therefore do not do any animations this time
  9021. if (gridLinePath) {
  9022. gridLine[old || tick.isNew ? 'attr' : 'animate']({
  9023. d: gridLinePath,
  9024. opacity: opacity
  9025. });
  9026. }
  9027. }
  9028. };
  9029. /**
  9030. * Renders the tick mark.
  9031. *
  9032. * @private
  9033. * @param {Highcharts.PositionObject} xy The position vector of the mark
  9034. * @param {number} opacity The opacity of the mark
  9035. * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
  9036. * @return {void}
  9037. */
  9038. Tick.prototype.renderMark = function (xy, opacity, reverseCrisp) {
  9039. var tick = this, axis = tick.axis, options = axis.options, renderer = axis.chart.renderer, type = tick.type, tickPrefix = type ? type + 'Tick' : 'tick', tickSize = axis.tickSize(tickPrefix), mark = tick.mark, isNewMark = !mark, x = xy.x, y = xy.y, tickWidth = pick(options[tickPrefix + 'Width'], !type && axis.isXAxis ? 1 : 0), // X axis defaults to 1
  9040. tickColor = options[tickPrefix + 'Color'];
  9041. if (tickSize) {
  9042. // negate the length
  9043. if (axis.opposite) {
  9044. tickSize[0] = -tickSize[0];
  9045. }
  9046. // First time, create it
  9047. if (isNewMark) {
  9048. /**
  9049. * The rendered mark of the tick.
  9050. * @name Highcharts.Tick#mark
  9051. * @type {Highcharts.SVGElement|undefined}
  9052. */
  9053. tick.mark = mark = renderer.path()
  9054. .addClass('highcharts-' + (type ? type + '-' : '') + 'tick')
  9055. .add(axis.axisGroup);
  9056. if (!axis.chart.styledMode) {
  9057. mark.attr({
  9058. stroke: tickColor,
  9059. 'stroke-width': tickWidth
  9060. });
  9061. }
  9062. }
  9063. mark[isNewMark ? 'attr' : 'animate']({
  9064. d: tick.getMarkPath(x, y, tickSize[0], mark.strokeWidth() * reverseCrisp, axis.horiz, renderer),
  9065. opacity: opacity
  9066. });
  9067. }
  9068. };
  9069. /**
  9070. * Renders the tick label.
  9071. * Note: The label should already be created in init(), so it should only
  9072. * have to be moved into place.
  9073. *
  9074. * @private
  9075. * @param {Highcharts.PositionObject} xy The position vector of the label
  9076. * @param {boolean} old Whether or not the tick is old
  9077. * @param {number} opacity The opacity of the label
  9078. * @param {number} index The index of the tick
  9079. * @return {void}
  9080. */
  9081. Tick.prototype.renderLabel = function (xy, old, opacity, index) {
  9082. var 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), show = true, x = xy.x, y = xy.y;
  9083. if (label && isNumber(x)) {
  9084. label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
  9085. // Apply show first and show last. If the tick is both first and
  9086. // last, it is a single centered tick, in which case we show the
  9087. // label anyway (#2100).
  9088. if ((tick.isFirst &&
  9089. !tick.isLast &&
  9090. !pick(options.showFirstLabel, 1)) ||
  9091. (tick.isLast &&
  9092. !tick.isFirst &&
  9093. !pick(options.showLastLabel, 1))) {
  9094. show = false;
  9095. // Handle label overflow and show or hide accordingly
  9096. }
  9097. else if (horiz &&
  9098. !labelOptions.step &&
  9099. !labelOptions.rotation &&
  9100. !old &&
  9101. opacity !== 0) {
  9102. tick.handleOverflow(xy);
  9103. }
  9104. // apply step
  9105. if (step && index % step) {
  9106. // show those indices dividable by step
  9107. show = false;
  9108. }
  9109. // Set the new position, and show or hide
  9110. if (show && isNumber(xy.y)) {
  9111. xy.opacity = opacity;
  9112. label[tick.isNewLabel ? 'attr' : 'animate'](xy);
  9113. tick.isNewLabel = false;
  9114. }
  9115. else {
  9116. label.attr('y', -9999); // #1338
  9117. tick.isNewLabel = true;
  9118. }
  9119. }
  9120. };
  9121. /**
  9122. * Replace labels with the moved ones to perform animation. Additionally
  9123. * destroy unused labels.
  9124. *
  9125. * @private
  9126. * @function Highcharts.Tick#replaceMovedLabel
  9127. * @return {void}
  9128. */
  9129. Tick.prototype.replaceMovedLabel = function () {
  9130. var tick = this, label = tick.label, axis = tick.axis, reversed = axis.reversed, chart = tick.axis.chart, inverted = chart.inverted, x, y;
  9131. // Animate and destroy
  9132. if (label && !tick.isNew) {
  9133. x = inverted ? label.xy.x : (reversed ? axis.left : axis.width + axis.left);
  9134. y = inverted ?
  9135. (reversed ? axis.width + axis.top : axis.top) :
  9136. label.xy.y;
  9137. label.animate({ x: x, y: y, opacity: 0 }, void 0, label.destroy);
  9138. delete tick.label;
  9139. }
  9140. axis.isDirty = true;
  9141. tick.label = tick.movedLabel;
  9142. delete tick.movedLabel;
  9143. };
  9144. return Tick;
  9145. }());
  9146. H.Tick = Tick;
  9147. return H.Tick;
  9148. });
  9149. _registerModule(_modules, 'parts/Time.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Highcharts, U) {
  9150. /* *
  9151. *
  9152. * (c) 2010-2020 Torstein Honsi
  9153. *
  9154. * License: www.highcharts.com/license
  9155. *
  9156. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  9157. *
  9158. * */
  9159. /**
  9160. * Normalized interval.
  9161. *
  9162. * @interface Highcharts.TimeNormalizedObject
  9163. */ /**
  9164. * The count.
  9165. *
  9166. * @name Highcharts.TimeNormalizedObject#count
  9167. * @type {number}
  9168. */ /**
  9169. * The interval in axis values (ms).
  9170. *
  9171. * @name Highcharts.TimeNormalizedObject#unitRange
  9172. * @type {number}
  9173. */
  9174. /**
  9175. * Function of an additional date format specifier.
  9176. *
  9177. * @callback Highcharts.TimeFormatCallbackFunction
  9178. *
  9179. * @param {number} timestamp
  9180. * The time to format.
  9181. *
  9182. * @return {string}
  9183. * The formatted portion of the date.
  9184. */
  9185. /**
  9186. * Additonal time tick information.
  9187. *
  9188. * @interface Highcharts.TimeTicksInfoObject
  9189. * @extends Highcharts.TimeNormalizedObject
  9190. */ /**
  9191. * @name Highcharts.TimeTicksInfoObject#higherRanks
  9192. * @type {Array<string>}
  9193. */ /**
  9194. * @name Highcharts.TimeTicksInfoObject#totalRange
  9195. * @type {number}
  9196. */
  9197. /**
  9198. * Time ticks.
  9199. *
  9200. * @interface Highcharts.AxisTickPositionsArray
  9201. * @extends global.Array<number>
  9202. */ /**
  9203. * @name Highcharts.AxisTickPositionsArray#info
  9204. * @type {Highcharts.TimeTicksInfoObject|undefined}
  9205. */
  9206. /**
  9207. * A callback to return the time zone offset for a given datetime. It
  9208. * takes the timestamp in terms of milliseconds since January 1 1970,
  9209. * and returns the timezone offset in minutes. This provides a hook
  9210. * for drawing time based charts in specific time zones using their
  9211. * local DST crossover dates, with the help of external libraries.
  9212. *
  9213. * @callback Highcharts.TimezoneOffsetCallbackFunction
  9214. *
  9215. * @param {number} timestamp
  9216. * Timestamp in terms of milliseconds since January 1 1970.
  9217. *
  9218. * @return {number}
  9219. * Timezone offset in minutes.
  9220. */
  9221. var defined = U.defined, error = U.error, extend = U.extend, isObject = U.isObject, merge = U.merge, objectEach = U.objectEach, pad = U.pad, pick = U.pick, splat = U.splat, timeUnits = U.timeUnits;
  9222. var H = Highcharts, win = H.win;
  9223. /* eslint-disable no-invalid-this, valid-jsdoc */
  9224. /**
  9225. * The Time class. Time settings are applied in general for each page using
  9226. * `Highcharts.setOptions`, or individually for each Chart item through the
  9227. * [time](https://api.highcharts.com/highcharts/time) options set.
  9228. *
  9229. * The Time object is available from {@link Highcharts.Chart#time},
  9230. * which refers to `Highcharts.time` if no individual time settings are
  9231. * applied.
  9232. *
  9233. * @example
  9234. * // Apply time settings globally
  9235. * Highcharts.setOptions({
  9236. * time: {
  9237. * timezone: 'Europe/London'
  9238. * }
  9239. * });
  9240. *
  9241. * // Apply time settings by instance
  9242. * var chart = Highcharts.chart('container', {
  9243. * time: {
  9244. * timezone: 'America/New_York'
  9245. * },
  9246. * series: [{
  9247. * data: [1, 4, 3, 5]
  9248. * }]
  9249. * });
  9250. *
  9251. * // Use the Time object
  9252. * console.log(
  9253. * 'Current time in New York',
  9254. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  9255. * );
  9256. *
  9257. * @since 6.0.5
  9258. *
  9259. * @class
  9260. * @name Highcharts.Time
  9261. *
  9262. * @param {Highcharts.TimeOptions} options
  9263. * Time options as defined in [chart.options.time](/highcharts/time).
  9264. */
  9265. var Time = /** @class */ (function () {
  9266. /* *
  9267. *
  9268. * Constructors
  9269. *
  9270. * */
  9271. function Time(options) {
  9272. /* *
  9273. *
  9274. * Properties
  9275. *
  9276. * */
  9277. this.options = {};
  9278. this.useUTC = false;
  9279. this.variableTimezone = false;
  9280. this.Date = win.Date;
  9281. /**
  9282. * Get the time zone offset based on the current timezone information as
  9283. * set in the global options.
  9284. *
  9285. * @function Highcharts.Time#getTimezoneOffset
  9286. *
  9287. * @param {number} timestamp
  9288. * The JavaScript timestamp to inspect.
  9289. *
  9290. * @return {number}
  9291. * The timezone offset in minutes compared to UTC.
  9292. */
  9293. this.getTimezoneOffset = this.timezoneOffsetFunction();
  9294. this.update(options);
  9295. }
  9296. /* *
  9297. *
  9298. * Functions
  9299. *
  9300. * */
  9301. /**
  9302. * Time units used in `Time.get` and `Time.set`
  9303. *
  9304. * @typedef {"Date"|"Day"|"FullYear"|"Hours"|"Milliseconds"|"Minutes"|"Month"|"Seconds"} Highcharts.TimeUnitValue
  9305. */
  9306. /**
  9307. * Get the value of a date object in given units, and subject to the Time
  9308. * object's current timezone settings. This function corresponds directly to
  9309. * JavaScripts `Date.getXXX / Date.getUTCXXX`, so instead of calling
  9310. * `date.getHours()` or `date.getUTCHours()` we will call
  9311. * `time.get('Hours')`.
  9312. *
  9313. * @function Highcharts.Time#get
  9314. *
  9315. * @param {Highcharts.TimeUnitValue} unit
  9316. * @param {Date} date
  9317. *
  9318. * @return {number}
  9319. * The given time unit
  9320. */
  9321. Time.prototype.get = function (unit, date) {
  9322. if (this.variableTimezone || this.timezoneOffset) {
  9323. var realMs = date.getTime();
  9324. var ms = realMs - this.getTimezoneOffset(date);
  9325. date.setTime(ms); // Temporary adjust to timezone
  9326. var ret = date['getUTC' + unit]();
  9327. date.setTime(realMs); // Reset
  9328. return ret;
  9329. }
  9330. // UTC time with no timezone handling
  9331. if (this.useUTC) {
  9332. return date['getUTC' + unit]();
  9333. }
  9334. // Else, local time
  9335. return date['get' + unit]();
  9336. };
  9337. /**
  9338. * Set the value of a date object in given units, and subject to the Time
  9339. * object's current timezone settings. This function corresponds directly to
  9340. * JavaScripts `Date.setXXX / Date.setUTCXXX`, so instead of calling
  9341. * `date.setHours(0)` or `date.setUTCHours(0)` we will call
  9342. * `time.set('Hours', 0)`.
  9343. *
  9344. * @function Highcharts.Time#set
  9345. *
  9346. * @param {Highcharts.TimeUnitValue} unit
  9347. * @param {Date} date
  9348. * @param {number} value
  9349. *
  9350. * @return {number}
  9351. * The epoch milliseconds of the updated date
  9352. */
  9353. Time.prototype.set = function (unit, date, value) {
  9354. // UTC time with timezone handling
  9355. if (this.variableTimezone || this.timezoneOffset) {
  9356. // For lower order time units, just set it directly using UTC
  9357. // time
  9358. if (unit === 'Milliseconds' ||
  9359. unit === 'Seconds' ||
  9360. unit === 'Minutes') {
  9361. return date['setUTC' + unit](value);
  9362. }
  9363. // Higher order time units need to take the time zone into
  9364. // account
  9365. // Adjust by timezone
  9366. var offset = this.getTimezoneOffset(date);
  9367. var ms = date.getTime() - offset;
  9368. date.setTime(ms);
  9369. date['setUTC' + unit](value);
  9370. var newOffset = this.getTimezoneOffset(date);
  9371. ms = date.getTime() + newOffset;
  9372. return date.setTime(ms);
  9373. }
  9374. // UTC time with no timezone handling
  9375. if (this.useUTC) {
  9376. return date['setUTC' + unit](value);
  9377. }
  9378. // Else, local time
  9379. return date['set' + unit](value);
  9380. };
  9381. /**
  9382. * Update the Time object with current options. It is called internally on
  9383. * initializing Highcharts, after running `Highcharts.setOptions` and on
  9384. * `Chart.update`.
  9385. *
  9386. * @private
  9387. * @function Highcharts.Time#update
  9388. *
  9389. * @param {Highcharts.TimeOptions} options
  9390. *
  9391. * @return {void}
  9392. */
  9393. Time.prototype.update = function (options) {
  9394. var useUTC = pick(options && options.useUTC, true), time = this;
  9395. this.options = options = merge(true, this.options || {}, options);
  9396. // Allow using a different Date class
  9397. this.Date = options.Date || win.Date || Date;
  9398. this.useUTC = useUTC;
  9399. this.timezoneOffset = (useUTC && options.timezoneOffset);
  9400. this.getTimezoneOffset = this.timezoneOffsetFunction();
  9401. /*
  9402. * The time object has options allowing for variable time zones, meaning
  9403. * the axis ticks or series data needs to consider this.
  9404. */
  9405. this.variableTimezone = !!(!useUTC ||
  9406. options.getTimezoneOffset ||
  9407. options.timezone);
  9408. };
  9409. /**
  9410. * Make a time and returns milliseconds. Interprets the inputs as UTC time,
  9411. * local time or a specific timezone time depending on the current time
  9412. * settings.
  9413. *
  9414. * @function Highcharts.Time#makeTime
  9415. *
  9416. * @param {number} year
  9417. * The year
  9418. *
  9419. * @param {number} month
  9420. * The month. Zero-based, so January is 0.
  9421. *
  9422. * @param {number} [date=1]
  9423. * The day of the month
  9424. *
  9425. * @param {number} [hours=0]
  9426. * The hour of the day, 0-23.
  9427. *
  9428. * @param {number} [minutes=0]
  9429. * The minutes
  9430. *
  9431. * @param {number} [seconds=0]
  9432. * The seconds
  9433. *
  9434. * @return {number}
  9435. * The time in milliseconds since January 1st 1970.
  9436. */
  9437. Time.prototype.makeTime = function (year, month, date, hours, minutes, seconds) {
  9438. var d, offset, newOffset;
  9439. if (this.useUTC) {
  9440. d = this.Date.UTC.apply(0, arguments);
  9441. offset = this.getTimezoneOffset(d);
  9442. d += offset;
  9443. newOffset = this.getTimezoneOffset(d);
  9444. if (offset !== newOffset) {
  9445. d += newOffset - offset;
  9446. // A special case for transitioning from summer time to winter time.
  9447. // When the clock is set back, the same time is repeated twice, i.e.
  9448. // 02:30 am is repeated since the clock is set back from 3 am to
  9449. // 2 am. We need to make the same time as local Date does.
  9450. }
  9451. else if (offset - 36e5 === this.getTimezoneOffset(d - 36e5) &&
  9452. !H.isSafari) {
  9453. d -= 36e5;
  9454. }
  9455. }
  9456. else {
  9457. d = new this.Date(year, month, pick(date, 1), pick(hours, 0), pick(minutes, 0), pick(seconds, 0)).getTime();
  9458. }
  9459. return d;
  9460. };
  9461. /**
  9462. * Sets the getTimezoneOffset function. If the `timezone` option is set, a
  9463. * default getTimezoneOffset function with that timezone is returned. If
  9464. * a `getTimezoneOffset` option is defined, it is returned. If neither are
  9465. * specified, the function using the `timezoneOffset` option or 0 offset is
  9466. * returned.
  9467. *
  9468. * @private
  9469. * @function Highcharts.Time#timezoneOffsetFunction
  9470. *
  9471. * @return {Function}
  9472. * A getTimezoneOffset function
  9473. */
  9474. Time.prototype.timezoneOffsetFunction = function () {
  9475. var time = this, options = this.options, moment = win.moment;
  9476. if (!this.useUTC) {
  9477. return function (timestamp) {
  9478. return new Date(timestamp.toString()).getTimezoneOffset() * 60000;
  9479. };
  9480. }
  9481. if (options.timezone) {
  9482. if (!moment) {
  9483. // getTimezoneOffset-function stays undefined because it depends
  9484. // on Moment.js
  9485. error(25);
  9486. }
  9487. else {
  9488. return function (timestamp) {
  9489. return -moment.tz(timestamp, options.timezone).utcOffset() * 60000;
  9490. };
  9491. }
  9492. }
  9493. // If not timezone is set, look for the getTimezoneOffset callback
  9494. if (this.useUTC && options.getTimezoneOffset) {
  9495. return function (timestamp) {
  9496. return options.getTimezoneOffset(timestamp.valueOf()) * 60000;
  9497. };
  9498. }
  9499. // Last, use the `timezoneOffset` option if set
  9500. return function () {
  9501. return (time.timezoneOffset || 0) * 60000;
  9502. };
  9503. };
  9504. /**
  9505. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970)
  9506. * into a human readable date string. The available format keys are listed
  9507. * below. Additional formats can be given in the
  9508. * {@link Highcharts.dateFormats} hook.
  9509. *
  9510. * Supported format keys:
  9511. * - `%a`: Short weekday, like 'Mon'
  9512. * - `%A`: Long weekday, like 'Monday'
  9513. * - `%d`: Two digit day of the month, 01 to 31
  9514. * - `%e`: Day of the month, 1 through 31
  9515. * - `%w`: Day of the week, 0 through 6
  9516. * - `%b`: Short month, like 'Jan'
  9517. * - `%B`: Long month, like 'January'
  9518. * - `%m`: Two digit month number, 01 through 12
  9519. * - `%y`: Two digits year, like 09 for 2009
  9520. * - `%Y`: Four digits year, like 2009
  9521. * - `%H`: Two digits hours in 24h format, 00 through 23
  9522. * - `%k`: Hours in 24h format, 0 through 23
  9523. * - `%I`: Two digits hours in 12h format, 00 through 11
  9524. * - `%l`: Hours in 12h format, 1 through 12
  9525. * - `%M`: Two digits minutes, 00 through 59
  9526. * - `%p`: Upper case AM or PM
  9527. * - `%P`: Lower case AM or PM
  9528. * - `%S`: Two digits seconds, 00 through 59
  9529. * - `%L`: Milliseconds (naming from Ruby)
  9530. *
  9531. * @example
  9532. * const time = new Highcharts.Time();
  9533. * const s = time.dateFormat('%Y-%m-%d %H:%M:%S', Date.UTC(2020, 0, 1));
  9534. * console.log(s); // => 2020-01-01 00:00:00
  9535. *
  9536. * @function Highcharts.Time#dateFormat
  9537. *
  9538. * @param {string} format
  9539. * The desired format where various time representations are
  9540. * prefixed with %.
  9541. *
  9542. * @param {number} timestamp
  9543. * The JavaScript timestamp.
  9544. *
  9545. * @param {boolean} [capitalize=false]
  9546. * Upper case first letter in the return.
  9547. *
  9548. * @return {string}
  9549. * The formatted date.
  9550. */
  9551. Time.prototype.dateFormat = function (format, timestamp, capitalize) {
  9552. var _a;
  9553. if (!defined(timestamp) || isNaN(timestamp)) {
  9554. return ((_a = H.defaultOptions.lang) === null || _a === void 0 ? void 0 : _a.invalidDate) || '';
  9555. }
  9556. format = pick(format, '%Y-%m-%d %H:%M:%S');
  9557. var time = this, date = new this.Date(timestamp),
  9558. // get the basic time values
  9559. 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 === null || lang === void 0 ? void 0 : lang.weekdays, shortWeekdays = lang === null || lang === void 0 ? void 0 : lang.shortWeekdays,
  9560. // List all format keys. Custom formats can be added from the
  9561. // outside.
  9562. replacements = extend({
  9563. // Day
  9564. // Short weekday, like 'Mon'
  9565. a: shortWeekdays ?
  9566. shortWeekdays[day] :
  9567. langWeekdays[day].substr(0, 3),
  9568. // Long weekday, like 'Monday'
  9569. A: langWeekdays[day],
  9570. // Two digit day of the month, 01 to 31
  9571. d: pad(dayOfMonth),
  9572. // Day of the month, 1 through 31
  9573. e: pad(dayOfMonth, 2, ' '),
  9574. // Day of the week, 0 through 6
  9575. w: day,
  9576. // Week (none implemented)
  9577. // 'W': weekNumber(),
  9578. // Month
  9579. // Short month, like 'Jan'
  9580. b: lang.shortMonths[month],
  9581. // Long month, like 'January'
  9582. B: lang.months[month],
  9583. // Two digit month number, 01 through 12
  9584. m: pad(month + 1),
  9585. // Month number, 1 through 12 (#8150)
  9586. o: month + 1,
  9587. // Year
  9588. // Two digits year, like 09 for 2009
  9589. y: fullYear.toString().substr(2, 2),
  9590. // Four digits year, like 2009
  9591. Y: fullYear,
  9592. // Time
  9593. // Two digits hours in 24h format, 00 through 23
  9594. H: pad(hours),
  9595. // Hours in 24h format, 0 through 23
  9596. k: hours,
  9597. // Two digits hours in 12h format, 00 through 11
  9598. I: pad((hours % 12) || 12),
  9599. // Hours in 12h format, 1 through 12
  9600. l: (hours % 12) || 12,
  9601. // Two digits minutes, 00 through 59
  9602. M: pad(this.get('Minutes', date)),
  9603. // Upper case AM or PM
  9604. p: hours < 12 ? 'AM' : 'PM',
  9605. // Lower case AM or PM
  9606. P: hours < 12 ? 'am' : 'pm',
  9607. // Two digits seconds, 00 through 59
  9608. S: pad(date.getSeconds()),
  9609. // Milliseconds (naming from Ruby)
  9610. L: pad(Math.floor(timestamp % 1000), 3)
  9611. }, H.dateFormats);
  9612. // Do the replaces
  9613. objectEach(replacements, function (val, key) {
  9614. // Regex would do it in one line, but this is faster
  9615. while (format.indexOf('%' + key) !== -1) {
  9616. format = format.replace('%' + key, typeof val === 'function' ? val.call(time, timestamp) : val);
  9617. }
  9618. });
  9619. // Optionally capitalize the string and return
  9620. return capitalize ?
  9621. (format.substr(0, 1).toUpperCase() +
  9622. format.substr(1)) :
  9623. format;
  9624. };
  9625. /**
  9626. * Resolve legacy formats of dateTimeLabelFormats (strings and arrays) into
  9627. * an object.
  9628. * @private
  9629. * @param {string|Array<T>|Highcharts.Dictionary<T>} f - General format description
  9630. * @return {Highcharts.Dictionary<T>} - The object definition
  9631. */
  9632. Time.prototype.resolveDTLFormat = function (f) {
  9633. if (!isObject(f, true)) { // check for string or array
  9634. f = splat(f);
  9635. return {
  9636. main: f[0],
  9637. from: f[1],
  9638. to: f[2]
  9639. };
  9640. }
  9641. return f;
  9642. };
  9643. /**
  9644. * Return an array with time positions distributed on round time values
  9645. * right and right after min and max. Used in datetime axes as well as for
  9646. * grouping data on a datetime axis.
  9647. *
  9648. * @function Highcharts.Time#getTimeTicks
  9649. *
  9650. * @param {Highcharts.TimeNormalizedObject} normalizedInterval
  9651. * The interval in axis values (ms) and the count
  9652. *
  9653. * @param {number} [min]
  9654. * The minimum in axis values
  9655. *
  9656. * @param {number} [max]
  9657. * The maximum in axis values
  9658. *
  9659. * @param {number} [startOfWeek=1]
  9660. *
  9661. * @return {Highcharts.AxisTickPositionsArray}
  9662. */
  9663. Time.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek) {
  9664. var time = this, Date = time.Date, tickPositions = [], i, higherRanks = {}, minYear, // used in months and years as a basis for Date.UTC()
  9665. // When crossing DST, use the max. Resolves #6278.
  9666. minDate = new Date(min), interval = normalizedInterval.unitRange, count = normalizedInterval.count || 1, variableDayLength, minDay;
  9667. startOfWeek = pick(startOfWeek, 1);
  9668. if (defined(min)) { // #1300
  9669. time.set('Milliseconds', minDate, interval >= timeUnits.second ?
  9670. 0 : // #3935
  9671. count * Math.floor(time.get('Milliseconds', minDate) / count)); // #3652, #3654
  9672. if (interval >= timeUnits.second) { // second
  9673. time.set('Seconds', minDate, interval >= timeUnits.minute ?
  9674. 0 : // #3935
  9675. count * Math.floor(time.get('Seconds', minDate) / count));
  9676. }
  9677. if (interval >= timeUnits.minute) { // minute
  9678. time.set('Minutes', minDate, interval >= timeUnits.hour ?
  9679. 0 :
  9680. count * Math.floor(time.get('Minutes', minDate) / count));
  9681. }
  9682. if (interval >= timeUnits.hour) { // hour
  9683. time.set('Hours', minDate, interval >= timeUnits.day ?
  9684. 0 :
  9685. count * Math.floor(time.get('Hours', minDate) / count));
  9686. }
  9687. if (interval >= timeUnits.day) { // day
  9688. time.set('Date', minDate, interval >= timeUnits.month ?
  9689. 1 :
  9690. Math.max(1, count * Math.floor(time.get('Date', minDate) / count)));
  9691. }
  9692. if (interval >= timeUnits.month) { // month
  9693. time.set('Month', minDate, interval >= timeUnits.year ? 0 :
  9694. count * Math.floor(time.get('Month', minDate) / count));
  9695. minYear = time.get('FullYear', minDate);
  9696. }
  9697. if (interval >= timeUnits.year) { // year
  9698. minYear -= minYear % count;
  9699. time.set('FullYear', minDate, minYear);
  9700. }
  9701. // week is a special case that runs outside the hierarchy
  9702. if (interval === timeUnits.week) {
  9703. // get start of current week, independent of count
  9704. minDay = time.get('Day', minDate);
  9705. time.set('Date', minDate, (time.get('Date', minDate) -
  9706. minDay + startOfWeek +
  9707. // We don't want to skip days that are before
  9708. // startOfWeek (#7051)
  9709. (minDay < startOfWeek ? -7 : 0)));
  9710. }
  9711. // Get basics for variable time spans
  9712. minYear = time.get('FullYear', minDate);
  9713. var minMonth = time.get('Month', minDate), minDateDate = time.get('Date', minDate), minHours = time.get('Hours', minDate);
  9714. // Redefine min to the floored/rounded minimum time (#7432)
  9715. min = minDate.getTime();
  9716. // Handle local timezone offset
  9717. if (time.variableTimezone) {
  9718. // Detect whether we need to take the DST crossover into
  9719. // consideration. If we're crossing over DST, the day length may
  9720. // be 23h or 25h and we need to compute the exact clock time for
  9721. // each tick instead of just adding hours. This comes at a cost,
  9722. // so first we find out if it is needed (#4951).
  9723. variableDayLength = (
  9724. // Long range, assume we're crossing over.
  9725. max - min > 4 * timeUnits.month ||
  9726. // Short range, check if min and max are in different time
  9727. // zones.
  9728. time.getTimezoneOffset(min) !==
  9729. time.getTimezoneOffset(max));
  9730. }
  9731. // Iterate and add tick positions at appropriate values
  9732. var t = minDate.getTime();
  9733. i = 1;
  9734. while (t < max) {
  9735. tickPositions.push(t);
  9736. // if the interval is years, use Date.UTC to increase years
  9737. if (interval === timeUnits.year) {
  9738. t = time.makeTime(minYear + i * count, 0);
  9739. // if the interval is months, use Date.UTC to increase months
  9740. }
  9741. else if (interval === timeUnits.month) {
  9742. t = time.makeTime(minYear, minMonth + i * count);
  9743. // if we're using global time, the interval is not fixed as it
  9744. // jumps one hour at the DST crossover
  9745. }
  9746. else if (variableDayLength &&
  9747. (interval === timeUnits.day || interval === timeUnits.week)) {
  9748. t = time.makeTime(minYear, minMonth, minDateDate +
  9749. i * count * (interval === timeUnits.day ? 1 : 7));
  9750. }
  9751. else if (variableDayLength &&
  9752. interval === timeUnits.hour &&
  9753. count > 1) {
  9754. // make sure higher ranks are preserved across DST (#6797,
  9755. // #7621)
  9756. t = time.makeTime(minYear, minMonth, minDateDate, minHours + i * count);
  9757. // else, the interval is fixed and we use simple addition
  9758. }
  9759. else {
  9760. t += interval * count;
  9761. }
  9762. i++;
  9763. }
  9764. // push the last time
  9765. tickPositions.push(t);
  9766. // Handle higher ranks. Mark new days if the time is on midnight
  9767. // (#950, #1649, #1760, #3349). Use a reasonable dropout threshold
  9768. // to prevent looping over dense data grouping (#6156).
  9769. if (interval <= timeUnits.hour && tickPositions.length < 10000) {
  9770. tickPositions.forEach(function (t) {
  9771. if (
  9772. // Speed optimization, no need to run dateFormat unless
  9773. // we're on a full or half hour
  9774. t % 1800000 === 0 &&
  9775. // Check for local or global midnight
  9776. time.dateFormat('%H%M%S%L', t) === '000000000') {
  9777. higherRanks[t] = 'day';
  9778. }
  9779. });
  9780. }
  9781. }
  9782. // record information on the chosen unit - for dynamic label formatter
  9783. tickPositions.info = extend(normalizedInterval, {
  9784. higherRanks: higherRanks,
  9785. totalRange: interval * count
  9786. });
  9787. return tickPositions;
  9788. };
  9789. return Time;
  9790. }());
  9791. H.Time = Time;
  9792. return H.Time;
  9793. });
  9794. _registerModule(_modules, 'parts/Options.js', [_modules['parts/Globals.js'], _modules['parts/Time.js'], _modules['parts/Color.js'], _modules['parts/Utilities.js']], function (H, Time, Color, U) {
  9795. /* *
  9796. *
  9797. * (c) 2010-2020 Torstein Honsi
  9798. *
  9799. * License: www.highcharts.com/license
  9800. *
  9801. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  9802. *
  9803. * */
  9804. /**
  9805. * @typedef {"plotBox"|"spacingBox"} Highcharts.ButtonRelativeToValue
  9806. */
  9807. /**
  9808. * Gets fired when a series is added to the chart after load time, using the
  9809. * `addSeries` method. Returning `false` prevents the series from being added.
  9810. *
  9811. * @callback Highcharts.ChartAddSeriesCallbackFunction
  9812. *
  9813. * @param {Highcharts.Chart} this
  9814. * The chart on which the event occured.
  9815. *
  9816. * @param {Highcharts.ChartAddSeriesEventObject} event
  9817. * The event that occured.
  9818. */
  9819. /**
  9820. * Contains common event information. Through the `options` property you can
  9821. * access the series options that were passed to the `addSeries` method.
  9822. *
  9823. * @interface Highcharts.ChartAddSeriesEventObject
  9824. */ /**
  9825. * The series options that were passed to the `addSeries` method.
  9826. * @name Highcharts.ChartAddSeriesEventObject#options
  9827. * @type {Highcharts.SeriesOptionsType}
  9828. */ /**
  9829. * Prevents the default behaviour of the event.
  9830. * @name Highcharts.ChartAddSeriesEventObject#preventDefault
  9831. * @type {Function}
  9832. */ /**
  9833. * The event target.
  9834. * @name Highcharts.ChartAddSeriesEventObject#target
  9835. * @type {Highcharts.Chart}
  9836. */ /**
  9837. * The event type.
  9838. * @name Highcharts.ChartAddSeriesEventObject#type
  9839. * @type {"addSeries"}
  9840. */
  9841. /**
  9842. * Gets fired when clicking on the plot background.
  9843. *
  9844. * @callback Highcharts.ChartClickCallbackFunction
  9845. *
  9846. * @param {Highcharts.Chart} this
  9847. * The chart on which the event occured.
  9848. *
  9849. * @param {Highcharts.PointerEventObject} event
  9850. * The event that occured.
  9851. */
  9852. /**
  9853. * Contains an axes of the clicked spot.
  9854. *
  9855. * @interface Highcharts.ChartClickEventAxisObject
  9856. */ /**
  9857. * Axis at the clicked spot.
  9858. * @name Highcharts.ChartClickEventAxisObject#axis
  9859. * @type {Highcharts.Axis}
  9860. */ /**
  9861. * Axis value at the clicked spot.
  9862. * @name Highcharts.ChartClickEventAxisObject#value
  9863. * @type {number}
  9864. */
  9865. /**
  9866. * Contains information about the clicked spot on the chart. Remember the unit
  9867. * of a datetime axis is milliseconds since 1970-01-01 00:00:00.
  9868. *
  9869. * @interface Highcharts.ChartClickEventObject
  9870. * @extends Highcharts.PointerEventObject
  9871. */ /**
  9872. * Information about the x-axis on the clicked spot.
  9873. * @name Highcharts.ChartClickEventObject#xAxis
  9874. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  9875. */ /**
  9876. * Information about the y-axis on the clicked spot.
  9877. * @name Highcharts.ChartClickEventObject#yAxis
  9878. * @type {Array<Highcharts.ChartClickEventAxisObject>}
  9879. */ /**
  9880. * Information about the z-axis on the clicked spot.
  9881. * @name Highcharts.ChartClickEventObject#zAxis
  9882. * @type {Array<Highcharts.ChartClickEventAxisObject>|undefined}
  9883. */
  9884. /**
  9885. * Gets fired when the chart is finished loading.
  9886. *
  9887. * @callback Highcharts.ChartLoadCallbackFunction
  9888. *
  9889. * @param {Highcharts.Chart} this
  9890. * The chart on which the event occured.
  9891. *
  9892. * @param {global.Event} event
  9893. * The event that occured.
  9894. */
  9895. /**
  9896. * Fires when the chart is redrawn, either after a call to `chart.redraw()` or
  9897. * after an axis, series or point is modified with the `redraw` option set to
  9898. * `true`.
  9899. *
  9900. * @callback Highcharts.ChartRedrawCallbackFunction
  9901. *
  9902. * @param {Highcharts.Chart} this
  9903. * The chart on which the event occured.
  9904. *
  9905. * @param {global.Event} event
  9906. * The event that occured.
  9907. */
  9908. /**
  9909. * Gets fired after initial load of the chart (directly after the `load` event),
  9910. * and after each redraw (directly after the `redraw` event).
  9911. *
  9912. * @callback Highcharts.ChartRenderCallbackFunction
  9913. *
  9914. * @param {Highcharts.Chart} this
  9915. * The chart on which the event occured.
  9916. *
  9917. * @param {global.Event} event
  9918. * The event that occured.
  9919. */
  9920. /**
  9921. * Gets fired when an area of the chart has been selected. The default action
  9922. * for the selection event is to zoom the chart to the selected area. It can be
  9923. * prevented by calling `event.preventDefault()` or return false.
  9924. *
  9925. * @callback Highcharts.ChartSelectionCallbackFunction
  9926. *
  9927. * @param {Highcharts.Chart} this
  9928. * The chart on which the event occured.
  9929. *
  9930. * @param {global.ChartSelectionContextObject} event
  9931. * Event informations
  9932. *
  9933. * @return {boolean|undefined}
  9934. * Return false to prevent the default action, usually zoom.
  9935. */
  9936. /**
  9937. * The primary axes are `xAxis[0]` and `yAxis[0]`. Remember the unit of a
  9938. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  9939. *
  9940. * @interface Highcharts.ChartSelectionContextObject
  9941. * @extends global.Event
  9942. */ /**
  9943. * Arrays containing the axes of each dimension and each axis' min and max
  9944. * values.
  9945. * @name Highcharts.ChartSelectionContextObject#xAxis
  9946. * @type {Array<Highcharts.ChartSelectionAxisContextObject>}
  9947. */ /**
  9948. * Arrays containing the axes of each dimension and each axis' min and max
  9949. * values.
  9950. * @name Highcharts.ChartSelectionContextObject#yAxis
  9951. * @type {Array<Highcharts.ChartSelectionAxisContextObject>}
  9952. */
  9953. /**
  9954. * Axis context of the selection.
  9955. *
  9956. * @interface Highcharts.ChartSelectionAxisContextObject
  9957. */ /**
  9958. * The selected Axis.
  9959. * @name Highcharts.ChartSelectionAxisContextObject#axis
  9960. * @type {Highcharts.Axis}
  9961. */ /**
  9962. * The maximum axis value, either automatic or set manually.
  9963. * @name Highcharts.ChartSelectionAxisContextObject#max
  9964. * @type {number}
  9965. */ /**
  9966. * The minimum axis value, either automatic or set manually.
  9967. * @name Highcharts.ChartSelectionAxisContextObject#min
  9968. * @type {number}
  9969. */
  9970. var color = Color.parse;
  9971. var merge = U.merge;
  9972. var isTouchDevice = H.isTouchDevice, svg = H.svg;
  9973. /* ************************************************************************** *
  9974. * Handle the options *
  9975. * ************************************************************************** */
  9976. /**
  9977. * Global default settings.
  9978. *
  9979. * @name Highcharts.defaultOptions
  9980. * @type {Highcharts.Options}
  9981. */ /**
  9982. * @optionparent
  9983. */
  9984. H.defaultOptions = {
  9985. /**
  9986. * An array containing the default colors for the chart's series. When
  9987. * all colors are used, new colors are pulled from the start again.
  9988. *
  9989. * Default colors can also be set on a series or series.type basis,
  9990. * see [column.colors](#plotOptions.column.colors),
  9991. * [pie.colors](#plotOptions.pie.colors).
  9992. *
  9993. * In styled mode, the colors option doesn't exist. Instead, colors
  9994. * are defined in CSS and applied either through series or point class
  9995. * names, or through the [chart.colorCount](#chart.colorCount) option.
  9996. *
  9997. *
  9998. * ### Legacy
  9999. *
  10000. * In Highcharts 3.x, the default colors were:
  10001. * ```js
  10002. * colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce',
  10003. * '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']
  10004. * ```
  10005. *
  10006. * In Highcharts 2.x, the default colors were:
  10007. * ```js
  10008. * colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE',
  10009. * '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92']
  10010. * ```
  10011. *
  10012. * @sample {highcharts} highcharts/chart/colors/
  10013. * Assign a global color theme
  10014. *
  10015. * @type {Array<Highcharts.ColorString>}
  10016. * @default ["#7cb5ec", "#434348", "#90ed7d", "#f7a35c", "#8085e9",
  10017. * "#f15c80", "#e4d354", "#2b908f", "#f45b5b", "#91e8e1"]
  10018. */
  10019. colors: '#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1'.split(' '),
  10020. /**
  10021. * Styled mode only. Configuration object for adding SVG definitions for
  10022. * reusable elements. See [gradients, shadows and
  10023. * patterns](https://www.highcharts.com/docs/chart-design-and-style/gradients-shadows-and-patterns)
  10024. * for more information and code examples.
  10025. *
  10026. * @type {*}
  10027. * @since 5.0.0
  10028. * @apioption defs
  10029. */
  10030. /**
  10031. * @ignore-option
  10032. */
  10033. symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
  10034. /**
  10035. * The language object is global and it can't be set on each chart
  10036. * initialization. Instead, use `Highcharts.setOptions` to set it before any
  10037. * chart is initialized.
  10038. *
  10039. * ```js
  10040. * Highcharts.setOptions({
  10041. * lang: {
  10042. * months: [
  10043. * 'Janvier', 'Février', 'Mars', 'Avril',
  10044. * 'Mai', 'Juin', 'Juillet', 'Août',
  10045. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  10046. * ],
  10047. * weekdays: [
  10048. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  10049. * 'Jeudi', 'Vendredi', 'Samedi'
  10050. * ]
  10051. * }
  10052. * });
  10053. * ```
  10054. */
  10055. lang: {
  10056. /**
  10057. * The loading text that appears when the chart is set into the loading
  10058. * state following a call to `chart.showLoading`.
  10059. */
  10060. loading: 'Loading...',
  10061. /**
  10062. * An array containing the months names. Corresponds to the `%B` format
  10063. * in `Highcharts.dateFormat()`.
  10064. *
  10065. * @type {Array<string>}
  10066. * @default ["January", "February", "March", "April", "May", "June",
  10067. * "July", "August", "September", "October", "November",
  10068. * "December"]
  10069. */
  10070. months: [
  10071. 'January', 'February', 'March', 'April', 'May', 'June', 'July',
  10072. 'August', 'September', 'October', 'November', 'December'
  10073. ],
  10074. /**
  10075. * An array containing the months names in abbreviated form. Corresponds
  10076. * to the `%b` format in `Highcharts.dateFormat()`.
  10077. *
  10078. * @type {Array<string>}
  10079. * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  10080. * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  10081. */
  10082. shortMonths: [
  10083. 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
  10084. 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
  10085. ],
  10086. /**
  10087. * An array containing the weekday names.
  10088. *
  10089. * @type {Array<string>}
  10090. * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  10091. * "Friday", "Saturday"]
  10092. */
  10093. weekdays: [
  10094. 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
  10095. 'Thursday', 'Friday', 'Saturday'
  10096. ],
  10097. /**
  10098. * Short week days, starting Sunday. If not specified, Highcharts uses
  10099. * the first three letters of the `lang.weekdays` option.
  10100. *
  10101. * @sample highcharts/lang/shortweekdays/
  10102. * Finnish two-letter abbreviations
  10103. *
  10104. * @type {Array<string>}
  10105. * @since 4.2.4
  10106. * @apioption lang.shortWeekdays
  10107. */
  10108. /**
  10109. * What to show in a date field for invalid dates. Defaults to an empty
  10110. * string.
  10111. *
  10112. * @type {string}
  10113. * @since 4.1.8
  10114. * @product highcharts highstock
  10115. * @apioption lang.invalidDate
  10116. */
  10117. /**
  10118. * The title appearing on hovering the zoom in button. The text itself
  10119. * defaults to "+" and can be changed in the button options.
  10120. *
  10121. * @type {string}
  10122. * @default Zoom in
  10123. * @product highmaps
  10124. * @apioption lang.zoomIn
  10125. */
  10126. /**
  10127. * The title appearing on hovering the zoom out button. The text itself
  10128. * defaults to "-" and can be changed in the button options.
  10129. *
  10130. * @type {string}
  10131. * @default Zoom out
  10132. * @product highmaps
  10133. * @apioption lang.zoomOut
  10134. */
  10135. /**
  10136. * The default decimal point used in the `Highcharts.numberFormat`
  10137. * method unless otherwise specified in the function arguments.
  10138. *
  10139. * @since 1.2.2
  10140. */
  10141. decimalPoint: '.',
  10142. /**
  10143. * [Metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix) used
  10144. * to shorten high numbers in axis labels. Replacing any of the
  10145. * positions with `null` causes the full number to be written. Setting
  10146. * `numericSymbols` to `null` disables shortening altogether.
  10147. *
  10148. * @sample {highcharts} highcharts/lang/numericsymbols/
  10149. * Replacing the symbols with text
  10150. * @sample {highstock} highcharts/lang/numericsymbols/
  10151. * Replacing the symbols with text
  10152. *
  10153. * @type {Array<string>}
  10154. * @default ["k", "M", "G", "T", "P", "E"]
  10155. * @since 2.3.0
  10156. */
  10157. numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'],
  10158. /**
  10159. * The magnitude of [numericSymbols](#lang.numericSymbol) replacements.
  10160. * Use 10000 for Japanese, Korean and various Chinese locales, which
  10161. * use symbols for 10^4, 10^8 and 10^12.
  10162. *
  10163. * @sample highcharts/lang/numericsymbolmagnitude/
  10164. * 10000 magnitude for Japanese
  10165. *
  10166. * @type {number}
  10167. * @default 1000
  10168. * @since 5.0.3
  10169. * @apioption lang.numericSymbolMagnitude
  10170. */
  10171. /**
  10172. * The text for the label appearing when a chart is zoomed.
  10173. *
  10174. * @since 1.2.4
  10175. */
  10176. resetZoom: 'Reset zoom',
  10177. /**
  10178. * The tooltip title for the label appearing when a chart is zoomed.
  10179. *
  10180. * @since 1.2.4
  10181. */
  10182. resetZoomTitle: 'Reset zoom level 1:1',
  10183. /**
  10184. * The default thousands separator used in the `Highcharts.numberFormat`
  10185. * method unless otherwise specified in the function arguments. Defaults
  10186. * to a single space character, which is recommended in
  10187. * [ISO 31-0](https://en.wikipedia.org/wiki/ISO_31-0#Numbers) and works
  10188. * across Anglo-American and continental European languages.
  10189. *
  10190. * @default \u0020
  10191. * @since 1.2.2
  10192. */
  10193. thousandsSep: ' '
  10194. },
  10195. /**
  10196. * Global options that don't apply to each chart. These options, like
  10197. * the `lang` options, must be set using the `Highcharts.setOptions`
  10198. * method.
  10199. *
  10200. * ```js
  10201. * Highcharts.setOptions({
  10202. * global: {
  10203. * useUTC: false
  10204. * }
  10205. * });
  10206. * ```
  10207. */
  10208. /**
  10209. * _Canvg rendering for Android 2.x is removed as of Highcharts 5.0\.
  10210. * Use the [libURL](#exporting.libURL) option to configure exporting._
  10211. *
  10212. * The URL to the additional file to lazy load for Android 2.x devices.
  10213. * These devices don't support SVG, so we download a helper file that
  10214. * contains [canvg](https://github.com/canvg/canvg), its dependency
  10215. * rbcolor, and our own CanVG Renderer class. To avoid hotlinking to
  10216. * our site, you can install canvas-tools.js on your own server and
  10217. * change this option accordingly.
  10218. *
  10219. * @deprecated
  10220. *
  10221. * @type {string}
  10222. * @default https://code.highcharts.com/{version}/modules/canvas-tools.js
  10223. * @product highcharts highmaps
  10224. * @apioption global.canvasToolsURL
  10225. */
  10226. /**
  10227. * This option is deprecated since v6.0.5. Instead, use
  10228. * [time.useUTC](#time.useUTC) that supports individual time settings
  10229. * per chart.
  10230. *
  10231. * @deprecated
  10232. *
  10233. * @type {boolean}
  10234. * @apioption global.useUTC
  10235. */
  10236. /**
  10237. * This option is deprecated since v6.0.5. Instead, use
  10238. * [time.Date](#time.Date) that supports individual time settings
  10239. * per chart.
  10240. *
  10241. * @deprecated
  10242. *
  10243. * @type {Function}
  10244. * @product highcharts highstock
  10245. * @apioption global.Date
  10246. */
  10247. /**
  10248. * This option is deprecated since v6.0.5. Instead, use
  10249. * [time.getTimezoneOffset](#time.getTimezoneOffset) that supports
  10250. * individual time settings per chart.
  10251. *
  10252. * @deprecated
  10253. *
  10254. * @type {Function}
  10255. * @product highcharts highstock
  10256. * @apioption global.getTimezoneOffset
  10257. */
  10258. /**
  10259. * This option is deprecated since v6.0.5. Instead, use
  10260. * [time.timezone](#time.timezone) that supports individual time
  10261. * settings per chart.
  10262. *
  10263. * @deprecated
  10264. *
  10265. * @type {string}
  10266. * @product highcharts highstock
  10267. * @apioption global.timezone
  10268. */
  10269. /**
  10270. * This option is deprecated since v6.0.5. Instead, use
  10271. * [time.timezoneOffset](#time.timezoneOffset) that supports individual
  10272. * time settings per chart.
  10273. *
  10274. * @deprecated
  10275. *
  10276. * @type {number}
  10277. * @product highcharts highstock
  10278. * @apioption global.timezoneOffset
  10279. */
  10280. global: {},
  10281. /**
  10282. * Time options that can apply globally or to individual charts. These
  10283. * settings affect how `datetime` axes are laid out, how tooltips are
  10284. * formatted, how series
  10285. * [pointIntervalUnit](#plotOptions.series.pointIntervalUnit) works and how
  10286. * the Highstock range selector handles time.
  10287. *
  10288. * The common use case is that all charts in the same Highcharts object
  10289. * share the same time settings, in which case the global settings are set
  10290. * using `setOptions`.
  10291. *
  10292. * ```js
  10293. * // Apply time settings globally
  10294. * Highcharts.setOptions({
  10295. * time: {
  10296. * timezone: 'Europe/London'
  10297. * }
  10298. * });
  10299. * // Apply time settings by instance
  10300. * var chart = Highcharts.chart('container', {
  10301. * time: {
  10302. * timezone: 'America/New_York'
  10303. * },
  10304. * series: [{
  10305. * data: [1, 4, 3, 5]
  10306. * }]
  10307. * });
  10308. *
  10309. * // Use the Time object
  10310. * console.log(
  10311. * 'Current time in New York',
  10312. * chart.time.dateFormat('%Y-%m-%d %H:%M:%S', Date.now())
  10313. * );
  10314. * ```
  10315. *
  10316. * Since v6.0.5, the time options were moved from the `global` obect to the
  10317. * `time` object, and time options can be set on each individual chart.
  10318. *
  10319. * @sample {highcharts|highstock}
  10320. * highcharts/time/timezone/
  10321. * Set the timezone globally
  10322. * @sample {highcharts}
  10323. * highcharts/time/individual/
  10324. * Set the timezone per chart instance
  10325. * @sample {highstock}
  10326. * stock/time/individual/
  10327. * Set the timezone per chart instance
  10328. *
  10329. * @since 6.0.5
  10330. * @optionparent time
  10331. */
  10332. time: {
  10333. /**
  10334. * A custom `Date` class for advanced date handling. For example,
  10335. * [JDate](https://github.com/tahajahangir/jdate) can be hooked in to
  10336. * handle Jalali dates.
  10337. *
  10338. * @type {*}
  10339. * @since 4.0.4
  10340. * @product highcharts highstock gantt
  10341. */
  10342. Date: void 0,
  10343. /**
  10344. * A callback to return the time zone offset for a given datetime. It
  10345. * takes the timestamp in terms of milliseconds since January 1 1970,
  10346. * and returns the timezone offset in minutes. This provides a hook
  10347. * for drawing time based charts in specific time zones using their
  10348. * local DST crossover dates, with the help of external libraries.
  10349. *
  10350. * @see [global.timezoneOffset](#global.timezoneOffset)
  10351. *
  10352. * @sample {highcharts|highstock} highcharts/time/gettimezoneoffset/
  10353. * Use moment.js to draw Oslo time regardless of browser locale
  10354. *
  10355. * @type {Highcharts.TimezoneOffsetCallbackFunction}
  10356. * @since 4.1.0
  10357. * @product highcharts highstock gantt
  10358. */
  10359. getTimezoneOffset: void 0,
  10360. /**
  10361. * Requires [moment.js](https://momentjs.com/). If the timezone option
  10362. * is specified, it creates a default
  10363. * [getTimezoneOffset](#time.getTimezoneOffset) function that looks
  10364. * up the specified timezone in moment.js. If moment.js is not included,
  10365. * this throws a Highcharts error in the console, but does not crash the
  10366. * chart.
  10367. *
  10368. * @see [getTimezoneOffset](#time.getTimezoneOffset)
  10369. *
  10370. * @sample {highcharts|highstock} highcharts/time/timezone/
  10371. * Europe/Oslo
  10372. *
  10373. * @type {string}
  10374. * @since 5.0.7
  10375. * @product highcharts highstock gantt
  10376. */
  10377. timezone: void 0,
  10378. /**
  10379. * The timezone offset in minutes. Positive values are west, negative
  10380. * values are east of UTC, as in the ECMAScript
  10381. * [getTimezoneOffset](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
  10382. * method. Use this to display UTC based data in a predefined time zone.
  10383. *
  10384. * @see [time.getTimezoneOffset](#time.getTimezoneOffset)
  10385. *
  10386. * @sample {highcharts|highstock} highcharts/time/timezoneoffset/
  10387. * Timezone offset
  10388. *
  10389. * @since 3.0.8
  10390. * @product highcharts highstock gantt
  10391. */
  10392. timezoneOffset: 0,
  10393. /**
  10394. * Whether to use UTC time for axis scaling, tickmark placement and
  10395. * time display in `Highcharts.dateFormat`. Advantages of using UTC
  10396. * is that the time displays equally regardless of the user agent's
  10397. * time zone settings. Local time can be used when the data is loaded
  10398. * in real time or when correct Daylight Saving Time transitions are
  10399. * required.
  10400. *
  10401. * @sample {highcharts} highcharts/time/useutc-true/
  10402. * True by default
  10403. * @sample {highcharts} highcharts/time/useutc-false/
  10404. * False
  10405. */
  10406. useUTC: true
  10407. },
  10408. /**
  10409. * General options for the chart.
  10410. */
  10411. chart: {
  10412. /**
  10413. * Default `mapData` for all series. If set to a string, it functions
  10414. * as an index into the `Highcharts.maps` array. Otherwise it is
  10415. * interpreted as map data.
  10416. *
  10417. * @see [mapData](#series.map.mapData)
  10418. *
  10419. * @sample maps/demo/geojson
  10420. * Loading geoJSON data
  10421. * @sample maps/chart/topojson
  10422. * Loading topoJSON converted to geoJSON
  10423. *
  10424. * @type {string|Array<*>|Highcharts.GeoJSON}
  10425. * @since 5.0.0
  10426. * @product highmaps
  10427. * @apioption chart.map
  10428. */
  10429. /**
  10430. * Set lat/lon transformation definitions for the chart. If not defined,
  10431. * these are extracted from the map data.
  10432. *
  10433. * @type {*}
  10434. * @since 5.0.0
  10435. * @product highmaps
  10436. * @apioption chart.mapTransforms
  10437. */
  10438. /**
  10439. * When using multiple axis, the ticks of two or more opposite axes
  10440. * will automatically be aligned by adding ticks to the axis or axes
  10441. * with the least ticks, as if `tickAmount` were specified.
  10442. *
  10443. * This can be prevented by setting `alignTicks` to false. If the grid
  10444. * lines look messy, it's a good idea to hide them for the secondary
  10445. * axis by setting `gridLineWidth` to 0.
  10446. *
  10447. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  10448. * then the `alignTicks ` will be disabled for the Axis.
  10449. *
  10450. * Disabled for logarithmic axes.
  10451. *
  10452. * @sample {highcharts} highcharts/chart/alignticks-true/
  10453. * True by default
  10454. * @sample {highcharts} highcharts/chart/alignticks-false/
  10455. * False
  10456. * @sample {highstock} stock/chart/alignticks-true/
  10457. * True by default
  10458. * @sample {highstock} stock/chart/alignticks-false/
  10459. * False
  10460. *
  10461. * @type {boolean}
  10462. * @default true
  10463. * @product highcharts highstock gantt
  10464. * @apioption chart.alignTicks
  10465. */
  10466. /**
  10467. * Set the overall animation for all chart updating. Animation can be
  10468. * disabled throughout the chart by setting it to false here. It can
  10469. * be overridden for each individual API method as a function parameter.
  10470. * The only animation not affected by this option is the initial series
  10471. * animation, see [plotOptions.series.animation](
  10472. * #plotOptions.series.animation).
  10473. *
  10474. * The animation can either be set as a boolean or a configuration
  10475. * object. If `true`, it will use the 'swing' jQuery easing and a
  10476. * duration of 500 ms. If used as a configuration object, the following
  10477. * properties are supported:
  10478. *
  10479. * - **duration**: The duration of the animation in milliseconds.
  10480. *
  10481. * - **easing**: A string reference to an easing function set on the
  10482. * `Math` object. See
  10483. * [the easing demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-animation-easing/).
  10484. *
  10485. * When zooming on a series with less than 100 points, the chart redraw
  10486. * will be done with animation, but in case of more data points, it is
  10487. * necessary to set this option to ensure animation on zoom.
  10488. *
  10489. * @sample {highcharts} highcharts/chart/animation-none/
  10490. * Updating with no animation
  10491. * @sample {highcharts} highcharts/chart/animation-duration/
  10492. * With a longer duration
  10493. * @sample {highcharts} highcharts/chart/animation-easing/
  10494. * With a jQuery UI easing
  10495. * @sample {highmaps} maps/chart/animation-none/
  10496. * Updating with no animation
  10497. * @sample {highmaps} maps/chart/animation-duration/
  10498. * With a longer duration
  10499. *
  10500. * @type {boolean|Highcharts.AnimationOptionsObject}
  10501. * @default undefined
  10502. * @apioption chart.animation
  10503. */
  10504. /**
  10505. * A CSS class name to apply to the charts container `div`, allowing
  10506. * unique CSS styling for each chart.
  10507. *
  10508. * @type {string}
  10509. * @apioption chart.className
  10510. */
  10511. /**
  10512. * Event listeners for the chart.
  10513. *
  10514. * @apioption chart.events
  10515. */
  10516. /**
  10517. * Fires when a series is added to the chart after load time, using the
  10518. * `addSeries` method. One parameter, `event`, is passed to the
  10519. * function, containing common event information. Through
  10520. * `event.options` you can access the series options that were passed to
  10521. * the `addSeries` method. Returning false prevents the series from
  10522. * being added.
  10523. *
  10524. * @sample {highcharts} highcharts/chart/events-addseries/
  10525. * Alert on add series
  10526. * @sample {highstock} stock/chart/events-addseries/
  10527. * Alert on add series
  10528. *
  10529. * @type {Highcharts.ChartAddSeriesCallbackFunction}
  10530. * @since 1.2.0
  10531. * @context Highcharts.Chart
  10532. * @apioption chart.events.addSeries
  10533. */
  10534. /**
  10535. * Fires when clicking on the plot background. One parameter, `event`,
  10536. * is passed to the function, containing common event information.
  10537. *
  10538. * Information on the clicked spot can be found through `event.xAxis`
  10539. * and `event.yAxis`, which are arrays containing the axes of each
  10540. * dimension and each axis' value at the clicked spot. The primary axes
  10541. * are `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  10542. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  10543. *
  10544. * ```js
  10545. * click: function(e) {
  10546. * console.log(
  10547. * Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', e.xAxis[0].value),
  10548. * e.yAxis[0].value
  10549. * )
  10550. * }
  10551. * ```
  10552. *
  10553. * @sample {highcharts} highcharts/chart/events-click/
  10554. * Alert coordinates on click
  10555. * @sample {highcharts} highcharts/chart/events-container/
  10556. * Alternatively, attach event to container
  10557. * @sample {highstock} stock/chart/events-click/
  10558. * Alert coordinates on click
  10559. * @sample {highstock} highcharts/chart/events-container/
  10560. * Alternatively, attach event to container
  10561. * @sample {highmaps} maps/chart/events-click/
  10562. * Record coordinates on click
  10563. * @sample {highmaps} highcharts/chart/events-container/
  10564. * Alternatively, attach event to container
  10565. *
  10566. * @type {Highcharts.ChartClickCallbackFunction}
  10567. * @since 1.2.0
  10568. * @context Highcharts.Chart
  10569. * @apioption chart.events.click
  10570. */
  10571. /**
  10572. * Fires when the chart is finished loading. Since v4.2.2, it also waits
  10573. * for images to be loaded, for example from point markers. One
  10574. * parameter, `event`, is passed to the function, containing common
  10575. * event information.
  10576. *
  10577. * There is also a second parameter to the chart constructor where a
  10578. * callback function can be passed to be executed on chart.load.
  10579. *
  10580. * @sample {highcharts} highcharts/chart/events-load/
  10581. * Alert on chart load
  10582. * @sample {highstock} stock/chart/events-load/
  10583. * Alert on chart load
  10584. * @sample {highmaps} maps/chart/events-load/
  10585. * Add series on chart load
  10586. *
  10587. * @type {Highcharts.ChartLoadCallbackFunction}
  10588. * @context Highcharts.Chart
  10589. * @apioption chart.events.load
  10590. */
  10591. /**
  10592. * Fires when the chart is redrawn, either after a call to
  10593. * `chart.redraw()` or after an axis, series or point is modified with
  10594. * the `redraw` option set to `true`. One parameter, `event`, is passed
  10595. * to the function, containing common event information.
  10596. *
  10597. * @sample {highcharts} highcharts/chart/events-redraw/
  10598. * Alert on chart redraw
  10599. * @sample {highstock} stock/chart/events-redraw/
  10600. * Alert on chart redraw when adding a series or moving the
  10601. * zoomed range
  10602. * @sample {highmaps} maps/chart/events-redraw/
  10603. * Set subtitle on chart redraw
  10604. *
  10605. * @type {Highcharts.ChartRedrawCallbackFunction}
  10606. * @since 1.2.0
  10607. * @context Highcharts.Chart
  10608. * @apioption chart.events.redraw
  10609. */
  10610. /**
  10611. * Fires after initial load of the chart (directly after the `load`
  10612. * event), and after each redraw (directly after the `redraw` event).
  10613. *
  10614. * @type {Highcharts.ChartRenderCallbackFunction}
  10615. * @since 5.0.7
  10616. * @context Highcharts.Chart
  10617. * @apioption chart.events.render
  10618. */
  10619. /**
  10620. * Fires when an area of the chart has been selected. Selection is
  10621. * enabled by setting the chart's zoomType. One parameter, `event`, is
  10622. * passed to the function, containing common event information. The
  10623. * default action for the selection event is to zoom the chart to the
  10624. * selected area. It can be prevented by calling
  10625. * `event.preventDefault()` or return false.
  10626. *
  10627. * Information on the selected area can be found through `event.xAxis`
  10628. * and `event.yAxis`, which are arrays containing the axes of each
  10629. * dimension and each axis' min and max values. The primary axes are
  10630. * `event.xAxis[0]` and `event.yAxis[0]`. Remember the unit of a
  10631. * datetime axis is milliseconds since 1970-01-01 00:00:00.
  10632. *
  10633. * ```js
  10634. * selection: function(event) {
  10635. * // log the min and max of the primary, datetime x-axis
  10636. * console.log(
  10637. * Highcharts.dateFormat(
  10638. * '%Y-%m-%d %H:%M:%S',
  10639. * event.xAxis[0].min
  10640. * ),
  10641. * Highcharts.dateFormat(
  10642. * '%Y-%m-%d %H:%M:%S',
  10643. * event.xAxis[0].max
  10644. * )
  10645. * );
  10646. * // log the min and max of the y axis
  10647. * console.log(event.yAxis[0].min, event.yAxis[0].max);
  10648. * }
  10649. * ```
  10650. *
  10651. * @sample {highcharts} highcharts/chart/events-selection/
  10652. * Report on selection and reset
  10653. * @sample {highcharts} highcharts/chart/events-selection-points/
  10654. * Select a range of points through a drag selection
  10655. * @sample {highstock} stock/chart/events-selection/
  10656. * Report on selection and reset
  10657. * @sample {highstock} highcharts/chart/events-selection-points/
  10658. * Select a range of points through a drag selection
  10659. * (Highcharts)
  10660. *
  10661. * @type {Highcharts.ChartSelectionCallbackFunction}
  10662. * @apioption chart.events.selection
  10663. */
  10664. /**
  10665. * The margin between the outer edge of the chart and the plot area.
  10666. * The numbers in the array designate top, right, bottom and left
  10667. * respectively. Use the options `marginTop`, `marginRight`,
  10668. * `marginBottom` and `marginLeft` for shorthand setting of one option.
  10669. *
  10670. * By default there is no margin. The actual space is dynamically
  10671. * calculated from the offset of axis labels, axis title, title,
  10672. * subtitle and legend in addition to the `spacingTop`, `spacingRight`,
  10673. * `spacingBottom` and `spacingLeft` options.
  10674. *
  10675. * @sample {highcharts} highcharts/chart/margins-zero/
  10676. * Zero margins
  10677. * @sample {highstock} stock/chart/margin-zero/
  10678. * Zero margins
  10679. *
  10680. * @type {number|Array<number>}
  10681. * @apioption chart.margin
  10682. */
  10683. /**
  10684. * The margin between the bottom outer edge of the chart and the plot
  10685. * area. Use this to set a fixed pixel value for the margin as opposed
  10686. * to the default dynamic margin. See also `spacingBottom`.
  10687. *
  10688. * @sample {highcharts} highcharts/chart/marginbottom/
  10689. * 100px bottom margin
  10690. * @sample {highstock} stock/chart/marginbottom/
  10691. * 100px bottom margin
  10692. * @sample {highmaps} maps/chart/margin/
  10693. * 100px margins
  10694. *
  10695. * @type {number}
  10696. * @since 2.0
  10697. * @apioption chart.marginBottom
  10698. */
  10699. /**
  10700. * The margin between the left outer edge of the chart and the plot
  10701. * area. Use this to set a fixed pixel value for the margin as opposed
  10702. * to the default dynamic margin. See also `spacingLeft`.
  10703. *
  10704. * @sample {highcharts} highcharts/chart/marginleft/
  10705. * 150px left margin
  10706. * @sample {highstock} stock/chart/marginleft/
  10707. * 150px left margin
  10708. * @sample {highmaps} maps/chart/margin/
  10709. * 100px margins
  10710. *
  10711. * @type {number}
  10712. * @since 2.0
  10713. * @apioption chart.marginLeft
  10714. */
  10715. /**
  10716. * The margin between the right outer edge of the chart and the plot
  10717. * area. Use this to set a fixed pixel value for the margin as opposed
  10718. * to the default dynamic margin. See also `spacingRight`.
  10719. *
  10720. * @sample {highcharts} highcharts/chart/marginright/
  10721. * 100px right margin
  10722. * @sample {highstock} stock/chart/marginright/
  10723. * 100px right margin
  10724. * @sample {highmaps} maps/chart/margin/
  10725. * 100px margins
  10726. *
  10727. * @type {number}
  10728. * @since 2.0
  10729. * @apioption chart.marginRight
  10730. */
  10731. /**
  10732. * The margin between the top outer edge of the chart and the plot area.
  10733. * Use this to set a fixed pixel value for the margin as opposed to
  10734. * the default dynamic margin. See also `spacingTop`.
  10735. *
  10736. * @sample {highcharts} highcharts/chart/margintop/ 100px top margin
  10737. * @sample {highstock} stock/chart/margintop/
  10738. * 100px top margin
  10739. * @sample {highmaps} maps/chart/margin/
  10740. * 100px margins
  10741. *
  10742. * @type {number}
  10743. * @since 2.0
  10744. * @apioption chart.marginTop
  10745. */
  10746. /**
  10747. * Callback function to override the default function that formats all
  10748. * the numbers in the chart. Returns a string with the formatted number.
  10749. *
  10750. * @sample highcharts/members/highcharts-numberformat
  10751. * Arabic digits in Highcharts
  10752. * @type {Highcharts.NumberFormatterCallbackFunction}
  10753. * @since 8.0.0
  10754. * @apioption chart.numberFormatter
  10755. */
  10756. /**
  10757. * Allows setting a key to switch between zooming and panning. Can be
  10758. * one of `alt`, `ctrl`, `meta` (the command key on Mac and Windows
  10759. * key on Windows) or `shift`. The keys are mapped directly to the key
  10760. * properties of the click event argument (`event.altKey`,
  10761. * `event.ctrlKey`, `event.metaKey` and `event.shiftKey`).
  10762. *
  10763. * @type {string}
  10764. * @since 4.0.3
  10765. * @product highcharts gantt
  10766. * @validvalue ["alt", "ctrl", "meta", "shift"]
  10767. * @apioption chart.panKey
  10768. */
  10769. /**
  10770. * Allow panning in a chart. Best used with [panKey](#chart.panKey)
  10771. * to combine zooming and panning.
  10772. *
  10773. * On touch devices, when the [tooltip.followTouchMove](
  10774. * #tooltip.followTouchMove) option is `true` (default), panning
  10775. * requires two fingers. To allow panning with one finger, set
  10776. * `followTouchMove` to `false`.
  10777. *
  10778. * @sample {highcharts} highcharts/chart/pankey/ Zooming and panning
  10779. * @sample {highstock} stock/chart/panning/ Zooming and xy panning
  10780. *
  10781. * @product highcharts highstock gantt
  10782. * @apioption chart.panning
  10783. */
  10784. /**
  10785. * Enable or disable chart panning.
  10786. *
  10787. * @type {boolean}
  10788. * @default {highcharts} false
  10789. * @default {highstock} true
  10790. * @apioption chart.panning.enabled
  10791. */
  10792. /**
  10793. * Decides in what dimensions the user can pan the chart. Can be
  10794. * one of `x`, `y`, or `xy`.
  10795. *
  10796. * @sample {highcharts} highcharts/chart/panning-type
  10797. * Zooming and xy panning
  10798. *
  10799. * @type {string}
  10800. * @validvalue ["x", "y", "xy"]
  10801. * @default x
  10802. * @apioption chart.panning.type
  10803. */
  10804. /**
  10805. * Equivalent to [zoomType](#chart.zoomType), but for multitouch
  10806. * gestures only. By default, the `pinchType` is the same as the
  10807. * `zoomType` setting. However, pinching can be enabled separately in
  10808. * some cases, for example in stock charts where a mouse drag pans the
  10809. * chart, while pinching is enabled. When [tooltip.followTouchMove](
  10810. * #tooltip.followTouchMove) is true, pinchType only applies to
  10811. * two-finger touches.
  10812. *
  10813. * @type {string}
  10814. * @default {highcharts} undefined
  10815. * @default {highstock} x
  10816. * @since 3.0
  10817. * @product highcharts highstock gantt
  10818. * @validvalue ["x", "y", "xy"]
  10819. * @apioption chart.pinchType
  10820. */
  10821. /**
  10822. * Whether to apply styled mode. When in styled mode, no presentational
  10823. * attributes or CSS are applied to the chart SVG. Instead, CSS rules
  10824. * are required to style the chart. The default style sheet is
  10825. * available from `https://code.highcharts.com/css/highcharts.css`.
  10826. *
  10827. * @type {boolean}
  10828. * @default false
  10829. * @since 7.0
  10830. * @apioption chart.styledMode
  10831. */
  10832. styledMode: false,
  10833. /**
  10834. * The corner radius of the outer chart border.
  10835. *
  10836. * @sample {highcharts} highcharts/chart/borderradius/
  10837. * 20px radius
  10838. * @sample {highstock} stock/chart/border/
  10839. * 10px radius
  10840. * @sample {highmaps} maps/chart/border/
  10841. * Border options
  10842. *
  10843. */
  10844. borderRadius: 0,
  10845. /**
  10846. * In styled mode, this sets how many colors the class names
  10847. * should rotate between. With ten colors, series (or points) are
  10848. * given class names like `highcharts-color-0`, `highcharts-color-0`
  10849. * [...] `highcharts-color-9`. The equivalent in non-styled mode
  10850. * is to set colors using the [colors](#colors) setting.
  10851. *
  10852. * @since 5.0.0
  10853. */
  10854. colorCount: 10,
  10855. /**
  10856. * Alias of `type`.
  10857. *
  10858. * @sample {highcharts} highcharts/chart/defaultseriestype/
  10859. * Bar
  10860. *
  10861. * @deprecated
  10862. *
  10863. * @product highcharts
  10864. */
  10865. defaultSeriesType: 'line',
  10866. /**
  10867. * If true, the axes will scale to the remaining visible series once
  10868. * one series is hidden. If false, hiding and showing a series will
  10869. * not affect the axes or the other series. For stacks, once one series
  10870. * within the stack is hidden, the rest of the stack will close in
  10871. * around it even if the axis is not affected.
  10872. *
  10873. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true/
  10874. * True by default
  10875. * @sample {highcharts} highcharts/chart/ignorehiddenseries-false/
  10876. * False
  10877. * @sample {highcharts} highcharts/chart/ignorehiddenseries-true-stacked/
  10878. * True with stack
  10879. * @sample {highstock} stock/chart/ignorehiddenseries-true/
  10880. * True by default
  10881. * @sample {highstock} stock/chart/ignorehiddenseries-false/
  10882. * False
  10883. *
  10884. * @since 1.2.0
  10885. * @product highcharts highstock gantt
  10886. */
  10887. ignoreHiddenSeries: true,
  10888. /**
  10889. * Whether to invert the axes so that the x axis is vertical and y axis
  10890. * is horizontal. When `true`, the x axis is [reversed](#xAxis.reversed)
  10891. * by default.
  10892. *
  10893. * @productdesc {highcharts}
  10894. * If a bar series is present in the chart, it will be inverted
  10895. * automatically. Inverting the chart doesn't have an effect if there
  10896. * are no cartesian series in the chart, or if the chart is
  10897. * [polar](#chart.polar).
  10898. *
  10899. * @sample {highcharts} highcharts/chart/inverted/
  10900. * Inverted line
  10901. * @sample {highstock} stock/navigator/inverted/
  10902. * Inverted stock chart
  10903. *
  10904. * @type {boolean}
  10905. * @default false
  10906. * @product highcharts highstock gantt
  10907. * @apioption chart.inverted
  10908. */
  10909. /**
  10910. * The distance between the outer edge of the chart and the content,
  10911. * like title or legend, or axis title and labels if present. The
  10912. * numbers in the array designate top, right, bottom and left
  10913. * respectively. Use the options spacingTop, spacingRight, spacingBottom
  10914. * and spacingLeft options for shorthand setting of one option.
  10915. *
  10916. * @type {Array<number>}
  10917. * @see [chart.margin](#chart.margin)
  10918. * @default [10, 10, 15, 10]
  10919. * @since 3.0.6
  10920. */
  10921. spacing: [10, 10, 15, 10],
  10922. /**
  10923. * The button that appears after a selection zoom, allowing the user
  10924. * to reset zoom.
  10925. */
  10926. resetZoomButton: {
  10927. /**
  10928. * What frame the button placement should be related to. Can be
  10929. * either `plotBox` or `spacingBox`.
  10930. *
  10931. * @sample {highcharts} highcharts/chart/resetzoombutton-relativeto/
  10932. * Relative to the chart
  10933. * @sample {highstock} highcharts/chart/resetzoombutton-relativeto/
  10934. * Relative to the chart
  10935. *
  10936. * @type {Highcharts.ButtonRelativeToValue}
  10937. * @default plot
  10938. * @since 2.2
  10939. * @apioption chart.resetZoomButton.relativeTo
  10940. */
  10941. /**
  10942. * A collection of attributes for the button. The object takes SVG
  10943. * attributes like `fill`, `stroke`, `stroke-width` or `r`, the
  10944. * border radius. The theme also supports `style`, a collection of
  10945. * CSS properties for the text. Equivalent attributes for the hover
  10946. * state are given in `theme.states.hover`.
  10947. *
  10948. * @sample {highcharts} highcharts/chart/resetzoombutton-theme/
  10949. * Theming the button
  10950. * @sample {highstock} highcharts/chart/resetzoombutton-theme/
  10951. * Theming the button
  10952. *
  10953. * @type {Highcharts.SVGAttributes}
  10954. * @since 2.2
  10955. */
  10956. theme: {
  10957. /** @internal */
  10958. zIndex: 6
  10959. },
  10960. /**
  10961. * The position of the button.
  10962. *
  10963. * @sample {highcharts} highcharts/chart/resetzoombutton-position/
  10964. * Above the plot area
  10965. * @sample {highstock} highcharts/chart/resetzoombutton-position/
  10966. * Above the plot area
  10967. * @sample {highmaps} highcharts/chart/resetzoombutton-position/
  10968. * Above the plot area
  10969. *
  10970. * @type {Highcharts.AlignObject}
  10971. * @since 2.2
  10972. */
  10973. position: {
  10974. /**
  10975. * The horizontal alignment of the button.
  10976. */
  10977. align: 'right',
  10978. /**
  10979. * The horizontal offset of the button.
  10980. */
  10981. x: -10,
  10982. /**
  10983. * The vertical alignment of the button.
  10984. *
  10985. * @type {Highcharts.VerticalAlignValue}
  10986. * @default top
  10987. * @apioption chart.resetZoomButton.position.verticalAlign
  10988. */
  10989. /**
  10990. * The vertical offset of the button.
  10991. */
  10992. y: 10
  10993. }
  10994. },
  10995. /**
  10996. * The pixel width of the plot area border.
  10997. *
  10998. * @sample {highcharts} highcharts/chart/plotborderwidth/
  10999. * 1px border
  11000. * @sample {highstock} stock/chart/plotborder/
  11001. * 2px border
  11002. * @sample {highmaps} maps/chart/plotborder/
  11003. * Plot border options
  11004. *
  11005. * @type {number}
  11006. * @default 0
  11007. * @apioption chart.plotBorderWidth
  11008. */
  11009. /**
  11010. * Whether to apply a drop shadow to the plot area. Requires that
  11011. * plotBackgroundColor be set. The shadow can be an object configuration
  11012. * containing `color`, `offsetX`, `offsetY`, `opacity` and `width`.
  11013. *
  11014. * @sample {highcharts} highcharts/chart/plotshadow/
  11015. * Plot shadow
  11016. * @sample {highstock} stock/chart/plotshadow/
  11017. * Plot shadow
  11018. * @sample {highmaps} maps/chart/plotborder/
  11019. * Plot border options
  11020. *
  11021. * @type {boolean|Highcharts.CSSObject}
  11022. * @default false
  11023. * @apioption chart.plotShadow
  11024. */
  11025. /**
  11026. * When true, cartesian charts like line, spline, area and column are
  11027. * transformed into the polar coordinate system. This produces _polar
  11028. * charts_, also known as _radar charts_.
  11029. *
  11030. * @sample {highcharts} highcharts/demo/polar/
  11031. * Polar chart
  11032. * @sample {highcharts} highcharts/demo/polar-wind-rose/
  11033. * Wind rose, stacked polar column chart
  11034. * @sample {highcharts} highcharts/demo/polar-spider/
  11035. * Spider web chart
  11036. * @sample {highcharts} highcharts/parallel-coordinates/polar/
  11037. * Star plot, multivariate data in a polar chart
  11038. *
  11039. * @type {boolean}
  11040. * @default false
  11041. * @since 2.3.0
  11042. * @product highcharts
  11043. * @requires highcharts-more
  11044. * @apioption chart.polar
  11045. */
  11046. /**
  11047. * Whether to reflow the chart to fit the width of the container div
  11048. * on resizing the window.
  11049. *
  11050. * @sample {highcharts} highcharts/chart/reflow-true/
  11051. * True by default
  11052. * @sample {highcharts} highcharts/chart/reflow-false/
  11053. * False
  11054. * @sample {highstock} stock/chart/reflow-true/
  11055. * True by default
  11056. * @sample {highstock} stock/chart/reflow-false/
  11057. * False
  11058. * @sample {highmaps} maps/chart/reflow-true/
  11059. * True by default
  11060. * @sample {highmaps} maps/chart/reflow-false/
  11061. * False
  11062. *
  11063. * @type {boolean}
  11064. * @default true
  11065. * @since 2.1
  11066. * @apioption chart.reflow
  11067. */
  11068. /**
  11069. * The HTML element where the chart will be rendered. If it is a string,
  11070. * the element by that id is used. The HTML element can also be passed
  11071. * by direct reference, or as the first argument of the chart
  11072. * constructor, in which case the option is not needed.
  11073. *
  11074. * @sample {highcharts} highcharts/chart/reflow-true/
  11075. * String
  11076. * @sample {highcharts} highcharts/chart/renderto-object/
  11077. * Object reference
  11078. * @sample {highcharts} highcharts/chart/renderto-jquery/
  11079. * Object reference through jQuery
  11080. * @sample {highstock} stock/chart/renderto-string/
  11081. * String
  11082. * @sample {highstock} stock/chart/renderto-object/
  11083. * Object reference
  11084. * @sample {highstock} stock/chart/renderto-jquery/
  11085. * Object reference through jQuery
  11086. *
  11087. * @type {string|Highcharts.HTMLDOMElement}
  11088. * @apioption chart.renderTo
  11089. */
  11090. /**
  11091. * The background color of the marker square when selecting (zooming
  11092. * in on) an area of the chart.
  11093. *
  11094. * @see In styled mode, the selection marker fill is set with the
  11095. * `.highcharts-selection-marker` class.
  11096. *
  11097. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11098. * @default rgba(51,92,173,0.25)
  11099. * @since 2.1.7
  11100. * @apioption chart.selectionMarkerFill
  11101. */
  11102. /**
  11103. * Whether to apply a drop shadow to the outer chart area. Requires
  11104. * that backgroundColor be set. The shadow can be an object
  11105. * configuration containing `color`, `offsetX`, `offsetY`, `opacity` and
  11106. * `width`.
  11107. *
  11108. * @sample {highcharts} highcharts/chart/shadow/
  11109. * Shadow
  11110. * @sample {highstock} stock/chart/shadow/
  11111. * Shadow
  11112. * @sample {highmaps} maps/chart/border/
  11113. * Chart border and shadow
  11114. *
  11115. * @type {boolean|Highcharts.CSSObject}
  11116. * @default false
  11117. * @apioption chart.shadow
  11118. */
  11119. /**
  11120. * Whether to show the axes initially. This only applies to empty charts
  11121. * where series are added dynamically, as axes are automatically added
  11122. * to cartesian series.
  11123. *
  11124. * @sample {highcharts} highcharts/chart/showaxes-false/
  11125. * False by default
  11126. * @sample {highcharts} highcharts/chart/showaxes-true/
  11127. * True
  11128. *
  11129. * @type {boolean}
  11130. * @since 1.2.5
  11131. * @product highcharts gantt
  11132. * @apioption chart.showAxes
  11133. */
  11134. /**
  11135. * The space between the bottom edge of the chart and the content (plot
  11136. * area, axis title and labels, title, subtitle or legend in top
  11137. * position).
  11138. *
  11139. * @sample {highcharts} highcharts/chart/spacingbottom/
  11140. * Spacing bottom set to 100
  11141. * @sample {highstock} stock/chart/spacingbottom/
  11142. * Spacing bottom set to 100
  11143. * @sample {highmaps} maps/chart/spacing/
  11144. * Spacing 100 all around
  11145. *
  11146. * @type {number}
  11147. * @default 15
  11148. * @since 2.1
  11149. * @apioption chart.spacingBottom
  11150. */
  11151. /**
  11152. * The space between the left edge of the chart and the content (plot
  11153. * area, axis title and labels, title, subtitle or legend in top
  11154. * position).
  11155. *
  11156. * @sample {highcharts} highcharts/chart/spacingleft/
  11157. * Spacing left set to 100
  11158. * @sample {highstock} stock/chart/spacingleft/
  11159. * Spacing left set to 100
  11160. * @sample {highmaps} maps/chart/spacing/
  11161. * Spacing 100 all around
  11162. *
  11163. * @type {number}
  11164. * @default 10
  11165. * @since 2.1
  11166. * @apioption chart.spacingLeft
  11167. */
  11168. /**
  11169. * The space between the right edge of the chart and the content (plot
  11170. * area, axis title and labels, title, subtitle or legend in top
  11171. * position).
  11172. *
  11173. * @sample {highcharts} highcharts/chart/spacingright-100/
  11174. * Spacing set to 100
  11175. * @sample {highcharts} highcharts/chart/spacingright-legend/
  11176. * Legend in right position with default spacing
  11177. * @sample {highstock} stock/chart/spacingright/
  11178. * Spacing set to 100
  11179. * @sample {highmaps} maps/chart/spacing/
  11180. * Spacing 100 all around
  11181. *
  11182. * @type {number}
  11183. * @default 10
  11184. * @since 2.1
  11185. * @apioption chart.spacingRight
  11186. */
  11187. /**
  11188. * The space between the top edge of the chart and the content (plot
  11189. * area, axis title and labels, title, subtitle or legend in top
  11190. * position).
  11191. *
  11192. * @sample {highcharts} highcharts/chart/spacingtop-100/
  11193. * A top spacing of 100
  11194. * @sample {highcharts} highcharts/chart/spacingtop-10/
  11195. * Floating chart title makes the plot area align to the default
  11196. * spacingTop of 10.
  11197. * @sample {highstock} stock/chart/spacingtop/
  11198. * A top spacing of 100
  11199. * @sample {highmaps} maps/chart/spacing/
  11200. * Spacing 100 all around
  11201. *
  11202. * @type {number}
  11203. * @default 10
  11204. * @since 2.1
  11205. * @apioption chart.spacingTop
  11206. */
  11207. /**
  11208. * Additional CSS styles to apply inline to the container `div`. Note
  11209. * that since the default font styles are applied in the renderer, it
  11210. * is ignorant of the individual chart options and must be set globally.
  11211. *
  11212. * @see In styled mode, general chart styles can be set with the
  11213. * `.highcharts-root` class.
  11214. * @sample {highcharts} highcharts/chart/style-serif-font/
  11215. * Using a serif type font
  11216. * @sample {highcharts} highcharts/css/em/
  11217. * Styled mode with relative font sizes
  11218. * @sample {highstock} stock/chart/style/
  11219. * Using a serif type font
  11220. * @sample {highmaps} maps/chart/style-serif-font/
  11221. * Using a serif type font
  11222. *
  11223. * @type {Highcharts.CSSObject}
  11224. * @default {"fontFamily": "\"Lucida Grande\", \"Lucida Sans Unicode\", Verdana, Arial, Helvetica, sans-serif","fontSize":"12px"}
  11225. * @apioption chart.style
  11226. */
  11227. /**
  11228. * The default series type for the chart. Can be any of the chart types
  11229. * listed under [plotOptions](#plotOptions) and [series](#series) or can
  11230. * be a series provided by an additional module.
  11231. *
  11232. * In TypeScript this option has no effect in sense of typing and
  11233. * instead the `type` option must always be set in the series.
  11234. *
  11235. * @sample {highcharts} highcharts/chart/type-bar/
  11236. * Bar
  11237. * @sample {highstock} stock/chart/type/
  11238. * Areaspline
  11239. * @sample {highmaps} maps/chart/type-mapline/
  11240. * Mapline
  11241. *
  11242. * @type {string}
  11243. * @default {highcharts} line
  11244. * @default {highstock} line
  11245. * @default {highmaps} map
  11246. * @since 2.1.0
  11247. * @apioption chart.type
  11248. */
  11249. /**
  11250. * Decides in what dimensions the user can zoom by dragging the mouse.
  11251. * Can be one of `x`, `y` or `xy`.
  11252. *
  11253. * @see [panKey](#chart.panKey)
  11254. *
  11255. * @sample {highcharts} highcharts/chart/zoomtype-none/
  11256. * None by default
  11257. * @sample {highcharts} highcharts/chart/zoomtype-x/
  11258. * X
  11259. * @sample {highcharts} highcharts/chart/zoomtype-y/
  11260. * Y
  11261. * @sample {highcharts} highcharts/chart/zoomtype-xy/
  11262. * Xy
  11263. * @sample {highstock} stock/demo/basic-line/
  11264. * None by default
  11265. * @sample {highstock} stock/chart/zoomtype-x/
  11266. * X
  11267. * @sample {highstock} stock/chart/zoomtype-y/
  11268. * Y
  11269. * @sample {highstock} stock/chart/zoomtype-xy/
  11270. * Xy
  11271. *
  11272. * @type {string}
  11273. * @product highcharts highstock gantt
  11274. * @validvalue ["x", "y", "xy"]
  11275. * @apioption chart.zoomType
  11276. */
  11277. /**
  11278. * An explicit width for the chart. By default (when `null`) the width
  11279. * is calculated from the offset width of the containing element.
  11280. *
  11281. * @sample {highcharts} highcharts/chart/width/
  11282. * 800px wide
  11283. * @sample {highstock} stock/chart/width/
  11284. * 800px wide
  11285. * @sample {highmaps} maps/chart/size/
  11286. * Chart with explicit size
  11287. *
  11288. * @type {null|number|string}
  11289. */
  11290. width: null,
  11291. /**
  11292. * An explicit height for the chart. If a _number_, the height is
  11293. * given in pixels. If given a _percentage string_ (for example
  11294. * `'56%'`), the height is given as the percentage of the actual chart
  11295. * width. This allows for preserving the aspect ratio across responsive
  11296. * sizes.
  11297. *
  11298. * By default (when `null`) the height is calculated from the offset
  11299. * height of the containing element, or 400 pixels if the containing
  11300. * element's height is 0.
  11301. *
  11302. * @sample {highcharts} highcharts/chart/height/
  11303. * 500px height
  11304. * @sample {highstock} stock/chart/height/
  11305. * 300px height
  11306. * @sample {highmaps} maps/chart/size/
  11307. * Chart with explicit size
  11308. * @sample highcharts/chart/height-percent/
  11309. * Highcharts with percentage height
  11310. *
  11311. * @type {null|number|string}
  11312. */
  11313. height: null,
  11314. /**
  11315. * The color of the outer chart border.
  11316. *
  11317. * @see In styled mode, the stroke is set with the
  11318. * `.highcharts-background` class.
  11319. *
  11320. * @sample {highcharts} highcharts/chart/bordercolor/
  11321. * Brown border
  11322. * @sample {highstock} stock/chart/border/
  11323. * Brown border
  11324. * @sample {highmaps} maps/chart/border/
  11325. * Border options
  11326. *
  11327. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11328. */
  11329. borderColor: '#335cad',
  11330. /**
  11331. * The pixel width of the outer chart border.
  11332. *
  11333. * @see In styled mode, the stroke is set with the
  11334. * `.highcharts-background` class.
  11335. *
  11336. * @sample {highcharts} highcharts/chart/borderwidth/
  11337. * 5px border
  11338. * @sample {highstock} stock/chart/border/
  11339. * 2px border
  11340. * @sample {highmaps} maps/chart/border/
  11341. * Border options
  11342. *
  11343. * @type {number}
  11344. * @default 0
  11345. * @apioption chart.borderWidth
  11346. */
  11347. /**
  11348. * The background color or gradient for the outer chart area.
  11349. *
  11350. * @see In styled mode, the background is set with the
  11351. * `.highcharts-background` class.
  11352. *
  11353. * @sample {highcharts} highcharts/chart/backgroundcolor-color/
  11354. * Color
  11355. * @sample {highcharts} highcharts/chart/backgroundcolor-gradient/
  11356. * Gradient
  11357. * @sample {highstock} stock/chart/backgroundcolor-color/
  11358. * Color
  11359. * @sample {highstock} stock/chart/backgroundcolor-gradient/
  11360. * Gradient
  11361. * @sample {highmaps} maps/chart/backgroundcolor-color/
  11362. * Color
  11363. * @sample {highmaps} maps/chart/backgroundcolor-gradient/
  11364. * Gradient
  11365. *
  11366. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11367. */
  11368. backgroundColor: '#ffffff',
  11369. /**
  11370. * The background color or gradient for the plot area.
  11371. *
  11372. * @see In styled mode, the plot background is set with the
  11373. * `.highcharts-plot-background` class.
  11374. *
  11375. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-color/
  11376. * Color
  11377. * @sample {highcharts} highcharts/chart/plotbackgroundcolor-gradient/
  11378. * Gradient
  11379. * @sample {highstock} stock/chart/plotbackgroundcolor-color/
  11380. * Color
  11381. * @sample {highstock} stock/chart/plotbackgroundcolor-gradient/
  11382. * Gradient
  11383. * @sample {highmaps} maps/chart/plotbackgroundcolor-color/
  11384. * Color
  11385. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  11386. * Gradient
  11387. *
  11388. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11389. * @apioption chart.plotBackgroundColor
  11390. */
  11391. /**
  11392. * The URL for an image to use as the plot background. To set an image
  11393. * as the background for the entire chart, set a CSS background image
  11394. * to the container element. Note that for the image to be applied to
  11395. * exported charts, its URL needs to be accessible by the export server.
  11396. *
  11397. * @see In styled mode, a plot background image can be set with the
  11398. * `.highcharts-plot-background` class and a [custom pattern](
  11399. * https://www.highcharts.com/docs/chart-design-and-style/
  11400. * gradients-shadows-and-patterns).
  11401. *
  11402. * @sample {highcharts} highcharts/chart/plotbackgroundimage/
  11403. * Skies
  11404. * @sample {highstock} stock/chart/plotbackgroundimage/
  11405. * Skies
  11406. *
  11407. * @type {string}
  11408. * @apioption chart.plotBackgroundImage
  11409. */
  11410. /**
  11411. * The color of the inner chart or plot area border.
  11412. *
  11413. * @see In styled mode, a plot border stroke can be set with the
  11414. * `.highcharts-plot-border` class.
  11415. *
  11416. * @sample {highcharts} highcharts/chart/plotbordercolor/
  11417. * Blue border
  11418. * @sample {highstock} stock/chart/plotborder/
  11419. * Blue border
  11420. * @sample {highmaps} maps/chart/plotborder/
  11421. * Plot border options
  11422. *
  11423. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11424. */
  11425. plotBorderColor: '#cccccc'
  11426. },
  11427. /**
  11428. * The chart's main title.
  11429. *
  11430. * @sample {highmaps} maps/title/title/
  11431. * Title options demonstrated
  11432. */
  11433. title: {
  11434. /**
  11435. * When the title is floating, the plot area will not move to make space
  11436. * for it.
  11437. *
  11438. * @sample {highcharts} highcharts/chart/zoomtype-none/
  11439. * False by default
  11440. * @sample {highcharts} highcharts/title/floating/
  11441. * True - title on top of the plot area
  11442. * @sample {highstock} stock/chart/title-floating/
  11443. * True - title on top of the plot area
  11444. *
  11445. * @type {boolean}
  11446. * @default false
  11447. * @since 2.1
  11448. * @apioption title.floating
  11449. */
  11450. /**
  11451. * CSS styles for the title. Use this for font styling, but use `align`,
  11452. * `x` and `y` for text alignment.
  11453. *
  11454. * In styled mode, the title style is given in the `.highcharts-title`
  11455. * class.
  11456. *
  11457. * @sample {highcharts} highcharts/title/style/
  11458. * Custom color and weight
  11459. * @sample {highstock} stock/chart/title-style/
  11460. * Custom color and weight
  11461. * @sample highcharts/css/titles/
  11462. * Styled mode
  11463. *
  11464. * @type {Highcharts.CSSObject}
  11465. * @default {highcharts|highmaps} { "color": "#333333", "fontSize": "18px" }
  11466. * @default {highstock} { "color": "#333333", "fontSize": "16px" }
  11467. * @apioption title.style
  11468. */
  11469. /**
  11470. * Whether to
  11471. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  11472. * to render the text.
  11473. *
  11474. * @type {boolean}
  11475. * @default false
  11476. * @apioption title.useHTML
  11477. */
  11478. /**
  11479. * The vertical alignment of the title. Can be one of `"top"`,
  11480. * `"middle"` and `"bottom"`. When a value is given, the title behaves
  11481. * as if [floating](#title.floating) were `true`.
  11482. *
  11483. * @sample {highcharts} highcharts/title/verticalalign/
  11484. * Chart title in bottom right corner
  11485. * @sample {highstock} stock/chart/title-verticalalign/
  11486. * Chart title in bottom right corner
  11487. *
  11488. * @type {Highcharts.VerticalAlignValue}
  11489. * @since 2.1
  11490. * @apioption title.verticalAlign
  11491. */
  11492. /**
  11493. * The x position of the title relative to the alignment within
  11494. * `chart.spacingLeft` and `chart.spacingRight`.
  11495. *
  11496. * @sample {highcharts} highcharts/title/align/
  11497. * Aligned to the plot area (x = 70px = margin left - spacing
  11498. * left)
  11499. * @sample {highstock} stock/chart/title-align/
  11500. * Aligned to the plot area (x = 50px = margin left - spacing
  11501. * left)
  11502. *
  11503. * @type {number}
  11504. * @default 0
  11505. * @since 2.0
  11506. * @apioption title.x
  11507. */
  11508. /**
  11509. * The y position of the title relative to the alignment within
  11510. * [chart.spacingTop](#chart.spacingTop) and [chart.spacingBottom](
  11511. * #chart.spacingBottom). By default it depends on the font size.
  11512. *
  11513. * @sample {highcharts} highcharts/title/y/
  11514. * Title inside the plot area
  11515. * @sample {highstock} stock/chart/title-verticalalign/
  11516. * Chart title in bottom right corner
  11517. *
  11518. * @type {number}
  11519. * @since 2.0
  11520. * @apioption title.y
  11521. */
  11522. /**
  11523. * The title of the chart. To disable the title, set the `text` to
  11524. * `undefined`.
  11525. *
  11526. * @sample {highcharts} highcharts/title/text/
  11527. * Custom title
  11528. * @sample {highstock} stock/chart/title-text/
  11529. * Custom title
  11530. *
  11531. * @default {highcharts|highmaps} Chart title
  11532. * @default {highstock} undefined
  11533. */
  11534. text: 'Chart title',
  11535. /**
  11536. * The horizontal alignment of the title. Can be one of "left", "center"
  11537. * and "right".
  11538. *
  11539. * @sample {highcharts} highcharts/title/align/
  11540. * Aligned to the plot area (x = 70px = margin left - spacing
  11541. * left)
  11542. * @sample {highstock} stock/chart/title-align/
  11543. * Aligned to the plot area (x = 50px = margin left - spacing
  11544. * left)
  11545. *
  11546. * @type {Highcharts.AlignValue}
  11547. * @since 2.0
  11548. */
  11549. align: 'center',
  11550. /**
  11551. * The margin between the title and the plot area, or if a subtitle
  11552. * is present, the margin between the subtitle and the plot area.
  11553. *
  11554. * @sample {highcharts} highcharts/title/margin-50/
  11555. * A chart title margin of 50
  11556. * @sample {highcharts} highcharts/title/margin-subtitle/
  11557. * The same margin applied with a subtitle
  11558. * @sample {highstock} stock/chart/title-margin/
  11559. * A chart title margin of 50
  11560. *
  11561. * @since 2.1
  11562. */
  11563. margin: 15,
  11564. /**
  11565. * Adjustment made to the title width, normally to reserve space for
  11566. * the exporting burger menu.
  11567. *
  11568. * @sample highcharts/title/widthadjust/
  11569. * Wider menu, greater padding
  11570. *
  11571. * @since 4.2.5
  11572. */
  11573. widthAdjust: -44
  11574. },
  11575. /**
  11576. * The chart's subtitle. This can be used both to display a subtitle below
  11577. * the main title, and to display random text anywhere in the chart. The
  11578. * subtitle can be updated after chart initialization through the
  11579. * `Chart.setTitle` method.
  11580. *
  11581. * @sample {highmaps} maps/title/subtitle/
  11582. * Subtitle options demonstrated
  11583. */
  11584. subtitle: {
  11585. /**
  11586. * When the subtitle is floating, the plot area will not move to make
  11587. * space for it.
  11588. *
  11589. * @sample {highcharts} highcharts/subtitle/floating/
  11590. * Floating title and subtitle
  11591. * @sample {highstock} stock/chart/subtitle-footnote
  11592. * Footnote floating at bottom right of plot area
  11593. *
  11594. * @type {boolean}
  11595. * @default false
  11596. * @since 2.1
  11597. * @apioption subtitle.floating
  11598. */
  11599. /**
  11600. * CSS styles for the title.
  11601. *
  11602. * In styled mode, the subtitle style is given in the
  11603. * `.highcharts-subtitle` class.
  11604. *
  11605. * @sample {highcharts} highcharts/subtitle/style/
  11606. * Custom color and weight
  11607. * @sample {highcharts} highcharts/css/titles/
  11608. * Styled mode
  11609. * @sample {highstock} stock/chart/subtitle-style
  11610. * Custom color and weight
  11611. * @sample {highstock} highcharts/css/titles/
  11612. * Styled mode
  11613. * @sample {highmaps} highcharts/css/titles/
  11614. * Styled mode
  11615. *
  11616. * @type {Highcharts.CSSObject}
  11617. * @default {"color": "#666666"}
  11618. * @apioption subtitle.style
  11619. */
  11620. /**
  11621. * Whether to
  11622. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  11623. * to render the text.
  11624. *
  11625. * @type {boolean}
  11626. * @default false
  11627. * @apioption subtitle.useHTML
  11628. */
  11629. /**
  11630. * The vertical alignment of the title. Can be one of `"top"`,
  11631. * `"middle"` and `"bottom"`. When middle, the subtitle behaves as
  11632. * floating.
  11633. *
  11634. * @sample {highcharts} highcharts/subtitle/verticalalign/
  11635. * Footnote at the bottom right of plot area
  11636. * @sample {highstock} stock/chart/subtitle-footnote
  11637. * Footnote at the bottom right of plot area
  11638. *
  11639. * @type {Highcharts.VerticalAlignValue}
  11640. * @since 2.1
  11641. * @apioption subtitle.verticalAlign
  11642. */
  11643. /**
  11644. * The x position of the subtitle relative to the alignment within
  11645. * `chart.spacingLeft` and `chart.spacingRight`.
  11646. *
  11647. * @sample {highcharts} highcharts/subtitle/align/
  11648. * Footnote at right of plot area
  11649. * @sample {highstock} stock/chart/subtitle-footnote
  11650. * Footnote at the bottom right of plot area
  11651. *
  11652. * @type {number}
  11653. * @default 0
  11654. * @since 2.0
  11655. * @apioption subtitle.x
  11656. */
  11657. /**
  11658. * The y position of the subtitle relative to the alignment within
  11659. * `chart.spacingTop` and `chart.spacingBottom`. By default the subtitle
  11660. * is laid out below the title unless the title is floating.
  11661. *
  11662. * @sample {highcharts} highcharts/subtitle/verticalalign/
  11663. * Footnote at the bottom right of plot area
  11664. * @sample {highstock} stock/chart/subtitle-footnote
  11665. * Footnote at the bottom right of plot area
  11666. *
  11667. * @type {number}
  11668. * @since 2.0
  11669. * @apioption subtitle.y
  11670. */
  11671. /**
  11672. * The subtitle of the chart.
  11673. *
  11674. * @sample {highcharts|highstock} highcharts/subtitle/text/
  11675. * Custom subtitle
  11676. * @sample {highcharts|highstock} highcharts/subtitle/text-formatted/
  11677. * Formatted and linked text.
  11678. */
  11679. text: '',
  11680. /**
  11681. * The horizontal alignment of the subtitle. Can be one of "left",
  11682. * "center" and "right".
  11683. *
  11684. * @sample {highcharts} highcharts/subtitle/align/
  11685. * Footnote at right of plot area
  11686. * @sample {highstock} stock/chart/subtitle-footnote
  11687. * Footnote at bottom right of plot area
  11688. *
  11689. * @type {Highcharts.AlignValue}
  11690. * @since 2.0
  11691. */
  11692. align: 'center',
  11693. /**
  11694. * Adjustment made to the subtitle width, normally to reserve space
  11695. * for the exporting burger menu.
  11696. *
  11697. * @see [title.widthAdjust](#title.widthAdjust)
  11698. *
  11699. * @sample highcharts/title/widthadjust/
  11700. * Wider menu, greater padding
  11701. *
  11702. * @since 4.2.5
  11703. */
  11704. widthAdjust: -44
  11705. },
  11706. /**
  11707. * The chart's caption, which will render below the chart and will be part
  11708. * of exported charts. The caption can be updated after chart initialization
  11709. * through the `Chart.update` or `Chart.caption.update` methods.
  11710. *
  11711. * @sample highcharts/caption/text/
  11712. * A chart with a caption
  11713. * @since 7.2.0
  11714. */
  11715. caption: {
  11716. /**
  11717. * When the caption is floating, the plot area will not move to make
  11718. * space for it.
  11719. *
  11720. * @type {boolean}
  11721. * @default false
  11722. * @apioption caption.floating
  11723. */
  11724. /**
  11725. * The margin between the caption and the plot area.
  11726. */
  11727. margin: 15,
  11728. /**
  11729. * CSS styles for the caption.
  11730. *
  11731. * In styled mode, the caption style is given in the
  11732. * `.highcharts-caption` class.
  11733. *
  11734. * @sample {highcharts} highcharts/css/titles/
  11735. * Styled mode
  11736. *
  11737. * @type {Highcharts.CSSObject}
  11738. * @default {"color": "#666666"}
  11739. * @apioption caption.style
  11740. */
  11741. /**
  11742. * Whether to
  11743. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  11744. * to render the text.
  11745. *
  11746. * @type {boolean}
  11747. * @default false
  11748. * @apioption caption.useHTML
  11749. */
  11750. /**
  11751. * The x position of the caption relative to the alignment within
  11752. * `chart.spacingLeft` and `chart.spacingRight`.
  11753. *
  11754. * @type {number}
  11755. * @default 0
  11756. * @apioption caption.x
  11757. */
  11758. /**
  11759. * The y position of the caption relative to the alignment within
  11760. * `chart.spacingTop` and `chart.spacingBottom`.
  11761. *
  11762. * @type {number}
  11763. * @apioption caption.y
  11764. */
  11765. /**
  11766. * The caption text of the chart.
  11767. *
  11768. * @sample {highcharts} highcharts/caption/text/
  11769. * Custom caption
  11770. */
  11771. text: '',
  11772. /**
  11773. * The horizontal alignment of the caption. Can be one of "left",
  11774. * "center" and "right".
  11775. *
  11776. * @type {Highcharts.AlignValue}
  11777. */
  11778. align: 'left',
  11779. /**
  11780. * The vertical alignment of the caption. Can be one of `"top"`,
  11781. * `"middle"` and `"bottom"`. When middle, the caption behaves as
  11782. * floating.
  11783. *
  11784. * @type {Highcharts.VerticalAlignValue}
  11785. */
  11786. verticalAlign: 'bottom'
  11787. },
  11788. /**
  11789. * The plotOptions is a wrapper object for config objects for each series
  11790. * type. The config objects for each series can also be overridden for
  11791. * each series item as given in the series array.
  11792. *
  11793. * Configuration options for the series are given in three levels. Options
  11794. * for all series in a chart are given in the [plotOptions.series](
  11795. * #plotOptions.series) object. Then options for all series of a specific
  11796. * type are given in the plotOptions of that type, for example
  11797. * `plotOptions.line`. Next, options for one single series are given in
  11798. * [the series array](#series).
  11799. */
  11800. plotOptions: {},
  11801. /**
  11802. * HTML labels that can be positioned anywhere in the chart area.
  11803. *
  11804. * This option is deprecated since v7.1.2. Instead, use
  11805. * [annotations](#annotations) that support labels.
  11806. *
  11807. * @deprecated
  11808. * @product highcharts highstock
  11809. */
  11810. labels: {
  11811. /**
  11812. * An HTML label that can be positioned anywhere in the chart area.
  11813. *
  11814. * @deprecated
  11815. * @type {Array<*>}
  11816. * @apioption labels.items
  11817. */
  11818. /**
  11819. * Inner HTML or text for the label.
  11820. *
  11821. * @deprecated
  11822. * @type {string}
  11823. * @apioption labels.items.html
  11824. */
  11825. /**
  11826. * CSS styles for each label. To position the label, use left and top
  11827. * like this:
  11828. * ```js
  11829. * style: {
  11830. * left: '100px',
  11831. * top: '100px'
  11832. * }
  11833. * ```
  11834. *
  11835. * @deprecated
  11836. * @type {Highcharts.CSSObject}
  11837. * @apioption labels.items.style
  11838. */
  11839. /**
  11840. * Shared CSS styles for all labels.
  11841. *
  11842. * @deprecated
  11843. * @type {Highcharts.CSSObject}
  11844. * @default {"color": "#333333", "position": "absolute"}
  11845. */
  11846. style: {
  11847. /**
  11848. * @ignore-option
  11849. */
  11850. position: 'absolute',
  11851. /**
  11852. * @ignore-option
  11853. */
  11854. color: '#333333'
  11855. }
  11856. },
  11857. /**
  11858. * The legend is a box containing a symbol and name for each series
  11859. * item or point item in the chart. Each series (or points in case
  11860. * of pie charts) is represented by a symbol and its name in the legend.
  11861. *
  11862. * It is possible to override the symbol creator function and create
  11863. * [custom legend symbols](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/legend-custom-symbol/).
  11864. *
  11865. * @productdesc {highmaps}
  11866. * A Highmaps legend by default contains one legend item per series, but if
  11867. * a `colorAxis` is defined, the axis will be displayed in the legend.
  11868. * Either as a gradient, or as multiple legend items for `dataClasses`.
  11869. */
  11870. legend: {
  11871. /**
  11872. * The background color of the legend.
  11873. *
  11874. * @see In styled mode, the legend background fill can be applied with
  11875. * the `.highcharts-legend-box` class.
  11876. *
  11877. * @sample {highcharts} highcharts/legend/backgroundcolor/
  11878. * Yellowish background
  11879. * @sample {highstock} stock/legend/align/
  11880. * Various legend options
  11881. * @sample {highmaps} maps/legend/border-background/
  11882. * Border and background options
  11883. *
  11884. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  11885. * @apioption legend.backgroundColor
  11886. */
  11887. /**
  11888. * The width of the drawn border around the legend.
  11889. *
  11890. * @see In styled mode, the legend border stroke width can be applied
  11891. * with the `.highcharts-legend-box` class.
  11892. *
  11893. * @sample {highcharts} highcharts/legend/borderwidth/
  11894. * 2px border width
  11895. * @sample {highstock} stock/legend/align/
  11896. * Various legend options
  11897. * @sample {highmaps} maps/legend/border-background/
  11898. * Border and background options
  11899. *
  11900. * @type {number}
  11901. * @default 0
  11902. * @apioption legend.borderWidth
  11903. */
  11904. /**
  11905. * Enable or disable the legend. There is also a series-specific option,
  11906. * [showInLegend](#plotOptions.series.showInLegend), that can hide the
  11907. * series from the legend. In some series types this is `false` by
  11908. * default, so it must set to `true` in order to show the legend for the
  11909. * series.
  11910. *
  11911. * @sample {highcharts} highcharts/legend/enabled-false/ Legend disabled
  11912. * @sample {highstock} stock/legend/align/ Various legend options
  11913. * @sample {highmaps} maps/legend/enabled-false/ Legend disabled
  11914. *
  11915. * @default {highstock} false
  11916. * @default {highmaps} true
  11917. * @default {gantt} false
  11918. */
  11919. enabled: true,
  11920. /**
  11921. * The horizontal alignment of the legend box within the chart area.
  11922. * Valid values are `left`, `center` and `right`.
  11923. *
  11924. * In the case that the legend is aligned in a corner position, the
  11925. * `layout` option will determine whether to place it above/below
  11926. * or on the side of the plot area.
  11927. *
  11928. * @sample {highcharts} highcharts/legend/align/
  11929. * Legend at the right of the chart
  11930. * @sample {highstock} stock/legend/align/
  11931. * Various legend options
  11932. * @sample {highmaps} maps/legend/alignment/
  11933. * Legend alignment
  11934. *
  11935. * @type {Highcharts.AlignValue}
  11936. * @since 2.0
  11937. */
  11938. align: 'center',
  11939. /**
  11940. * If the [layout](legend.layout) is `horizontal` and the legend items
  11941. * span over two lines or more, whether to align the items into vertical
  11942. * columns. Setting this to `false` makes room for more items, but will
  11943. * look more messy.
  11944. *
  11945. * @since 6.1.0
  11946. */
  11947. alignColumns: true,
  11948. /**
  11949. * When the legend is floating, the plot area ignores it and is allowed
  11950. * to be placed below it.
  11951. *
  11952. * @sample {highcharts} highcharts/legend/floating-false/
  11953. * False by default
  11954. * @sample {highcharts} highcharts/legend/floating-true/
  11955. * True
  11956. * @sample {highmaps} maps/legend/alignment/
  11957. * Floating legend
  11958. *
  11959. * @type {boolean}
  11960. * @default false
  11961. * @since 2.1
  11962. * @apioption legend.floating
  11963. */
  11964. /**
  11965. * The layout of the legend items. Can be one of `horizontal` or
  11966. * `vertical` or `proximate`. When `proximate`, the legend items will be
  11967. * placed as close as possible to the graphs they're representing,
  11968. * except in inverted charts or when the legend position doesn't allow
  11969. * it.
  11970. *
  11971. * @sample {highcharts} highcharts/legend/layout-horizontal/
  11972. * Horizontal by default
  11973. * @sample {highcharts} highcharts/legend/layout-vertical/
  11974. * Vertical
  11975. * @sample highcharts/legend/layout-proximate
  11976. * Labels proximate to the data
  11977. * @sample {highstock} stock/legend/layout-horizontal/
  11978. * Horizontal by default
  11979. * @sample {highmaps} maps/legend/padding-itemmargin/
  11980. * Vertical with data classes
  11981. * @sample {highmaps} maps/legend/layout-vertical/
  11982. * Vertical with color axis gradient
  11983. *
  11984. * @validvalue ["horizontal", "vertical", "proximate"]
  11985. */
  11986. layout: 'horizontal',
  11987. /**
  11988. * In a legend with horizontal layout, the itemDistance defines the
  11989. * pixel distance between each item.
  11990. *
  11991. * @sample {highcharts} highcharts/legend/layout-horizontal/
  11992. * 50px item distance
  11993. * @sample {highstock} highcharts/legend/layout-horizontal/
  11994. * 50px item distance
  11995. *
  11996. * @type {number}
  11997. * @default {highcharts} 20
  11998. * @default {highstock} 20
  11999. * @default {highmaps} 8
  12000. * @since 3.0.3
  12001. * @apioption legend.itemDistance
  12002. */
  12003. /**
  12004. * The pixel bottom margin for each legend item.
  12005. *
  12006. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  12007. * Padding and item margins demonstrated
  12008. * @sample {highmaps} maps/legend/padding-itemmargin/
  12009. * Padding and item margins demonstrated
  12010. *
  12011. * @type {number}
  12012. * @default 0
  12013. * @since 2.2.0
  12014. * @apioption legend.itemMarginBottom
  12015. */
  12016. /**
  12017. * The pixel top margin for each legend item.
  12018. *
  12019. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  12020. * Padding and item margins demonstrated
  12021. * @sample {highmaps} maps/legend/padding-itemmargin/
  12022. * Padding and item margins demonstrated
  12023. *
  12024. * @type {number}
  12025. * @default 0
  12026. * @since 2.2.0
  12027. * @apioption legend.itemMarginTop
  12028. */
  12029. /**
  12030. * The width for each legend item. By default the items are laid out
  12031. * successively. In a [horizontal layout](legend.layout), if the items
  12032. * are laid out across two rows or more, they will be vertically aligned
  12033. * depending on the [legend.alignColumns](legend.alignColumns) option.
  12034. *
  12035. * @sample {highcharts} highcharts/legend/itemwidth-default/
  12036. * Undefined by default
  12037. * @sample {highcharts} highcharts/legend/itemwidth-80/
  12038. * 80 for aligned legend items
  12039. *
  12040. * @type {number}
  12041. * @since 2.0
  12042. * @apioption legend.itemWidth
  12043. */
  12044. /**
  12045. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  12046. * for each legend label. Available variables relates to properties on
  12047. * the series, or the point in case of pies.
  12048. *
  12049. * @type {string}
  12050. * @default {name}
  12051. * @since 1.3
  12052. * @apioption legend.labelFormat
  12053. */
  12054. /* eslint-disable valid-jsdoc */
  12055. /**
  12056. * Callback function to format each of the series' labels. The `this`
  12057. * keyword refers to the series object, or the point object in case of
  12058. * pie charts. By default the series or point name is printed.
  12059. *
  12060. * @productdesc {highmaps}
  12061. * In Highmaps the context can also be a data class in case of a
  12062. * `colorAxis`.
  12063. *
  12064. * @sample {highcharts} highcharts/legend/labelformatter/
  12065. * Add text
  12066. * @sample {highmaps} maps/legend/labelformatter/
  12067. * Data classes with label formatter
  12068. *
  12069. * @type {Highcharts.FormatterCallbackFunction<Point|Series>}
  12070. */
  12071. labelFormatter: function () {
  12072. /** eslint-enable valid-jsdoc */
  12073. return this.name;
  12074. },
  12075. /**
  12076. * Line height for the legend items. Deprecated as of 2.1\. Instead,
  12077. * the line height for each item can be set using
  12078. * `itemStyle.lineHeight`, and the padding between items using
  12079. * `itemMarginTop` and `itemMarginBottom`.
  12080. *
  12081. * @sample {highcharts} highcharts/legend/lineheight/
  12082. * Setting padding
  12083. *
  12084. * @deprecated
  12085. *
  12086. * @type {number}
  12087. * @default 16
  12088. * @since 2.0
  12089. * @product highcharts gantt
  12090. * @apioption legend.lineHeight
  12091. */
  12092. /**
  12093. * If the plot area sized is calculated automatically and the legend is
  12094. * not floating, the legend margin is the space between the legend and
  12095. * the axis labels or plot area.
  12096. *
  12097. * @sample {highcharts} highcharts/legend/margin-default/
  12098. * 12 pixels by default
  12099. * @sample {highcharts} highcharts/legend/margin-30/
  12100. * 30 pixels
  12101. *
  12102. * @type {number}
  12103. * @default 12
  12104. * @since 2.1
  12105. * @apioption legend.margin
  12106. */
  12107. /**
  12108. * Maximum pixel height for the legend. When the maximum height is
  12109. * extended, navigation will show.
  12110. *
  12111. * @type {number}
  12112. * @since 2.3.0
  12113. * @apioption legend.maxHeight
  12114. */
  12115. /**
  12116. * The color of the drawn border around the legend.
  12117. *
  12118. * @see In styled mode, the legend border stroke can be applied with the
  12119. * `.highcharts-legend-box` class.
  12120. *
  12121. * @sample {highcharts} highcharts/legend/bordercolor/
  12122. * Brown border
  12123. * @sample {highstock} stock/legend/align/
  12124. * Various legend options
  12125. * @sample {highmaps} maps/legend/border-background/
  12126. * Border and background options
  12127. *
  12128. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12129. */
  12130. borderColor: '#999999',
  12131. /**
  12132. * The border corner radius of the legend.
  12133. *
  12134. * @sample {highcharts} highcharts/legend/borderradius-default/
  12135. * Square by default
  12136. * @sample {highcharts} highcharts/legend/borderradius-round/
  12137. * 5px rounded
  12138. * @sample {highmaps} maps/legend/border-background/
  12139. * Border and background options
  12140. */
  12141. borderRadius: 0,
  12142. /**
  12143. * Options for the paging or navigation appearing when the legend is
  12144. * overflown. Navigation works well on screen, but not in static
  12145. * exported images. One way of working around that is to
  12146. * [increase the chart height in
  12147. * export](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/legend/navigation-enabled-false/).
  12148. */
  12149. navigation: {
  12150. /**
  12151. * How to animate the pages when navigating up or down. A value of
  12152. * `true` applies the default navigation given in the
  12153. * `chart.animation` option. Additional options can be given as an
  12154. * object containing values for easing and duration.
  12155. *
  12156. * @sample {highcharts} highcharts/legend/navigation/
  12157. * Legend page navigation demonstrated
  12158. * @sample {highstock} highcharts/legend/navigation/
  12159. * Legend page navigation demonstrated
  12160. *
  12161. * @type {boolean|Highcharts.AnimationOptionsObject}
  12162. * @default true
  12163. * @since 2.2.4
  12164. * @apioption legend.navigation.animation
  12165. */
  12166. /**
  12167. * The pixel size of the up and down arrows in the legend paging
  12168. * navigation.
  12169. *
  12170. * @sample {highcharts} highcharts/legend/navigation/
  12171. * Legend page navigation demonstrated
  12172. * @sample {highstock} highcharts/legend/navigation/
  12173. * Legend page navigation demonstrated
  12174. *
  12175. * @type {number}
  12176. * @default 12
  12177. * @since 2.2.4
  12178. * @apioption legend.navigation.arrowSize
  12179. */
  12180. /**
  12181. * Whether to enable the legend navigation. In most cases, disabling
  12182. * the navigation results in an unwanted overflow.
  12183. *
  12184. * See also the [adapt chart to legend](
  12185. * https://www.highcharts.com/products/plugin-registry/single/8/Adapt-Chart-To-Legend)
  12186. * plugin for a solution to extend the chart height to make room for
  12187. * the legend, optionally in exported charts only.
  12188. *
  12189. * @type {boolean}
  12190. * @default true
  12191. * @since 4.2.4
  12192. * @apioption legend.navigation.enabled
  12193. */
  12194. /**
  12195. * Text styles for the legend page navigation.
  12196. *
  12197. * @see In styled mode, the navigation items are styled with the
  12198. * `.highcharts-legend-navigation` class.
  12199. *
  12200. * @sample {highcharts} highcharts/legend/navigation/
  12201. * Legend page navigation demonstrated
  12202. * @sample {highstock} highcharts/legend/navigation/
  12203. * Legend page navigation demonstrated
  12204. *
  12205. * @type {Highcharts.CSSObject}
  12206. * @since 2.2.4
  12207. * @apioption legend.navigation.style
  12208. */
  12209. /**
  12210. * The color for the active up or down arrow in the legend page
  12211. * navigation.
  12212. *
  12213. * @see In styled mode, the active arrow be styled with the
  12214. * `.highcharts-legend-nav-active` class.
  12215. *
  12216. * @sample {highcharts} highcharts/legend/navigation/
  12217. * Legend page navigation demonstrated
  12218. * @sample {highstock} highcharts/legend/navigation/
  12219. * Legend page navigation demonstrated
  12220. *
  12221. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12222. * @since 2.2.4
  12223. */
  12224. activeColor: '#003399',
  12225. /**
  12226. * The color of the inactive up or down arrow in the legend page
  12227. * navigation. .
  12228. *
  12229. * @see In styled mode, the inactive arrow be styled with the
  12230. * `.highcharts-legend-nav-inactive` class.
  12231. *
  12232. * @sample {highcharts} highcharts/legend/navigation/
  12233. * Legend page navigation demonstrated
  12234. * @sample {highstock} highcharts/legend/navigation/
  12235. * Legend page navigation demonstrated
  12236. *
  12237. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12238. * @since 2.2.4
  12239. */
  12240. inactiveColor: '#cccccc'
  12241. },
  12242. /**
  12243. * The inner padding of the legend box.
  12244. *
  12245. * @sample {highcharts|highstock} highcharts/legend/padding-itemmargin/
  12246. * Padding and item margins demonstrated
  12247. * @sample {highmaps} maps/legend/padding-itemmargin/
  12248. * Padding and item margins demonstrated
  12249. *
  12250. * @type {number}
  12251. * @default 8
  12252. * @since 2.2.0
  12253. * @apioption legend.padding
  12254. */
  12255. /**
  12256. * Whether to reverse the order of the legend items compared to the
  12257. * order of the series or points as defined in the configuration object.
  12258. *
  12259. * @see [yAxis.reversedStacks](#yAxis.reversedStacks),
  12260. * [series.legendIndex](#series.legendIndex)
  12261. *
  12262. * @sample {highcharts} highcharts/legend/reversed/
  12263. * Stacked bar with reversed legend
  12264. *
  12265. * @type {boolean}
  12266. * @default false
  12267. * @since 1.2.5
  12268. * @apioption legend.reversed
  12269. */
  12270. /**
  12271. * Whether to show the symbol on the right side of the text rather than
  12272. * the left side. This is common in Arabic and Hebraic.
  12273. *
  12274. * @sample {highcharts} highcharts/legend/rtl/
  12275. * Symbol to the right
  12276. *
  12277. * @type {boolean}
  12278. * @default false
  12279. * @since 2.2
  12280. * @apioption legend.rtl
  12281. */
  12282. /**
  12283. * CSS styles for the legend area. In the 1.x versions the position
  12284. * of the legend area was determined by CSS. In 2.x, the position is
  12285. * determined by properties like `align`, `verticalAlign`, `x` and `y`,
  12286. * but the styles are still parsed for backwards compatibility.
  12287. *
  12288. * @deprecated
  12289. *
  12290. * @type {Highcharts.CSSObject}
  12291. * @product highcharts highstock
  12292. * @apioption legend.style
  12293. */
  12294. /**
  12295. * CSS styles for each legend item. Only a subset of CSS is supported,
  12296. * notably those options related to text. The default `textOverflow`
  12297. * property makes long texts truncate. Set it to `undefined` to wrap
  12298. * text instead. A `width` property can be added to control the text
  12299. * width.
  12300. *
  12301. * @see In styled mode, the legend items can be styled with the
  12302. * `.highcharts-legend-item` class.
  12303. *
  12304. * @sample {highcharts} highcharts/legend/itemstyle/
  12305. * Bold black text
  12306. * @sample {highmaps} maps/legend/itemstyle/
  12307. * Item text styles
  12308. *
  12309. * @type {Highcharts.CSSObject}
  12310. * @default {"color": "#333333", "cursor": "pointer", "fontSize": "12px", "fontWeight": "bold", "textOverflow": "ellipsis"}
  12311. */
  12312. itemStyle: {
  12313. /**
  12314. * @ignore
  12315. */
  12316. color: '#333333',
  12317. /**
  12318. * @ignore
  12319. */
  12320. cursor: 'pointer',
  12321. /**
  12322. * @ignore
  12323. */
  12324. fontSize: '12px',
  12325. /**
  12326. * @ignore
  12327. */
  12328. fontWeight: 'bold',
  12329. /**
  12330. * @ignore
  12331. */
  12332. textOverflow: 'ellipsis'
  12333. },
  12334. /**
  12335. * CSS styles for each legend item in hover mode. Only a subset of
  12336. * CSS is supported, notably those options related to text. Properties
  12337. * are inherited from `style` unless overridden here.
  12338. *
  12339. * @see In styled mode, the hovered legend items can be styled with
  12340. * the `.highcharts-legend-item:hover` pesudo-class.
  12341. *
  12342. * @sample {highcharts} highcharts/legend/itemhoverstyle/
  12343. * Red on hover
  12344. * @sample {highmaps} maps/legend/itemstyle/
  12345. * Item text styles
  12346. *
  12347. * @type {Highcharts.CSSObject}
  12348. * @default {"color": "#000000"}
  12349. */
  12350. itemHoverStyle: {
  12351. /**
  12352. * @ignore
  12353. */
  12354. color: '#000000'
  12355. },
  12356. /**
  12357. * CSS styles for each legend item when the corresponding series or
  12358. * point is hidden. Only a subset of CSS is supported, notably those
  12359. * options related to text. Properties are inherited from `style`
  12360. * unless overridden here.
  12361. *
  12362. * @see In styled mode, the hidden legend items can be styled with
  12363. * the `.highcharts-legend-item-hidden` class.
  12364. *
  12365. * @sample {highcharts} highcharts/legend/itemhiddenstyle/
  12366. * Darker gray color
  12367. *
  12368. * @type {Highcharts.CSSObject}
  12369. * @default {"color": "#cccccc"}
  12370. */
  12371. itemHiddenStyle: {
  12372. /**
  12373. * @ignore
  12374. */
  12375. color: '#cccccc'
  12376. },
  12377. /**
  12378. * Whether to apply a drop shadow to the legend. A `backgroundColor`
  12379. * also needs to be applied for this to take effect. The shadow can be
  12380. * an object configuration containing `color`, `offsetX`, `offsetY`,
  12381. * `opacity` and `width`.
  12382. *
  12383. * @sample {highcharts} highcharts/legend/shadow/
  12384. * White background and drop shadow
  12385. * @sample {highstock} stock/legend/align/
  12386. * Various legend options
  12387. * @sample {highmaps} maps/legend/border-background/
  12388. * Border and background options
  12389. *
  12390. * @type {boolean|Highcharts.CSSObject}
  12391. */
  12392. shadow: false,
  12393. /**
  12394. * Default styling for the checkbox next to a legend item when
  12395. * `showCheckbox` is true.
  12396. *
  12397. * @type {Highcharts.CSSObject}
  12398. * @default {"width": "13px", "height": "13px", "position":"absolute"}
  12399. */
  12400. itemCheckboxStyle: {
  12401. /**
  12402. * @ignore
  12403. */
  12404. position: 'absolute',
  12405. /**
  12406. * @ignore
  12407. */
  12408. width: '13px',
  12409. /**
  12410. * @ignore
  12411. */
  12412. height: '13px'
  12413. },
  12414. // itemWidth: undefined,
  12415. /**
  12416. * When this is true, the legend symbol width will be the same as
  12417. * the symbol height, which in turn defaults to the font size of the
  12418. * legend items.
  12419. *
  12420. * @since 5.0.0
  12421. */
  12422. squareSymbol: true,
  12423. /**
  12424. * The pixel height of the symbol for series types that use a rectangle
  12425. * in the legend. Defaults to the font size of legend items.
  12426. *
  12427. * @productdesc {highmaps}
  12428. * In Highmaps, when the symbol is the gradient of a vertical color
  12429. * axis, the height defaults to 200.
  12430. *
  12431. * @sample {highmaps} maps/legend/layout-vertical-sized/
  12432. * Sized vertical gradient
  12433. * @sample {highmaps} maps/legend/padding-itemmargin/
  12434. * No distance between data classes
  12435. *
  12436. * @type {number}
  12437. * @since 3.0.8
  12438. * @apioption legend.symbolHeight
  12439. */
  12440. /**
  12441. * The border radius of the symbol for series types that use a rectangle
  12442. * in the legend. Defaults to half the `symbolHeight`.
  12443. *
  12444. * @sample {highcharts} highcharts/legend/symbolradius/
  12445. * Round symbols
  12446. * @sample {highstock} highcharts/legend/symbolradius/
  12447. * Round symbols
  12448. * @sample {highmaps} highcharts/legend/symbolradius/
  12449. * Round symbols
  12450. *
  12451. * @type {number}
  12452. * @since 3.0.8
  12453. * @apioption legend.symbolRadius
  12454. */
  12455. /**
  12456. * The pixel width of the legend item symbol. When the `squareSymbol`
  12457. * option is set, this defaults to the `symbolHeight`, otherwise 16.
  12458. *
  12459. * @productdesc {highmaps}
  12460. * In Highmaps, when the symbol is the gradient of a horizontal color
  12461. * axis, the width defaults to 200.
  12462. *
  12463. * @sample {highcharts} highcharts/legend/symbolwidth/
  12464. * Greater symbol width and padding
  12465. * @sample {highmaps} maps/legend/padding-itemmargin/
  12466. * Padding and item margins demonstrated
  12467. * @sample {highmaps} maps/legend/layout-vertical-sized/
  12468. * Sized vertical gradient
  12469. *
  12470. * @type {number}
  12471. * @apioption legend.symbolWidth
  12472. */
  12473. /**
  12474. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  12475. * to render the legend item texts.
  12476. *
  12477. * Prior to 4.1.7, when using HTML, [legend.navigation](
  12478. * #legend.navigation) was disabled.
  12479. *
  12480. * @type {boolean}
  12481. * @default false
  12482. * @apioption legend.useHTML
  12483. */
  12484. /**
  12485. * The width of the legend box. If a number is set, it translates to
  12486. * pixels. Since v7.0.2 it allows setting a percent string of the full
  12487. * chart width, for example `40%`.
  12488. *
  12489. * Defaults to the full chart width from legends below or above the
  12490. * chart, half the chart width for legends to the left and right.
  12491. *
  12492. * @sample {highcharts} highcharts/legend/width/
  12493. * Aligned to the plot area
  12494. * @sample {highcharts} highcharts/legend/width-percent/
  12495. * A percent of the chart width
  12496. *
  12497. * @type {number|string}
  12498. * @since 2.0
  12499. * @apioption legend.width
  12500. */
  12501. /**
  12502. * The pixel padding between the legend item symbol and the legend
  12503. * item text.
  12504. *
  12505. * @sample {highcharts} highcharts/legend/symbolpadding/
  12506. * Greater symbol width and padding
  12507. */
  12508. symbolPadding: 5,
  12509. /**
  12510. * The vertical alignment of the legend box. Can be one of `top`,
  12511. * `middle` or `bottom`. Vertical position can be further determined
  12512. * by the `y` option.
  12513. *
  12514. * In the case that the legend is aligned in a corner position, the
  12515. * `layout` option will determine whether to place it above/below
  12516. * or on the side of the plot area.
  12517. *
  12518. * When the [layout](#legend.layout) option is `proximate`, the
  12519. * `verticalAlign` option doesn't apply.
  12520. *
  12521. * @sample {highcharts} highcharts/legend/verticalalign/
  12522. * Legend 100px from the top of the chart
  12523. * @sample {highstock} stock/legend/align/
  12524. * Various legend options
  12525. * @sample {highmaps} maps/legend/alignment/
  12526. * Legend alignment
  12527. *
  12528. * @type {Highcharts.VerticalAlignValue}
  12529. * @since 2.0
  12530. */
  12531. verticalAlign: 'bottom',
  12532. // width: undefined,
  12533. /**
  12534. * The x offset of the legend relative to its horizontal alignment
  12535. * `align` within chart.spacingLeft and chart.spacingRight. Negative
  12536. * x moves it to the left, positive x moves it to the right.
  12537. *
  12538. * @sample {highcharts} highcharts/legend/width/
  12539. * Aligned to the plot area
  12540. *
  12541. * @since 2.0
  12542. */
  12543. x: 0,
  12544. /**
  12545. * The vertical offset of the legend relative to it's vertical alignment
  12546. * `verticalAlign` within chart.spacingTop and chart.spacingBottom.
  12547. * Negative y moves it up, positive y moves it down.
  12548. *
  12549. * @sample {highcharts} highcharts/legend/verticalalign/
  12550. * Legend 100px from the top of the chart
  12551. * @sample {highstock} stock/legend/align/
  12552. * Various legend options
  12553. * @sample {highmaps} maps/legend/alignment/
  12554. * Legend alignment
  12555. *
  12556. * @since 2.0
  12557. */
  12558. y: 0,
  12559. /**
  12560. * A title to be added on top of the legend.
  12561. *
  12562. * @sample {highcharts} highcharts/legend/title/
  12563. * Legend title
  12564. * @sample {highmaps} maps/legend/alignment/
  12565. * Legend with title
  12566. *
  12567. * @since 3.0
  12568. */
  12569. title: {
  12570. /**
  12571. * A text or HTML string for the title.
  12572. *
  12573. * @type {string}
  12574. * @since 3.0
  12575. * @apioption legend.title.text
  12576. */
  12577. /**
  12578. * Generic CSS styles for the legend title.
  12579. *
  12580. * @see In styled mode, the legend title is styled with the
  12581. * `.highcharts-legend-title` class.
  12582. *
  12583. * @type {Highcharts.CSSObject}
  12584. * @default {"fontWeight": "bold"}
  12585. * @since 3.0
  12586. */
  12587. style: {
  12588. /**
  12589. * @ignore
  12590. */
  12591. fontWeight: 'bold'
  12592. }
  12593. }
  12594. },
  12595. /**
  12596. * The loading options control the appearance of the loading screen
  12597. * that covers the plot area on chart operations. This screen only
  12598. * appears after an explicit call to `chart.showLoading()`. It is a
  12599. * utility for developers to communicate to the end user that something
  12600. * is going on, for example while retrieving new data via an XHR connection.
  12601. * The "Loading..." text itself is not part of this configuration
  12602. * object, but part of the `lang` object.
  12603. */
  12604. loading: {
  12605. /**
  12606. * The duration in milliseconds of the fade out effect.
  12607. *
  12608. * @sample highcharts/loading/hideduration/
  12609. * Fade in and out over a second
  12610. *
  12611. * @type {number}
  12612. * @default 100
  12613. * @since 1.2.0
  12614. * @apioption loading.hideDuration
  12615. */
  12616. /**
  12617. * The duration in milliseconds of the fade in effect.
  12618. *
  12619. * @sample highcharts/loading/hideduration/
  12620. * Fade in and out over a second
  12621. *
  12622. * @type {number}
  12623. * @default 100
  12624. * @since 1.2.0
  12625. * @apioption loading.showDuration
  12626. */
  12627. /**
  12628. * CSS styles for the loading label `span`.
  12629. *
  12630. * @see In styled mode, the loading label is styled with the
  12631. * `.highcharts-loading-inner` class.
  12632. *
  12633. * @sample {highcharts|highmaps} highcharts/loading/labelstyle/
  12634. * Vertically centered
  12635. * @sample {highstock} stock/loading/general/
  12636. * Label styles
  12637. *
  12638. * @type {Highcharts.CSSObject}
  12639. * @default {"fontWeight": "bold", "position": "relative", "top": "45%"}
  12640. * @since 1.2.0
  12641. */
  12642. labelStyle: {
  12643. /**
  12644. * @ignore
  12645. */
  12646. fontWeight: 'bold',
  12647. /**
  12648. * @ignore
  12649. */
  12650. position: 'relative',
  12651. /**
  12652. * @ignore
  12653. */
  12654. top: '45%'
  12655. },
  12656. /**
  12657. * CSS styles for the loading screen that covers the plot area.
  12658. *
  12659. * In styled mode, the loading label is styled with the
  12660. * `.highcharts-loading` class.
  12661. *
  12662. * @sample {highcharts|highmaps} highcharts/loading/style/
  12663. * Gray plot area, white text
  12664. * @sample {highstock} stock/loading/general/
  12665. * Gray plot area, white text
  12666. *
  12667. * @type {Highcharts.CSSObject}
  12668. * @default {"position": "absolute", "backgroundColor": "#ffffff", "opacity": 0.5, "textAlign": "center"}
  12669. * @since 1.2.0
  12670. */
  12671. style: {
  12672. /**
  12673. * @ignore
  12674. */
  12675. position: 'absolute',
  12676. /**
  12677. * @ignore
  12678. */
  12679. backgroundColor: '#ffffff',
  12680. /**
  12681. * @ignore
  12682. */
  12683. opacity: 0.5,
  12684. /**
  12685. * @ignore
  12686. */
  12687. textAlign: 'center'
  12688. }
  12689. },
  12690. /**
  12691. * Options for the tooltip that appears when the user hovers over a
  12692. * series or point.
  12693. *
  12694. * @declare Highcharts.TooltipOptions
  12695. */
  12696. tooltip: {
  12697. /**
  12698. * The color of the tooltip border. When `undefined`, the border takes
  12699. * the color of the corresponding series or point.
  12700. *
  12701. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  12702. * Follow series by default
  12703. * @sample {highcharts} highcharts/tooltip/bordercolor-black/
  12704. * Black border
  12705. * @sample {highstock} stock/tooltip/general/
  12706. * Styled tooltip
  12707. * @sample {highmaps} maps/tooltip/background-border/
  12708. * Background and border demo
  12709. *
  12710. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  12711. * @apioption tooltip.borderColor
  12712. */
  12713. /**
  12714. * A CSS class name to apply to the tooltip's container div,
  12715. * allowing unique CSS styling for each chart.
  12716. *
  12717. * @type {string}
  12718. * @apioption tooltip.className
  12719. */
  12720. /**
  12721. * Since 4.1, the crosshair definitions are moved to the Axis object
  12722. * in order for a better separation from the tooltip. See
  12723. * [xAxis.crosshair](#xAxis.crosshair).
  12724. *
  12725. * @sample {highcharts} highcharts/tooltip/crosshairs-x/
  12726. * Enable a crosshair for the x value
  12727. *
  12728. * @deprecated
  12729. *
  12730. * @type {*}
  12731. * @default true
  12732. * @apioption tooltip.crosshairs
  12733. */
  12734. /**
  12735. * Distance from point to tooltip in pixels.
  12736. *
  12737. * @type {number}
  12738. * @default 16
  12739. * @apioption tooltip.distance
  12740. */
  12741. /**
  12742. * Whether the tooltip should follow the mouse as it moves across
  12743. * columns, pie slices and other point types with an extent.
  12744. * By default it behaves this way for pie, polygon, map, sankey
  12745. * and wordcloud series by override in the `plotOptions`
  12746. * for those series types.
  12747. *
  12748. * For touch moves to behave the same way, [followTouchMove](
  12749. * #tooltip.followTouchMove) must be `true` also.
  12750. *
  12751. * @type {boolean}
  12752. * @default {highcharts} false
  12753. * @default {highstock} false
  12754. * @default {highmaps} true
  12755. * @since 3.0
  12756. * @apioption tooltip.followPointer
  12757. */
  12758. /**
  12759. * Whether the tooltip should update as the finger moves on a touch
  12760. * device. If this is `true` and [chart.panning](#chart.panning) is
  12761. * set,`followTouchMove` will take over one-finger touches, so the user
  12762. * needs to use two fingers for zooming and panning.
  12763. *
  12764. * Note the difference to [followPointer](#tooltip.followPointer) that
  12765. * only defines the _position_ of the tooltip. If `followPointer` is
  12766. * false in for example a column series, the tooltip will show above or
  12767. * below the column, but as `followTouchMove` is true, the tooltip will
  12768. * jump from column to column as the user swipes across the plot area.
  12769. *
  12770. * @type {boolean}
  12771. * @default {highcharts} true
  12772. * @default {highstock} true
  12773. * @default {highmaps} false
  12774. * @since 3.0.1
  12775. * @apioption tooltip.followTouchMove
  12776. */
  12777. /**
  12778. * Callback function to format the text of the tooltip from scratch. In
  12779. * case of single or [shared](#tooltip.shared) tooltips, a string should
  12780. * be returned. In case of [split](#tooltip.split) tooltips, it should
  12781. * return an array where the first item is the header, and subsequent
  12782. * items are mapped to the points. Return `false` to disable tooltip for
  12783. * a specific point on series.
  12784. *
  12785. * A subset of HTML is supported. Unless `useHTML` is true, the HTML of
  12786. * the tooltip is parsed and converted to SVG, therefore this isn't a
  12787. * complete HTML renderer. The following HTML tags are supported: `b`,
  12788. * `br`, `em`, `i`, `span`, `strong`. Spans can be styled with a `style`
  12789. * attribute, but only text-related CSS, that is shared with SVG, is
  12790. * handled.
  12791. *
  12792. * The available data in the formatter differ a bit depending on whether
  12793. * the tooltip is shared or split, or belongs to a single point. In a
  12794. * shared/split tooltip, all properties except `x`, which is common for
  12795. * all points, are kept in an array, `this.points`.
  12796. *
  12797. * Available data are:
  12798. *
  12799. * - **this.percentage (not shared) /**
  12800. * **this.points[i].percentage (shared)**:
  12801. * Stacked series and pies only. The point's percentage of the total.
  12802. *
  12803. * - **this.point (not shared) / this.points[i].point (shared)**:
  12804. * The point object. The point name, if defined, is available through
  12805. * `this.point.name`.
  12806. *
  12807. * - **this.points**:
  12808. * In a shared tooltip, this is an array containing all other
  12809. * properties for each point.
  12810. *
  12811. * - **this.series (not shared) / this.points[i].series (shared)**:
  12812. * The series object. The series name is available through
  12813. * `this.series.name`.
  12814. *
  12815. * - **this.total (not shared) / this.points[i].total (shared)**:
  12816. * Stacked series only. The total value at this point's x value.
  12817. *
  12818. * - **this.x**:
  12819. * The x value. This property is the same regardless of the tooltip
  12820. * being shared or not.
  12821. *
  12822. * - **this.y (not shared) / this.points[i].y (shared)**:
  12823. * The y value.
  12824. *
  12825. * @sample {highcharts} highcharts/tooltip/formatter-simple/
  12826. * Simple string formatting
  12827. * @sample {highcharts} highcharts/tooltip/formatter-shared/
  12828. * Formatting with shared tooltip
  12829. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  12830. * Formatting with split tooltip
  12831. * @sample highcharts/tooltip/formatter-conditional-default/
  12832. * Extending default formatter
  12833. * @sample {highstock} stock/tooltip/formatter/
  12834. * Formatting with shared tooltip
  12835. * @sample {highmaps} maps/tooltip/formatter/
  12836. * String formatting
  12837. *
  12838. * @type {Highcharts.TooltipFormatterCallbackFunction}
  12839. * @apioption tooltip.formatter
  12840. */
  12841. /**
  12842. * Callback function to format the text of the tooltip for
  12843. * visible null points.
  12844. * Works analogously to [formatter](#tooltip.formatter).
  12845. *
  12846. * @sample highcharts/plotoptions/series-nullformat
  12847. * Format data label and tooltip for null point.
  12848. *
  12849. * @type {Highcharts.TooltipFormatterCallbackFunction}
  12850. * @apioption tooltip.nullFormatter
  12851. */
  12852. /**
  12853. * The number of milliseconds to wait until the tooltip is hidden when
  12854. * mouse out from a point or chart.
  12855. *
  12856. * @type {number}
  12857. * @default 500
  12858. * @since 3.0
  12859. * @apioption tooltip.hideDelay
  12860. */
  12861. /**
  12862. * Whether to allow the tooltip to render outside the chart's SVG
  12863. * element box. By default (`false`), the tooltip is rendered within the
  12864. * chart's SVG element, which results in the tooltip being aligned
  12865. * inside the chart area. For small charts, this may result in clipping
  12866. * or overlapping. When `true`, a separate SVG element is created and
  12867. * overlaid on the page, allowing the tooltip to be aligned inside the
  12868. * page itself.
  12869. *
  12870. * Defaults to `true` if `chart.scrollablePlotArea` is activated,
  12871. * otherwise `false`.
  12872. *
  12873. * @sample highcharts/tooltip/outside
  12874. * Small charts with tooltips outside
  12875. *
  12876. * @type {boolean|undefined}
  12877. * @default undefined
  12878. * @since 6.1.1
  12879. * @apioption tooltip.outside
  12880. */
  12881. /**
  12882. * A callback function for formatting the HTML output for a single point
  12883. * in the tooltip. Like the `pointFormat` string, but with more
  12884. * flexibility.
  12885. *
  12886. * @type {Highcharts.FormatterCallbackFunction<Highcharts.Point>}
  12887. * @since 4.1.0
  12888. * @context Highcharts.Point
  12889. * @apioption tooltip.pointFormatter
  12890. */
  12891. /**
  12892. * A callback function to place the tooltip in a default position. The
  12893. * callback receives three parameters: `labelWidth`, `labelHeight` and
  12894. * `point`, where point contains values for `plotX` and `plotY` telling
  12895. * where the reference point is in the plot area. Add `chart.plotLeft`
  12896. * and `chart.plotTop` to get the full coordinates.
  12897. *
  12898. * Since v7, when [tooltip.split](#tooltip.split) option is enabled,
  12899. * positioner is called for each of the boxes separately, including
  12900. * xAxis header. xAxis header is not a point, instead `point` argument
  12901. * contains info:
  12902. * `{ plotX: Number, plotY: Number, isHeader: Boolean }`
  12903. *
  12904. *
  12905. * The return should be an object containing x and y values, for example
  12906. * `{ x: 100, y: 100 }`.
  12907. *
  12908. * @sample {highcharts} highcharts/tooltip/positioner/
  12909. * A fixed tooltip position
  12910. * @sample {highstock} stock/tooltip/positioner/
  12911. * A fixed tooltip position on top of the chart
  12912. * @sample {highmaps} maps/tooltip/positioner/
  12913. * A fixed tooltip position
  12914. * @sample {highstock} stock/tooltip/split-positioner/
  12915. * Split tooltip with fixed positions
  12916. * @sample {highstock} stock/tooltip/positioner-scrollable-plotarea/
  12917. * Scrollable plot area combined with tooltip positioner
  12918. *
  12919. * @type {Highcharts.TooltipPositionerCallbackFunction}
  12920. * @since 2.2.4
  12921. * @apioption tooltip.positioner
  12922. */
  12923. /**
  12924. * The name of a symbol to use for the border around the tooltip. Can
  12925. * be one of: `"callout"`, `"circle"`, or `"square"`. When
  12926. * [tooltip.split](#tooltip.split)
  12927. * option is enabled, shape is applied to all boxes except header, which
  12928. * is controlled by
  12929. * [tooltip.headerShape](#tooltip.headerShape).
  12930. *
  12931. * Custom callbacks for symbol path generation can also be added to
  12932. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  12933. * [series.marker.symbol](plotOptions.line.marker.symbol).
  12934. *
  12935. * @type {Highcharts.TooltipShapeValue}
  12936. * @default callout
  12937. * @since 4.0
  12938. * @apioption tooltip.shape
  12939. */
  12940. /**
  12941. * The name of a symbol to use for the border around the tooltip
  12942. * header. Applies only when [tooltip.split](#tooltip.split) is
  12943. * enabled.
  12944. *
  12945. * Custom callbacks for symbol path generation can also be added to
  12946. * `Highcharts.SVGRenderer.prototype.symbols` the same way as for
  12947. * [series.marker.symbol](plotOptions.line.marker.symbol).
  12948. *
  12949. * @see [tooltip.shape](#tooltip.shape)
  12950. *
  12951. * @sample {highstock} stock/tooltip/split-positioner/
  12952. * Different shapes for header and split boxes
  12953. *
  12954. * @type {Highcharts.TooltipShapeValue}
  12955. * @default callout
  12956. * @validvalue ["callout", "square"]
  12957. * @since 7.0
  12958. * @apioption tooltip.headerShape
  12959. */
  12960. /**
  12961. * When the tooltip is shared, the entire plot area will capture mouse
  12962. * movement or touch events. Tooltip texts for series types with ordered
  12963. * data (not pie, scatter, flags etc) will be shown in a single bubble.
  12964. * This is recommended for single series charts and for tablet/mobile
  12965. * optimized charts.
  12966. *
  12967. * See also [tooltip.split](#tooltip.split), that is better suited for
  12968. * charts with many series, especially line-type series. The
  12969. * `tooltip.split` option takes precedence over `tooltip.shared`.
  12970. *
  12971. * @sample {highcharts} highcharts/tooltip/shared-false/
  12972. * False by default
  12973. * @sample {highcharts} highcharts/tooltip/shared-true/
  12974. * True
  12975. * @sample {highcharts} highcharts/tooltip/shared-x-crosshair/
  12976. * True with x axis crosshair
  12977. * @sample {highcharts} highcharts/tooltip/shared-true-mixed-types/
  12978. * True with mixed series types
  12979. *
  12980. * @type {boolean}
  12981. * @default false
  12982. * @since 2.1
  12983. * @product highcharts highstock
  12984. * @apioption tooltip.shared
  12985. */
  12986. /**
  12987. * Split the tooltip into one label per series, with the header close
  12988. * to the axis. This is recommended over [shared](#tooltip.shared)
  12989. * tooltips for charts with multiple line series, generally making them
  12990. * easier to read. This option takes precedence over `tooltip.shared`.
  12991. *
  12992. * @productdesc {highstock} In Highstock, tooltips are split by default
  12993. * since v6.0.0. Stock charts typically contain multi-dimension points
  12994. * and multiple panes, making split tooltips the preferred layout over
  12995. * the previous `shared` tooltip.
  12996. *
  12997. * @sample highcharts/tooltip/split/
  12998. * Split tooltip
  12999. * @sample {highcharts|highstock} highcharts/tooltip/formatter-split/
  13000. * Split tooltip and custom formatter callback
  13001. *
  13002. * @type {boolean}
  13003. * @default {highcharts} false
  13004. * @default {highstock} true
  13005. * @since 5.0.0
  13006. * @product highcharts highstock
  13007. * @apioption tooltip.split
  13008. */
  13009. /**
  13010. * Prevents the tooltip from switching or closing, when touched or
  13011. * pointed.
  13012. *
  13013. * @sample highcharts/tooltip/stickoncontact/
  13014. * Tooltip sticks on pointer contact
  13015. *
  13016. * @type {boolean}
  13017. * @since 8.0.1
  13018. * @apioption tooltip.stickOnContact
  13019. */
  13020. /**
  13021. * Use HTML to render the contents of the tooltip instead of SVG. Using
  13022. * HTML allows advanced formatting like tables and images in the
  13023. * tooltip. It is also recommended for rtl languages as it works around
  13024. * rtl bugs in early Firefox.
  13025. *
  13026. * @sample {highcharts|highstock} highcharts/tooltip/footerformat/
  13027. * A table for value alignment
  13028. * @sample {highcharts|highstock} highcharts/tooltip/fullhtml/
  13029. * Full HTML tooltip
  13030. * @sample {highmaps} maps/tooltip/usehtml/
  13031. * Pure HTML tooltip
  13032. *
  13033. * @type {boolean}
  13034. * @default false
  13035. * @since 2.2
  13036. * @apioption tooltip.useHTML
  13037. */
  13038. /**
  13039. * How many decimals to show in each series' y value. This is
  13040. * overridable in each series' tooltip options object. The default is to
  13041. * preserve all decimals.
  13042. *
  13043. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  13044. * Set decimals, prefix and suffix for the value
  13045. * @sample {highmaps} maps/tooltip/valuedecimals/
  13046. * Set decimals, prefix and suffix for the value
  13047. *
  13048. * @type {number}
  13049. * @since 2.2
  13050. * @apioption tooltip.valueDecimals
  13051. */
  13052. /**
  13053. * A string to prepend to each series' y value. Overridable in each
  13054. * series' tooltip options object.
  13055. *
  13056. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  13057. * Set decimals, prefix and suffix for the value
  13058. * @sample {highmaps} maps/tooltip/valuedecimals/
  13059. * Set decimals, prefix and suffix for the value
  13060. *
  13061. * @type {string}
  13062. * @since 2.2
  13063. * @apioption tooltip.valuePrefix
  13064. */
  13065. /**
  13066. * A string to append to each series' y value. Overridable in each
  13067. * series' tooltip options object.
  13068. *
  13069. * @sample {highcharts|highstock} highcharts/tooltip/valuedecimals/
  13070. * Set decimals, prefix and suffix for the value
  13071. * @sample {highmaps} maps/tooltip/valuedecimals/
  13072. * Set decimals, prefix and suffix for the value
  13073. *
  13074. * @type {string}
  13075. * @since 2.2
  13076. * @apioption tooltip.valueSuffix
  13077. */
  13078. /**
  13079. * The format for the date in the tooltip header if the X axis is a
  13080. * datetime axis. The default is a best guess based on the smallest
  13081. * distance between points in the chart.
  13082. *
  13083. * @sample {highcharts} highcharts/tooltip/xdateformat/
  13084. * A different format
  13085. *
  13086. * @type {string}
  13087. * @product highcharts highstock gantt
  13088. * @apioption tooltip.xDateFormat
  13089. */
  13090. /**
  13091. * How many decimals to show for the `point.change` value when the
  13092. * `series.compare` option is set. This is overridable in each series'
  13093. * tooltip options object. The default is to preserve all decimals.
  13094. *
  13095. * @type {number}
  13096. * @since 1.0.1
  13097. * @product highstock
  13098. * @apioption tooltip.changeDecimals
  13099. */
  13100. /**
  13101. * Enable or disable the tooltip.
  13102. *
  13103. * @sample {highcharts} highcharts/tooltip/enabled/
  13104. * Disabled
  13105. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  13106. * Disable tooltip and show values on chart instead
  13107. */
  13108. enabled: true,
  13109. /**
  13110. * Enable or disable animation of the tooltip.
  13111. *
  13112. * @type {boolean}
  13113. * @default true
  13114. * @since 2.3.0
  13115. */
  13116. animation: svg,
  13117. /**
  13118. * The radius of the rounded border corners.
  13119. *
  13120. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13121. * 5px by default
  13122. * @sample {highcharts} highcharts/tooltip/borderradius-0/
  13123. * Square borders
  13124. * @sample {highmaps} maps/tooltip/background-border/
  13125. * Background and border demo
  13126. */
  13127. borderRadius: 3,
  13128. /**
  13129. * For series on a datetime axes, the date format in the tooltip's
  13130. * header will by default be guessed based on the closest data points.
  13131. * This member gives the default string representations used for
  13132. * each unit. For an overview of the replacement codes, see
  13133. * [dateFormat](/class-reference/Highcharts#dateFormat).
  13134. *
  13135. * @see [xAxis.dateTimeLabelFormats](#xAxis.dateTimeLabelFormats)
  13136. *
  13137. * @type {Highcharts.Dictionary<string>}
  13138. * @product highcharts highstock gantt
  13139. */
  13140. dateTimeLabelFormats: {
  13141. /** @internal */
  13142. millisecond: '%A, %b %e, %H:%M:%S.%L',
  13143. /** @internal */
  13144. second: '%A, %b %e, %H:%M:%S',
  13145. /** @internal */
  13146. minute: '%A, %b %e, %H:%M',
  13147. /** @internal */
  13148. hour: '%A, %b %e, %H:%M',
  13149. /** @internal */
  13150. day: '%A, %b %e, %Y',
  13151. /** @internal */
  13152. week: 'Week from %A, %b %e, %Y',
  13153. /** @internal */
  13154. month: '%B %Y',
  13155. /** @internal */
  13156. year: '%Y'
  13157. },
  13158. /**
  13159. * A string to append to the tooltip format.
  13160. *
  13161. * @sample {highcharts} highcharts/tooltip/footerformat/
  13162. * A table for value alignment
  13163. * @sample {highmaps} maps/tooltip/format/
  13164. * Format demo
  13165. *
  13166. * @since 2.2
  13167. */
  13168. footerFormat: '',
  13169. /**
  13170. * Padding inside the tooltip, in pixels.
  13171. *
  13172. * @since 5.0.0
  13173. */
  13174. padding: 8,
  13175. /**
  13176. * Proximity snap for graphs or single points. It defaults to 10 for
  13177. * mouse-powered devices and 25 for touch devices.
  13178. *
  13179. * Note that in most cases the whole plot area captures the mouse
  13180. * movement, and in these cases `tooltip.snap` doesn't make sense. This
  13181. * applies when [stickyTracking](#plotOptions.series.stickyTracking)
  13182. * is `true` (default) and when the tooltip is [shared](#tooltip.shared)
  13183. * or [split](#tooltip.split).
  13184. *
  13185. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13186. * 10 px by default
  13187. * @sample {highcharts} highcharts/tooltip/snap-50/
  13188. * 50 px on graph
  13189. *
  13190. * @type {number}
  13191. * @default 10/25
  13192. * @since 1.2.0
  13193. * @product highcharts highstock
  13194. */
  13195. snap: isTouchDevice ? 25 : 10,
  13196. /**
  13197. * The HTML of the tooltip header line. Variables are enclosed by
  13198. * curly brackets. Available variables are `point.key`, `series.name`,
  13199. * `series.color` and other members from the `point` and `series`
  13200. * objects. The `point.key` variable contains the category name, x
  13201. * value or datetime string depending on the type of axis. For datetime
  13202. * axes, the `point.key` date format can be set using
  13203. * `tooltip.xDateFormat`.
  13204. *
  13205. * @sample {highcharts} highcharts/tooltip/footerformat/
  13206. * An HTML table in the tooltip
  13207. * @sample {highstock} highcharts/tooltip/footerformat/
  13208. * An HTML table in the tooltip
  13209. * @sample {highmaps} maps/tooltip/format/
  13210. * Format demo
  13211. *
  13212. * @type {string}
  13213. * @apioption tooltip.headerFormat
  13214. */
  13215. headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
  13216. /**
  13217. * The HTML of the null point's line in the tooltip. Works analogously
  13218. * to [pointFormat](#tooltip.pointFormat).
  13219. *
  13220. * @sample {highcharts} highcharts/plotoptions/series-nullformat
  13221. * Format data label and tooltip for null point.
  13222. *
  13223. * @type {string}
  13224. * @apioption tooltip.nullFormat
  13225. */
  13226. /**
  13227. * The HTML of the point's line in the tooltip. Variables are enclosed
  13228. * by curly brackets. Available variables are point.x, point.y, series.
  13229. * name and series.color and other properties on the same form.
  13230. * Furthermore, `point.y` can be extended by the `tooltip.valuePrefix`
  13231. * and `tooltip.valueSuffix` variables. This can also be overridden for
  13232. * each series, which makes it a good hook for displaying units.
  13233. *
  13234. * In styled mode, the dot is colored by a class name rather
  13235. * than the point color.
  13236. *
  13237. * @sample {highcharts} highcharts/tooltip/pointformat/
  13238. * A different point format with value suffix
  13239. * @sample {highmaps} maps/tooltip/format/
  13240. * Format demo
  13241. *
  13242. * @type {string}
  13243. * @since 2.2
  13244. * @apioption tooltip.pointFormat
  13245. */
  13246. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y}</b><br/>',
  13247. /**
  13248. * The background color or gradient for the tooltip.
  13249. *
  13250. * In styled mode, the stroke width is set in the
  13251. * `.highcharts-tooltip-box` class.
  13252. *
  13253. * @sample {highcharts} highcharts/tooltip/backgroundcolor-solid/
  13254. * Yellowish background
  13255. * @sample {highcharts} highcharts/tooltip/backgroundcolor-gradient/
  13256. * Gradient
  13257. * @sample {highcharts} highcharts/css/tooltip-border-background/
  13258. * Tooltip in styled mode
  13259. * @sample {highstock} stock/tooltip/general/
  13260. * Custom tooltip
  13261. * @sample {highstock} highcharts/css/tooltip-border-background/
  13262. * Tooltip in styled mode
  13263. * @sample {highmaps} maps/tooltip/background-border/
  13264. * Background and border demo
  13265. * @sample {highmaps} highcharts/css/tooltip-border-background/
  13266. * Tooltip in styled mode
  13267. *
  13268. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  13269. */
  13270. backgroundColor: color('#f7f7f7')
  13271. .setOpacity(0.85).get(),
  13272. /**
  13273. * The pixel width of the tooltip border.
  13274. *
  13275. * In styled mode, the stroke width is set in the
  13276. * `.highcharts-tooltip-box` class.
  13277. *
  13278. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13279. * 2px by default
  13280. * @sample {highcharts} highcharts/tooltip/borderwidth/
  13281. * No border (shadow only)
  13282. * @sample {highcharts} highcharts/css/tooltip-border-background/
  13283. * Tooltip in styled mode
  13284. * @sample {highstock} stock/tooltip/general/
  13285. * Custom tooltip
  13286. * @sample {highstock} highcharts/css/tooltip-border-background/
  13287. * Tooltip in styled mode
  13288. * @sample {highmaps} maps/tooltip/background-border/
  13289. * Background and border demo
  13290. * @sample {highmaps} highcharts/css/tooltip-border-background/
  13291. * Tooltip in styled mode
  13292. */
  13293. borderWidth: 1,
  13294. /**
  13295. * Whether to apply a drop shadow to the tooltip.
  13296. *
  13297. * @sample {highcharts} highcharts/tooltip/bordercolor-default/
  13298. * True by default
  13299. * @sample {highcharts} highcharts/tooltip/shadow/
  13300. * False
  13301. * @sample {highmaps} maps/tooltip/positioner/
  13302. * Fixed tooltip position, border and shadow disabled
  13303. *
  13304. * @type {boolean|Highcharts.ShadowOptionsObject}
  13305. */
  13306. shadow: true,
  13307. /**
  13308. * CSS styles for the tooltip. The tooltip can also be styled through
  13309. * the CSS class `.highcharts-tooltip`.
  13310. *
  13311. * Note that the default `pointerEvents` style makes the tooltip ignore
  13312. * mouse events, so in order to use clickable tooltips, this value must
  13313. * be set to `auto`.
  13314. *
  13315. * @sample {highcharts} highcharts/tooltip/style/
  13316. * Greater padding, bold text
  13317. *
  13318. * @type {Highcharts.CSSObject}
  13319. */
  13320. style: {
  13321. /** @internal */
  13322. color: '#333333',
  13323. /** @internal */
  13324. cursor: 'default',
  13325. /** @internal */
  13326. fontSize: '12px',
  13327. /** @internal */
  13328. whiteSpace: 'nowrap'
  13329. }
  13330. },
  13331. /**
  13332. * Highchart by default puts a credits label in the lower right corner
  13333. * of the chart. This can be changed using these options.
  13334. */
  13335. credits: {
  13336. /**
  13337. * Credits for map source to be concatenated with conventional credit
  13338. * text. By default this is a format string that collects copyright
  13339. * information from the map if available.
  13340. *
  13341. * @see [mapTextFull](#credits.mapTextFull)
  13342. * @see [text](#credits.text)
  13343. *
  13344. * @type {string}
  13345. * @default \u00a9 <a href="{geojson.copyrightUrl}">{geojson.copyrightShort}</a>
  13346. * @since 4.2.2
  13347. * @product highmaps
  13348. * @apioption credits.mapText
  13349. */
  13350. /**
  13351. * Detailed credits for map source to be displayed on hover of credits
  13352. * text. By default this is a format string that collects copyright
  13353. * information from the map if available.
  13354. *
  13355. * @see [mapText](#credits.mapText)
  13356. * @see [text](#credits.text)
  13357. *
  13358. * @type {string}
  13359. * @default {geojson.copyright}
  13360. * @since 4.2.2
  13361. * @product highmaps
  13362. * @apioption credits.mapTextFull
  13363. */
  13364. /**
  13365. * Whether to show the credits text.
  13366. *
  13367. * @sample {highcharts} highcharts/credits/enabled-false/
  13368. * Credits disabled
  13369. * @sample {highstock} stock/credits/enabled/
  13370. * Credits disabled
  13371. * @sample {highmaps} maps/credits/enabled-false/
  13372. * Credits disabled
  13373. */
  13374. enabled: true,
  13375. /**
  13376. * The URL for the credits label.
  13377. *
  13378. * @sample {highcharts} highcharts/credits/href/
  13379. * Custom URL and text
  13380. * @sample {highmaps} maps/credits/customized/
  13381. * Custom URL and text
  13382. */
  13383. href: 'https://www.highcharts.com?credits',
  13384. /**
  13385. * Position configuration for the credits label.
  13386. *
  13387. * @sample {highcharts} highcharts/credits/position-left/
  13388. * Left aligned
  13389. * @sample {highcharts} highcharts/credits/position-left/
  13390. * Left aligned
  13391. * @sample {highmaps} maps/credits/customized/
  13392. * Left aligned
  13393. * @sample {highmaps} maps/credits/customized/
  13394. * Left aligned
  13395. *
  13396. * @type {Highcharts.AlignObject}
  13397. * @since 2.1
  13398. */
  13399. position: {
  13400. /** @internal */
  13401. align: 'right',
  13402. /** @internal */
  13403. x: -10,
  13404. /** @internal */
  13405. verticalAlign: 'bottom',
  13406. /** @internal */
  13407. y: -5
  13408. },
  13409. /**
  13410. * CSS styles for the credits label.
  13411. *
  13412. * @see In styled mode, credits styles can be set with the
  13413. * `.highcharts-credits` class.
  13414. *
  13415. * @type {Highcharts.CSSObject}
  13416. */
  13417. style: {
  13418. /** @internal */
  13419. cursor: 'pointer',
  13420. /** @internal */
  13421. color: '#999999',
  13422. /** @internal */
  13423. fontSize: '9px'
  13424. },
  13425. /**
  13426. * The text for the credits label.
  13427. *
  13428. * @productdesc {highmaps}
  13429. * If a map is loaded as GeoJSON, the text defaults to
  13430. * `Highcharts @ {map-credits}`. Otherwise, it defaults to
  13431. * `Highcharts.com`.
  13432. *
  13433. * @sample {highcharts} highcharts/credits/href/
  13434. * Custom URL and text
  13435. * @sample {highmaps} maps/credits/customized/
  13436. * Custom URL and text
  13437. */
  13438. text: 'Highcharts.com'
  13439. }
  13440. };
  13441. /* eslint-disable spaced-comment */
  13442. '';
  13443. /**
  13444. * Global `Time` object with default options. Since v6.0.5, time settings can be
  13445. * applied individually for each chart. If no individual settings apply, this
  13446. * `Time` object is shared by all instances.
  13447. *
  13448. * @name Highcharts.time
  13449. * @type {Highcharts.Time}
  13450. */
  13451. H.time = new Time(merge(H.defaultOptions.global, H.defaultOptions.time));
  13452. /**
  13453. * Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
  13454. * human readable date string. The format is a subset of the formats for PHP's
  13455. * [strftime](https://www.php.net/manual/en/function.strftime.php) function.
  13456. * Additional formats can be given in the {@link Highcharts.dateFormats} hook.
  13457. *
  13458. * Since v6.0.5, all internal dates are formatted through the
  13459. * {@link Highcharts.Chart#time} instance to respect chart-level time settings.
  13460. * The `Highcharts.dateFormat` function only reflects global time settings set
  13461. * with `setOptions`.
  13462. *
  13463. * Supported format keys:
  13464. * - `%a`: Short weekday, like 'Mon'
  13465. * - `%A`: Long weekday, like 'Monday'
  13466. * - `%d`: Two digit day of the month, 01 to 31
  13467. * - `%e`: Day of the month, 1 through 31
  13468. * - `%w`: Day of the week, 0 through 6
  13469. * - `%b`: Short month, like 'Jan'
  13470. * - `%B`: Long month, like 'January'
  13471. * - `%m`: Two digit month number, 01 through 12
  13472. * - `%y`: Two digits year, like 09 for 2009
  13473. * - `%Y`: Four digits year, like 2009
  13474. * - `%H`: Two digits hours in 24h format, 00 through 23
  13475. * - `%k`: Hours in 24h format, 0 through 23
  13476. * - `%I`: Two digits hours in 12h format, 00 through 11
  13477. * - `%l`: Hours in 12h format, 1 through 12
  13478. * - `%M`: Two digits minutes, 00 through 59
  13479. * - `%p`: Upper case AM or PM
  13480. * - `%P`: Lower case AM or PM
  13481. * - `%S`: Two digits seconds, 00 through 59
  13482. * - `%L`: Milliseconds (naming from Ruby)
  13483. *
  13484. * @function Highcharts.dateFormat
  13485. *
  13486. * @param {string} format
  13487. * The desired format where various time representations are prefixed
  13488. * with `%`.
  13489. *
  13490. * @param {number} timestamp
  13491. * The JavaScript timestamp.
  13492. *
  13493. * @param {boolean} [capitalize=false]
  13494. * Upper case first letter in the return.
  13495. *
  13496. * @return {string}
  13497. * The formatted date.
  13498. */
  13499. H.dateFormat = function (format, timestamp, capitalize) {
  13500. return H.time.dateFormat(format, timestamp, capitalize);
  13501. };
  13502. var optionsModule = {
  13503. dateFormat: H.dateFormat,
  13504. defaultOptions: H.defaultOptions,
  13505. time: H.time
  13506. };
  13507. return optionsModule;
  13508. });
  13509. _registerModule(_modules, 'parts/Axis.js', [_modules['parts/Color.js'], _modules['parts/Globals.js'], _modules['parts/Tick.js'], _modules['parts/Utilities.js'], _modules['parts/Options.js']], function (Color, H, Tick, U, O) {
  13510. /* *
  13511. *
  13512. * (c) 2010-2020 Torstein Honsi
  13513. *
  13514. * License: www.highcharts.com/license
  13515. *
  13516. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  13517. *
  13518. * */
  13519. var addEvent = U.addEvent, animObject = U.animObject, arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, error = U.error, extend = U.extend, fireEvent = U.fireEvent, format = U.format, getMagnitude = U.getMagnitude, isArray = U.isArray, isFunction = U.isFunction, isNumber = U.isNumber, isString = U.isString, merge = U.merge, normalizeTickInterval = U.normalizeTickInterval, objectEach = U.objectEach, pick = U.pick, relativeLength = U.relativeLength, removeEvent = U.removeEvent, splat = U.splat, syncTimeout = U.syncTimeout;
  13520. /**
  13521. * Options for the path on the Axis to be calculated.
  13522. * @interface Highcharts.AxisPlotLinePathOptionsObject
  13523. */ /**
  13524. * Axis value.
  13525. * @name Highcharts.AxisPlotLinePathOptionsObject#value
  13526. * @type {number|undefined}
  13527. */ /**
  13528. * Line width used for calculation crisp line coordinates. Defaults to 1.
  13529. * @name Highcharts.AxisPlotLinePathOptionsObject#lineWidth
  13530. * @type {number|undefined}
  13531. */ /**
  13532. * If `false`, the function will return null when it falls outside the axis
  13533. * bounds. If `true`, the function will return a path aligned to the plot area
  13534. * sides if it falls outside. If `pass`, it will return a path outside.
  13535. * @name Highcharts.AxisPlotLinePathOptionsObject#force
  13536. * @type {string|boolean|undefined}
  13537. */ /**
  13538. * Used in Highstock. When `true`, plot paths (crosshair, plotLines, gridLines)
  13539. * will be rendered on all axes when defined on the first axis.
  13540. * @name Highcharts.AxisPlotLinePathOptionsObject#acrossPanes
  13541. * @type {boolean|undefined}
  13542. */ /**
  13543. * Use old coordinates (for resizing and rescaling).
  13544. * If not set, defaults to `false`.
  13545. * @name Highcharts.AxisPlotLinePathOptionsObject#old
  13546. * @type {boolean|undefined}
  13547. */ /**
  13548. * If given, return the plot line path of a pixel position on the axis.
  13549. * @name Highcharts.AxisPlotLinePathOptionsObject#translatedValue
  13550. * @type {number|undefined}
  13551. */ /**
  13552. * Used in Polar axes. Reverse the positions for concatenation of polygonal
  13553. * plot bands
  13554. * @name Highcharts.AxisPlotLinePathOptionsObject#reverse
  13555. * @type {boolean|undefined}
  13556. */
  13557. /**
  13558. * Options for crosshairs on axes.
  13559. *
  13560. * @product highstock
  13561. *
  13562. * @typedef {Highcharts.XAxisCrosshairOptions|Highcharts.YAxisCrosshairOptions} Highcharts.AxisCrosshairOptions
  13563. */
  13564. /**
  13565. * @typedef {"navigator"|"pan"|"rangeSelectorButton"|"rangeSelectorInput"|"scrollbar"|"traverseUpButton"|"zoom"} Highcharts.AxisExtremesTriggerValue
  13566. */
  13567. /**
  13568. * @callback Highcharts.AxisEventCallbackFunction
  13569. *
  13570. * @param {Highcharts.Axis} this
  13571. */
  13572. /**
  13573. * @callback Highcharts.AxisLabelsFormatterCallbackFunction
  13574. *
  13575. * @param {Highcharts.AxisLabelsFormatterContextObject<number>} this
  13576. *
  13577. * @param {Highcharts.AxisLabelsFormatterContextObject<string>} that
  13578. *
  13579. * @return {string}
  13580. */
  13581. /**
  13582. * @interface Highcharts.AxisLabelsFormatterContextObject<T>
  13583. */ /**
  13584. * @name Highcharts.AxisLabelsFormatterContextObject<T>#axis
  13585. * @type {Highcharts.Axis}
  13586. */ /**
  13587. * @name Highcharts.AxisLabelsFormatterContextObject<T>#chart
  13588. * @type {Highcharts.Chart}
  13589. */ /**
  13590. * @name Highcharts.AxisLabelsFormatterContextObject<T>#isFirst
  13591. * @type {boolean}
  13592. */ /**
  13593. * @name Highcharts.AxisLabelsFormatterContextObject<T>#isLast
  13594. * @type {boolean}
  13595. */ /**
  13596. * @name Highcharts.AxisLabelsFormatterContextObject<T>#pos
  13597. * @type {number}
  13598. */ /**
  13599. * This can be either a numeric value or a category string.
  13600. * @name Highcharts.AxisLabelsFormatterContextObject<T>#value
  13601. * @type {T}
  13602. */
  13603. /**
  13604. * Options for axes.
  13605. *
  13606. * @typedef {Highcharts.XAxisOptions|Highcharts.YAxisOptions|Highcharts.ZAxisOptions} Highcharts.AxisOptions
  13607. */
  13608. /**
  13609. * @callback Highcharts.AxisPointBreakEventCallbackFunction
  13610. *
  13611. * @param {Highcharts.Axis} this
  13612. *
  13613. * @param {Highcharts.AxisPointBreakEventObject} evt
  13614. */
  13615. /**
  13616. * @interface Highcharts.AxisPointBreakEventObject
  13617. */ /**
  13618. * @name Highcharts.AxisPointBreakEventObject#brk
  13619. * @type {Highcharts.Dictionary<number>}
  13620. */ /**
  13621. * @name Highcharts.AxisPointBreakEventObject#point
  13622. * @type {Highcharts.Point}
  13623. */ /**
  13624. * @name Highcharts.AxisPointBreakEventObject#preventDefault
  13625. * @type {Function}
  13626. */ /**
  13627. * @name Highcharts.AxisPointBreakEventObject#target
  13628. * @type {Highcharts.SVGElement}
  13629. */ /**
  13630. * @name Highcharts.AxisPointBreakEventObject#type
  13631. * @type {"pointBreak"|"pointInBreak"}
  13632. */
  13633. /**
  13634. * @callback Highcharts.AxisSetExtremesEventCallbackFunction
  13635. *
  13636. * @param {Highcharts.Axis} this
  13637. *
  13638. * @param {Highcharts.AxisSetExtremesEventObject} evt
  13639. */
  13640. /**
  13641. * @interface Highcharts.AxisSetExtremesEventObject
  13642. * @extends Highcharts.ExtremesObject
  13643. */ /**
  13644. * @name Highcharts.AxisSetExtremesEventObject#preventDefault
  13645. * @type {Function}
  13646. */ /**
  13647. * @name Highcharts.AxisSetExtremesEventObject#target
  13648. * @type {Highcharts.SVGElement}
  13649. */ /**
  13650. * @name Highcharts.AxisSetExtremesEventObject#trigger
  13651. * @type {Highcharts.AxisExtremesTriggerValue|string}
  13652. */ /**
  13653. * @name Highcharts.AxisSetExtremesEventObject#type
  13654. * @type {"setExtremes"}
  13655. */
  13656. /**
  13657. * @callback Highcharts.AxisTickPositionerCallbackFunction
  13658. *
  13659. * @param {Highcharts.Axis} this
  13660. *
  13661. * @return {Highcharts.AxisTickPositionsArray}
  13662. */
  13663. /**
  13664. * @interface Highcharts.AxisTickPositionsArray
  13665. * @augments Array<number>
  13666. */
  13667. /**
  13668. * @typedef {"high"|"low"|"middle"} Highcharts.AxisTitleAlignValue
  13669. */
  13670. /**
  13671. * @typedef {Highcharts.XAxisTitleOptions|Highcharts.YAxisTitleOptions|Highcharts.ZAxisTitleOptions} Highcharts.AxisTitleOptions
  13672. */
  13673. /**
  13674. * @typedef {"linear"|"logarithmic"|"datetime"|"category"|"treegrid"} Highcharts.AxisTypeValue
  13675. */
  13676. /**
  13677. * The returned object literal from the {@link Highcharts.Axis#getExtremes}
  13678. * function.
  13679. *
  13680. * @interface Highcharts.ExtremesObject
  13681. */ /**
  13682. * The maximum value of the axis' associated series.
  13683. * @name Highcharts.ExtremesObject#dataMax
  13684. * @type {number}
  13685. */ /**
  13686. * The minimum value of the axis' associated series.
  13687. * @name Highcharts.ExtremesObject#dataMin
  13688. * @type {number}
  13689. */ /**
  13690. * The maximum axis value, either automatic or set manually. If the `max` option
  13691. * is not set, `maxPadding` is 0 and `endOnTick` is false, this value will be
  13692. * the same as `dataMax`.
  13693. * @name Highcharts.ExtremesObject#max
  13694. * @type {number}
  13695. */ /**
  13696. * The minimum axis value, either automatic or set manually. If the `min` option
  13697. * is not set, `minPadding` is 0 and `startOnTick` is false, this value will be
  13698. * the same as `dataMin`.
  13699. * @name Highcharts.ExtremesObject#min
  13700. * @type {number}
  13701. */ /**
  13702. * The user defined maximum, either from the `max` option or from a zoom or
  13703. * `setExtremes` action.
  13704. * @name Highcharts.ExtremesObject#userMax
  13705. * @type {number}
  13706. */ /**
  13707. * The user defined minimum, either from the `min` option or from a zoom or
  13708. * `setExtremes` action.
  13709. * @name Highcharts.ExtremesObject#userMin
  13710. * @type {number}
  13711. */
  13712. /**
  13713. * Formatter function for the text of a crosshair label.
  13714. *
  13715. * @callback Highcharts.XAxisCrosshairLabelFormatterCallbackFunction
  13716. *
  13717. * @param {Highcharts.Axis} this
  13718. * Axis context
  13719. *
  13720. * @param {number} value
  13721. * Y value of the data point
  13722. *
  13723. * @return {string}
  13724. */
  13725. var defaultOptions = O.defaultOptions;
  13726. var deg2rad = H.deg2rad;
  13727. /**
  13728. * Create a new axis object. Called internally when instanciating a new chart or
  13729. * adding axes by {@link Highcharts.Chart#addAxis}.
  13730. *
  13731. * A chart can have from 0 axes (pie chart) to multiples. In a normal, single
  13732. * series cartesian chart, there is one X axis and one Y axis.
  13733. *
  13734. * The X axis or axes are referenced by {@link Highcharts.Chart.xAxis}, which is
  13735. * an array of Axis objects. If there is only one axis, it can be referenced
  13736. * through `chart.xAxis[0]`, and multiple axes have increasing indices. The same
  13737. * pattern goes for Y axes.
  13738. *
  13739. * If you need to get the axes from a series object, use the `series.xAxis` and
  13740. * `series.yAxis` properties. These are not arrays, as one series can only be
  13741. * associated to one X and one Y axis.
  13742. *
  13743. * A third way to reference the axis programmatically is by `id`. Add an `id` in
  13744. * the axis configuration options, and get the axis by
  13745. * {@link Highcharts.Chart#get}.
  13746. *
  13747. * Configuration options for the axes are given in options.xAxis and
  13748. * options.yAxis.
  13749. *
  13750. * @class
  13751. * @name Highcharts.Axis
  13752. *
  13753. * @param {Highcharts.Chart} chart
  13754. * The Chart instance to apply the axis on.
  13755. *
  13756. * @param {Highcharts.AxisOptions} userOptions
  13757. * Axis options.
  13758. */
  13759. var Axis = /** @class */ (function () {
  13760. /* *
  13761. *
  13762. * Constructors
  13763. *
  13764. * */
  13765. function Axis(chart, userOptions) {
  13766. this.alternateBands = void 0;
  13767. this.bottom = void 0;
  13768. this.categories = void 0;
  13769. this.chart = void 0;
  13770. this.closestPointRange = void 0;
  13771. this.coll = void 0;
  13772. this.hasNames = void 0;
  13773. this.hasVisibleSeries = void 0;
  13774. this.height = void 0;
  13775. this.isLinked = void 0;
  13776. this.labelEdge = void 0; // @todo
  13777. this.labelFormatter = void 0;
  13778. this.left = void 0;
  13779. this.len = void 0;
  13780. this.max = void 0;
  13781. this.maxLabelLength = void 0;
  13782. this.min = void 0;
  13783. this.minorTickInterval = void 0;
  13784. this.minorTicks = void 0;
  13785. this.minPixelPadding = void 0;
  13786. this.names = void 0;
  13787. this.offset = void 0;
  13788. this.oldMax = void 0;
  13789. this.oldMin = void 0;
  13790. this.options = void 0;
  13791. this.overlap = void 0;
  13792. this.paddedTicks = void 0;
  13793. this.plotLinesAndBands = void 0;
  13794. this.plotLinesAndBandsGroups = void 0;
  13795. this.pointRange = void 0;
  13796. this.pointRangePadding = void 0;
  13797. this.pos = void 0;
  13798. this.positiveValuesOnly = void 0;
  13799. this.right = void 0;
  13800. this.series = void 0;
  13801. this.side = void 0;
  13802. this.tickAmount = void 0;
  13803. this.tickInterval = void 0;
  13804. this.tickmarkOffset = void 0;
  13805. this.tickPositions = void 0;
  13806. this.tickRotCorr = void 0;
  13807. this.ticks = void 0;
  13808. this.top = void 0;
  13809. this.transA = void 0;
  13810. this.transB = void 0;
  13811. this.translationSlope = void 0;
  13812. this.userOptions = void 0;
  13813. this.visible = void 0;
  13814. this.width = void 0;
  13815. this.zoomEnabled = void 0;
  13816. this.init(chart, userOptions);
  13817. }
  13818. /* *
  13819. *
  13820. * Functions
  13821. *
  13822. * */
  13823. /**
  13824. * Overrideable function to initialize the axis.
  13825. *
  13826. * @see {@link Axis}
  13827. *
  13828. * @function Highcharts.Axis#init
  13829. *
  13830. * @param {Highcharts.Chart} chart
  13831. * The Chart instance to apply the axis on.
  13832. *
  13833. * @param {Highcharts.AxisOptions} userOptions
  13834. * Axis options.
  13835. *
  13836. * @fires Highcharts.Axis#event:afterInit
  13837. * @fires Highcharts.Axis#event:init
  13838. */
  13839. Axis.prototype.init = function (chart, userOptions) {
  13840. var isXAxis = userOptions.isX, axis = this;
  13841. /**
  13842. * The Chart that the axis belongs to.
  13843. *
  13844. * @name Highcharts.Axis#chart
  13845. * @type {Highcharts.Chart}
  13846. */
  13847. axis.chart = chart;
  13848. /**
  13849. * Whether the axis is horizontal.
  13850. *
  13851. * @name Highcharts.Axis#horiz
  13852. * @type {boolean|undefined}
  13853. */
  13854. axis.horiz = chart.inverted && !axis.isZAxis ? !isXAxis : isXAxis;
  13855. /**
  13856. * Whether the axis is the x-axis.
  13857. *
  13858. * @name Highcharts.Axis#isXAxis
  13859. * @type {boolean|undefined}
  13860. */
  13861. axis.isXAxis = isXAxis;
  13862. /**
  13863. * The collection where the axis belongs, for example `xAxis`, `yAxis`
  13864. * or `colorAxis`. Corresponds to properties on Chart, for example
  13865. * {@link Chart.xAxis}.
  13866. *
  13867. * @name Highcharts.Axis#coll
  13868. * @type {string}
  13869. */
  13870. axis.coll = axis.coll || (isXAxis ? 'xAxis' : 'yAxis');
  13871. fireEvent(this, 'init', { userOptions: userOptions });
  13872. axis.opposite = userOptions.opposite; // needed in setOptions
  13873. /**
  13874. * The side on which the axis is rendered. 0 is top, 1 is right, 2
  13875. * is bottom and 3 is left.
  13876. *
  13877. * @name Highcharts.Axis#side
  13878. * @type {number}
  13879. */
  13880. axis.side = userOptions.side || (axis.horiz ?
  13881. (axis.opposite ? 0 : 2) : // top : bottom
  13882. (axis.opposite ? 1 : 3)); // right : left
  13883. /**
  13884. * Current options for the axis after merge of defaults and user's
  13885. * options.
  13886. *
  13887. * @name Highcharts.Axis#options
  13888. * @type {Highcharts.AxisOptions}
  13889. */
  13890. axis.setOptions(userOptions);
  13891. var options = this.options, type = options.type;
  13892. axis.labelFormatter = (options.labels.formatter ||
  13893. // can be overwritten by dynamic format
  13894. axis.defaultLabelFormatter);
  13895. /**
  13896. * User's options for this axis without defaults.
  13897. *
  13898. * @name Highcharts.Axis#userOptions
  13899. * @type {Highcharts.AxisOptions}
  13900. */
  13901. axis.userOptions = userOptions;
  13902. axis.minPixelPadding = 0;
  13903. /**
  13904. * Whether the axis is reversed. Based on the `axis.reversed`,
  13905. * option, but inverted charts have reversed xAxis by default.
  13906. *
  13907. * @name Highcharts.Axis#reversed
  13908. * @type {boolean}
  13909. */
  13910. axis.reversed = options.reversed;
  13911. axis.visible = options.visible !== false;
  13912. axis.zoomEnabled = options.zoomEnabled !== false;
  13913. // Initial categories
  13914. axis.hasNames =
  13915. type === 'category' || options.categories === true;
  13916. /**
  13917. * If categories are present for the axis, names are used instead of
  13918. * numbers for that axis.
  13919. *
  13920. * Since Highcharts 3.0, categories can also be extracted by giving each
  13921. * point a name and setting axis type to `category`. However, if you
  13922. * have multiple series, best practice remains defining the `categories`
  13923. * array.
  13924. *
  13925. * @see [xAxis.categories](/highcharts/xAxis.categories)
  13926. *
  13927. * @name Highcharts.Axis#categories
  13928. * @type {Array<string>}
  13929. * @readonly
  13930. */
  13931. axis.categories = options.categories || axis.hasNames;
  13932. if (!axis.names) { // Preserve on update (#3830)
  13933. axis.names = [];
  13934. axis.names.keys = {};
  13935. }
  13936. // Placeholder for plotlines and plotbands groups
  13937. axis.plotLinesAndBandsGroups = {};
  13938. // Shorthand types
  13939. axis.positiveValuesOnly = !!(axis.logarithmic && !options.allowNegativeLog);
  13940. // Flag, if axis is linked to another axis
  13941. axis.isLinked = defined(options.linkedTo);
  13942. /**
  13943. * List of major ticks mapped by postition on axis.
  13944. *
  13945. * @see {@link Highcharts.Tick}
  13946. *
  13947. * @name Highcharts.Axis#ticks
  13948. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  13949. */
  13950. axis.ticks = {};
  13951. axis.labelEdge = [];
  13952. /**
  13953. * List of minor ticks mapped by position on the axis.
  13954. *
  13955. * @see {@link Highcharts.Tick}
  13956. *
  13957. * @name Highcharts.Axis#minorTicks
  13958. * @type {Highcharts.Dictionary<Highcharts.Tick>}
  13959. */
  13960. axis.minorTicks = {};
  13961. // List of plotLines/Bands
  13962. axis.plotLinesAndBands = [];
  13963. // Alternate bands
  13964. axis.alternateBands = {};
  13965. // Axis metrics
  13966. axis.len = 0;
  13967. axis.minRange = axis.userMinRange = options.minRange || options.maxZoom;
  13968. axis.range = options.range;
  13969. axis.offset = options.offset || 0;
  13970. /**
  13971. * The maximum value of the axis. In a logarithmic axis, this is the
  13972. * logarithm of the real value, and the real value can be obtained from
  13973. * {@link Axis#getExtremes}.
  13974. *
  13975. * @name Highcharts.Axis#max
  13976. * @type {number|null}
  13977. */
  13978. axis.max = null;
  13979. /**
  13980. * The minimum value of the axis. In a logarithmic axis, this is the
  13981. * logarithm of the real value, and the real value can be obtained from
  13982. * {@link Axis#getExtremes}.
  13983. *
  13984. * @name Highcharts.Axis#min
  13985. * @type {number|null}
  13986. */
  13987. axis.min = null;
  13988. /**
  13989. * The processed crosshair options.
  13990. *
  13991. * @name Highcharts.Axis#crosshair
  13992. * @type {boolean|Highcharts.AxisCrosshairOptions}
  13993. */
  13994. axis.crosshair = pick(options.crosshair, splat(chart.options.tooltip.crosshairs)[isXAxis ? 0 : 1], false);
  13995. var events = axis.options.events;
  13996. // Register. Don't add it again on Axis.update().
  13997. if (chart.axes.indexOf(axis) === -1) { //
  13998. if (isXAxis) { // #2713
  13999. chart.axes.splice(chart.xAxis.length, 0, axis);
  14000. }
  14001. else {
  14002. chart.axes.push(axis);
  14003. }
  14004. chart[axis.coll].push(axis);
  14005. }
  14006. /**
  14007. * All series associated to the axis.
  14008. *
  14009. * @name Highcharts.Axis#series
  14010. * @type {Array<Highcharts.Series>}
  14011. */
  14012. axis.series = axis.series || []; // populated by Series
  14013. // Reversed axis
  14014. if (chart.inverted &&
  14015. !axis.isZAxis &&
  14016. isXAxis &&
  14017. typeof axis.reversed === 'undefined') {
  14018. axis.reversed = true;
  14019. }
  14020. axis.labelRotation = axis.options.labels.rotation;
  14021. // register event listeners
  14022. objectEach(events, function (event, eventType) {
  14023. if (isFunction(event)) {
  14024. addEvent(axis, eventType, event);
  14025. }
  14026. });
  14027. fireEvent(this, 'afterInit');
  14028. };
  14029. /**
  14030. * Merge and set options.
  14031. *
  14032. * @private
  14033. * @function Highcharts.Axis#setOptions
  14034. *
  14035. * @param {Highcharts.AxisOptions} userOptions
  14036. * Axis options.
  14037. *
  14038. * @fires Highcharts.Axis#event:afterSetOptions
  14039. */
  14040. Axis.prototype.setOptions = function (userOptions) {
  14041. this.options = merge(Axis.defaultOptions, (this.coll === 'yAxis') && Axis.defaultYAxisOptions, [
  14042. Axis.defaultTopAxisOptions,
  14043. Axis.defaultRightAxisOptions,
  14044. Axis.defaultBottomAxisOptions,
  14045. Axis.defaultLeftAxisOptions
  14046. ][this.side], merge(
  14047. // if set in setOptions (#1053):
  14048. defaultOptions[this.coll], userOptions));
  14049. fireEvent(this, 'afterSetOptions', { userOptions: userOptions });
  14050. };
  14051. /**
  14052. * The default label formatter. The context is a special config object for
  14053. * the label. In apps, use the
  14054. * [labels.formatter](https://api.highcharts.com/highcharts/xAxis.labels.formatter)
  14055. * instead, except when a modification is needed.
  14056. *
  14057. * @function Highcharts.Axis#defaultLabelFormatter
  14058. *
  14059. * @param {Highcharts.AxisLabelsFormatterContextObject<number>|Highcharts.AxisLabelsFormatterContextObject<string>} this
  14060. * Formatter context of axis label.
  14061. *
  14062. * @return {string}
  14063. * The formatted label content.
  14064. */
  14065. Axis.prototype.defaultLabelFormatter = function () {
  14066. var axis = this.axis, 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, i = numericSymbols && numericSymbols.length, multi, ret, formatOption = axis.options.labels.format,
  14067. // make sure the same symbol is added for all labels on a linear
  14068. // axis
  14069. numericSymbolDetector = axis.logarithmic ?
  14070. Math.abs(value) :
  14071. axis.tickInterval;
  14072. var chart = this.chart;
  14073. var numberFormatter = chart.numberFormatter;
  14074. if (formatOption) {
  14075. ret = format(formatOption, this, chart);
  14076. }
  14077. else if (categories) {
  14078. ret = "" + this.value;
  14079. }
  14080. else if (dateTimeLabelFormat) { // datetime axis
  14081. ret = time.dateFormat(dateTimeLabelFormat, value);
  14082. }
  14083. else if (i && numericSymbolDetector >= 1000) {
  14084. // Decide whether we should add a numeric symbol like k (thousands)
  14085. // or M (millions). If we are to enable this in tooltip or other
  14086. // places as well, we can move this logic to the numberFormatter and
  14087. // enable it by a parameter.
  14088. while (i-- && typeof ret === 'undefined') {
  14089. multi = Math.pow(numSymMagnitude, i + 1);
  14090. if (
  14091. // Only accept a numeric symbol when the distance is more
  14092. // than a full unit. So for example if the symbol is k, we
  14093. // don't accept numbers like 0.5k.
  14094. numericSymbolDetector >= multi &&
  14095. // Accept one decimal before the symbol. Accepts 0.5k but
  14096. // not 0.25k. How does this work with the previous?
  14097. (value * 10) % multi === 0 &&
  14098. numericSymbols[i] !== null &&
  14099. value !== 0) { // #5480
  14100. ret = numberFormatter(value / multi, -1) + numericSymbols[i];
  14101. }
  14102. }
  14103. }
  14104. if (typeof ret === 'undefined') {
  14105. if (Math.abs(value) >= 10000) { // add thousands separators
  14106. ret = numberFormatter(value, -1);
  14107. }
  14108. else { // small numbers
  14109. ret = numberFormatter(value, -1, void 0, ''); // #2466
  14110. }
  14111. }
  14112. return ret;
  14113. };
  14114. /**
  14115. * Get the minimum and maximum for the series of each axis. The function
  14116. * analyzes the axis series and updates `this.dataMin` and `this.dataMax`.
  14117. *
  14118. * @private
  14119. * @function Highcharts.Axis#getSeriesExtremes
  14120. *
  14121. * @fires Highcharts.Axis#event:afterGetSeriesExtremes
  14122. * @fires Highcharts.Axis#event:getSeriesExtremes
  14123. */
  14124. Axis.prototype.getSeriesExtremes = function () {
  14125. var axis = this, chart = axis.chart, xExtremes;
  14126. fireEvent(this, 'getSeriesExtremes', null, function () {
  14127. axis.hasVisibleSeries = false;
  14128. // Reset properties in case we're redrawing (#3353)
  14129. axis.dataMin = axis.dataMax = axis.threshold = null;
  14130. axis.softThreshold = !axis.isXAxis;
  14131. if (axis.stacking) {
  14132. axis.stacking.buildStacks();
  14133. }
  14134. // loop through this axis' series
  14135. axis.series.forEach(function (series) {
  14136. if (series.visible ||
  14137. !chart.options.chart.ignoreHiddenSeries) {
  14138. var seriesOptions = series.options, xData, threshold = seriesOptions.threshold, seriesDataMin, seriesDataMax;
  14139. axis.hasVisibleSeries = true;
  14140. // Validate threshold in logarithmic axes
  14141. if (axis.positiveValuesOnly && threshold <= 0) {
  14142. threshold = null;
  14143. }
  14144. // Get dataMin and dataMax for X axes
  14145. if (axis.isXAxis) {
  14146. xData = series.xData;
  14147. if (xData.length) {
  14148. xExtremes = series.getXExtremes(xData);
  14149. // If xData contains values which is not numbers,
  14150. // then filter them out. To prevent performance hit,
  14151. // we only do this after we have already found
  14152. // seriesDataMin because in most cases all data is
  14153. // valid. #5234.
  14154. seriesDataMin = xExtremes.min;
  14155. seriesDataMax = xExtremes.max;
  14156. if (!isNumber(seriesDataMin) &&
  14157. // #5010:
  14158. !(seriesDataMin instanceof Date)) {
  14159. xData = xData.filter(isNumber);
  14160. xExtremes = series.getXExtremes(xData);
  14161. // Do it again with valid data
  14162. seriesDataMin = xExtremes.min;
  14163. seriesDataMax = xExtremes.max;
  14164. }
  14165. if (xData.length) {
  14166. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  14167. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  14168. }
  14169. }
  14170. // Get dataMin and dataMax for Y axes, as well as handle
  14171. // stacking and processed data
  14172. }
  14173. else {
  14174. // Get this particular series extremes
  14175. var dataExtremes = series.applyExtremes();
  14176. // Get the dataMin and dataMax so far. If percentage is
  14177. // used, the min and max are always 0 and 100. If
  14178. // seriesDataMin and seriesDataMax is null, then series
  14179. // doesn't have active y data, we continue with nulls
  14180. if (isNumber(dataExtremes.dataMin)) {
  14181. seriesDataMin = dataExtremes.dataMin;
  14182. axis.dataMin = Math.min(pick(axis.dataMin, seriesDataMin), seriesDataMin);
  14183. }
  14184. if (isNumber(dataExtremes.dataMax)) {
  14185. seriesDataMax = dataExtremes.dataMax;
  14186. axis.dataMax = Math.max(pick(axis.dataMax, seriesDataMax), seriesDataMax);
  14187. }
  14188. // Adjust to threshold
  14189. if (defined(threshold)) {
  14190. axis.threshold = threshold;
  14191. }
  14192. // If any series has a hard threshold, it takes
  14193. // precedence
  14194. if (!seriesOptions.softThreshold ||
  14195. axis.positiveValuesOnly) {
  14196. axis.softThreshold = false;
  14197. }
  14198. }
  14199. }
  14200. });
  14201. });
  14202. fireEvent(this, 'afterGetSeriesExtremes');
  14203. };
  14204. /**
  14205. * Translate from axis value to pixel position on the chart, or back. Use
  14206. * the `toPixels` and `toValue` functions in applications.
  14207. *
  14208. * @private
  14209. * @function Highcharts.Axis#translate
  14210. *
  14211. * @param {number} val
  14212. * TO-DO: parameter description
  14213. *
  14214. * @param {boolean|null} [backwards]
  14215. * TO-DO: parameter description
  14216. *
  14217. * @param {boolean|null} [cvsCoord]
  14218. * TO-DO: parameter description
  14219. *
  14220. * @param {boolean|null} [old]
  14221. * TO-DO: parameter description
  14222. *
  14223. * @param {boolean} [handleLog]
  14224. * TO-DO: parameter description
  14225. *
  14226. * @param {number} [pointPlacement]
  14227. * TO-DO: parameter description
  14228. *
  14229. * @return {number|undefined}
  14230. */
  14231. Axis.prototype.translate = function (val, backwards, cvsCoord, old, handleLog, pointPlacement) {
  14232. var axis = this.linkedParent || this, // #1417
  14233. sign = 1, cvsOffset = 0, localA = old ? axis.oldTransA : axis.transA, localMin = old ? axis.oldMin : axis.min, returnValue = 0, minPixelPadding = axis.minPixelPadding, doPostTranslate = (axis.isOrdinal ||
  14234. axis.brokenAxis && axis.brokenAxis.hasBreaks ||
  14235. (axis.logarithmic && handleLog)) && axis.lin2val;
  14236. if (!localA) {
  14237. localA = axis.transA;
  14238. }
  14239. // In vertical axes, the canvas coordinates start from 0 at the top like
  14240. // in SVG.
  14241. if (cvsCoord) {
  14242. sign *= -1; // canvas coordinates inverts the value
  14243. cvsOffset = axis.len;
  14244. }
  14245. // Handle reversed axis
  14246. if (axis.reversed) {
  14247. sign *= -1;
  14248. cvsOffset -= sign * (axis.sector || axis.len);
  14249. }
  14250. // From pixels to value
  14251. if (backwards) { // reverse translation
  14252. val = val * sign + cvsOffset;
  14253. val -= minPixelPadding;
  14254. // from chart pixel to value:
  14255. returnValue = val / localA + localMin;
  14256. if (doPostTranslate) { // log and ordinal axes
  14257. returnValue = axis.lin2val(returnValue);
  14258. }
  14259. // From value to pixels
  14260. }
  14261. else {
  14262. if (doPostTranslate) { // log and ordinal axes
  14263. val = axis.val2lin(val);
  14264. }
  14265. returnValue = isNumber(localMin) ?
  14266. (sign * (val - localMin) * localA +
  14267. cvsOffset +
  14268. (sign * minPixelPadding) +
  14269. (isNumber(pointPlacement) ?
  14270. localA * pointPlacement :
  14271. 0)) :
  14272. void 0;
  14273. }
  14274. return returnValue;
  14275. };
  14276. /**
  14277. * Translate a value in terms of axis units into pixels within the chart.
  14278. *
  14279. * @function Highcharts.Axis#toPixels
  14280. *
  14281. * @param {number} value
  14282. * A value in terms of axis units.
  14283. *
  14284. * @param {boolean} paneCoordinates
  14285. * Whether to return the pixel coordinate relative to the chart or just the
  14286. * axis/pane itself.
  14287. *
  14288. * @return {number}
  14289. * Pixel position of the value on the chart or axis.
  14290. */
  14291. Axis.prototype.toPixels = function (value, paneCoordinates) {
  14292. return this.translate(value, false, !this.horiz, null, true) +
  14293. (paneCoordinates ? 0 : this.pos);
  14294. };
  14295. /**
  14296. * Translate a pixel position along the axis to a value in terms of axis
  14297. * units.
  14298. *
  14299. * @function Highcharts.Axis#toValue
  14300. *
  14301. * @param {number} pixel
  14302. * The pixel value coordinate.
  14303. *
  14304. * @param {boolean} [paneCoordinates=false]
  14305. * Whether the input pixel is relative to the chart or just the axis/pane
  14306. * itself.
  14307. *
  14308. * @return {number}
  14309. * The axis value.
  14310. */
  14311. Axis.prototype.toValue = function (pixel, paneCoordinates) {
  14312. return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true);
  14313. };
  14314. /**
  14315. * Create the path for a plot line that goes from the given value on
  14316. * this axis, across the plot to the opposite side. Also used internally for
  14317. * grid lines and crosshairs.
  14318. *
  14319. * @function Highcharts.Axis#getPlotLinePath
  14320. *
  14321. * @param {Highcharts.AxisPlotLinePathOptionsObject} options
  14322. * Options for the path.
  14323. *
  14324. * @return {Highcharts.SVGPathArray|null}
  14325. * The SVG path definition for the plot line.
  14326. */
  14327. Axis.prototype.getPlotLinePath = function (options) {
  14328. var axis = this, chart = axis.chart, axisLeft = axis.left, axisTop = axis.top, old = options.old, value = options.value, translatedValue = options.translatedValue, lineWidth = options.lineWidth, force = options.force, x1, y1, x2, y2, cHeight = (old && chart.oldChartHeight) || chart.chartHeight, cWidth = (old && chart.oldChartWidth) || chart.chartWidth, skip, transB = axis.transB, evt;
  14329. // eslint-disable-next-line valid-jsdoc
  14330. /**
  14331. * Check if x is between a and b. If not, either move to a/b
  14332. * or skip, depending on the force parameter.
  14333. * @private
  14334. */
  14335. function between(x, a, b) {
  14336. if (force !== 'pass' && x < a || x > b) {
  14337. if (force) {
  14338. x = clamp(x, a, b);
  14339. }
  14340. else {
  14341. skip = true;
  14342. }
  14343. }
  14344. return x;
  14345. }
  14346. evt = {
  14347. value: value,
  14348. lineWidth: lineWidth,
  14349. old: old,
  14350. force: force,
  14351. acrossPanes: options.acrossPanes,
  14352. translatedValue: translatedValue
  14353. };
  14354. fireEvent(this, 'getPlotLinePath', evt, function (e) {
  14355. translatedValue = pick(translatedValue, axis.translate(value, null, null, old));
  14356. // Keep the translated value within sane bounds, and avoid Infinity
  14357. // to fail the isNumber test (#7709).
  14358. translatedValue = clamp(translatedValue, -1e5, 1e5);
  14359. x1 = x2 = Math.round(translatedValue + transB);
  14360. y1 = y2 = Math.round(cHeight - translatedValue - transB);
  14361. if (!isNumber(translatedValue)) { // no min or max
  14362. skip = true;
  14363. force = false; // #7175, don't force it when path is invalid
  14364. }
  14365. else if (axis.horiz) {
  14366. y1 = axisTop;
  14367. y2 = cHeight - axis.bottom;
  14368. x1 = x2 = between(x1, axisLeft, axisLeft + axis.width);
  14369. }
  14370. else {
  14371. x1 = axisLeft;
  14372. x2 = cWidth - axis.right;
  14373. y1 = y2 = between(y1, axisTop, axisTop + axis.height);
  14374. }
  14375. e.path = skip && !force ?
  14376. null :
  14377. chart.renderer.crispLine([['M', x1, y1], ['L', x2, y2]], lineWidth || 1);
  14378. });
  14379. return evt.path;
  14380. };
  14381. /**
  14382. * Internal function to et the tick positions of a linear axis to round
  14383. * values like whole tens or every five.
  14384. *
  14385. * @function Highcharts.Axis#getLinearTickPositions
  14386. *
  14387. * @param {number} tickInterval
  14388. * The normalized tick interval.
  14389. *
  14390. * @param {number} min
  14391. * Axis minimum.
  14392. *
  14393. * @param {number} max
  14394. * Axis maximum.
  14395. *
  14396. * @return {Array<number>}
  14397. * An array of axis values where ticks should be placed.
  14398. */
  14399. Axis.prototype.getLinearTickPositions = function (tickInterval, min, max) {
  14400. var pos, lastPos, roundedMin = correctFloat(Math.floor(min / tickInterval) * tickInterval), roundedMax = correctFloat(Math.ceil(max / tickInterval) * tickInterval), tickPositions = [], precision;
  14401. // When the precision is higher than what we filter out in
  14402. // correctFloat, skip it (#6183).
  14403. if (correctFloat(roundedMin + tickInterval) === roundedMin) {
  14404. precision = 20;
  14405. }
  14406. // For single points, add a tick regardless of the relative position
  14407. // (#2662, #6274)
  14408. if (this.single) {
  14409. return [min];
  14410. }
  14411. // Populate the intermediate values
  14412. pos = roundedMin;
  14413. while (pos <= roundedMax) {
  14414. // Place the tick on the rounded value
  14415. tickPositions.push(pos);
  14416. // Always add the raw tickInterval, not the corrected one.
  14417. pos = correctFloat(pos + tickInterval, precision);
  14418. // If the interval is not big enough in the current min - max range
  14419. // to actually increase the loop variable, we need to break out to
  14420. // prevent endless loop. Issue #619
  14421. if (pos === lastPos) {
  14422. break;
  14423. }
  14424. // Record the last value
  14425. lastPos = pos;
  14426. }
  14427. return tickPositions;
  14428. };
  14429. /**
  14430. * Resolve the new minorTicks/minorTickInterval options into the legacy
  14431. * loosely typed minorTickInterval option.
  14432. *
  14433. * @function Highcharts.Axis#getMinorTickInterval
  14434. *
  14435. * @return {number|"auto"|null}
  14436. */
  14437. Axis.prototype.getMinorTickInterval = function () {
  14438. var options = this.options;
  14439. if (options.minorTicks === true) {
  14440. return pick(options.minorTickInterval, 'auto');
  14441. }
  14442. if (options.minorTicks === false) {
  14443. return null;
  14444. }
  14445. return options.minorTickInterval;
  14446. };
  14447. /**
  14448. * Internal function to return the minor tick positions. For logarithmic
  14449. * axes, the same logic as for major ticks is reused.
  14450. *
  14451. * @function Highcharts.Axis#getMinorTickPositions
  14452. *
  14453. * @return {Array<number>}
  14454. * An array of axis values where ticks should be placed.
  14455. */
  14456. Axis.prototype.getMinorTickPositions = function () {
  14457. var axis = this, options = axis.options, tickPositions = axis.tickPositions, minorTickInterval = axis.minorTickInterval, minorTickPositions = [], pos, pointRangePadding = axis.pointRangePadding || 0, min = axis.min - pointRangePadding, // #1498
  14458. max = axis.max + pointRangePadding, // #1498
  14459. range = max - min;
  14460. // If minor ticks get too dense, they are hard to read, and may cause
  14461. // long running script. So we don't draw them.
  14462. if (range && range / minorTickInterval < axis.len / 3) { // #3875
  14463. var logarithmic_1 = axis.logarithmic;
  14464. if (logarithmic_1) {
  14465. // For each interval in the major ticks, compute the minor ticks
  14466. // separately.
  14467. this.paddedTicks.forEach(function (_pos, i, paddedTicks) {
  14468. if (i) {
  14469. minorTickPositions.push.apply(minorTickPositions, logarithmic_1.getLogTickPositions(minorTickInterval, paddedTicks[i - 1], paddedTicks[i], true));
  14470. }
  14471. });
  14472. }
  14473. else if (axis.dateTime &&
  14474. this.getMinorTickInterval() === 'auto') { // #1314
  14475. minorTickPositions = minorTickPositions.concat(axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(minorTickInterval), min, max, options.startOfWeek));
  14476. }
  14477. else {
  14478. for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) {
  14479. // Very, very, tight grid lines (#5771)
  14480. if (pos === minorTickPositions[0]) {
  14481. break;
  14482. }
  14483. minorTickPositions.push(pos);
  14484. }
  14485. }
  14486. }
  14487. if (minorTickPositions.length !== 0) {
  14488. axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
  14489. }
  14490. return minorTickPositions;
  14491. };
  14492. /**
  14493. * Adjust the min and max for the minimum range. Keep in mind that the
  14494. * series data is not yet processed, so we don't have information on data
  14495. * cropping and grouping, or updated `axis.pointRange` or
  14496. * `series.pointRange`. The data can't be processed until we have finally
  14497. * established min and max.
  14498. *
  14499. * @private
  14500. * @function Highcharts.Axis#adjustForMinRange
  14501. */
  14502. Axis.prototype.adjustForMinRange = function () {
  14503. var axis = this, options = axis.options, min = axis.min, max = axis.max, log = axis.logarithmic, zoomOffset, spaceAvailable, closestDataRange, i, distance, xData, loopLength, minArgs, maxArgs, minRange;
  14504. // Set the automatic minimum range based on the closest point distance
  14505. if (axis.isXAxis &&
  14506. typeof axis.minRange === 'undefined' &&
  14507. !log) {
  14508. if (defined(options.min) || defined(options.max)) {
  14509. axis.minRange = null; // don't do this again
  14510. }
  14511. else {
  14512. // Find the closest distance between raw data points, as opposed
  14513. // to closestPointRange that applies to processed points
  14514. // (cropped and grouped)
  14515. axis.series.forEach(function (series) {
  14516. xData = series.xData;
  14517. loopLength = series.xIncrement ? 1 : xData.length - 1;
  14518. for (i = loopLength; i > 0; i--) {
  14519. distance = xData[i] - xData[i - 1];
  14520. if (typeof closestDataRange === 'undefined' ||
  14521. distance < closestDataRange) {
  14522. closestDataRange = distance;
  14523. }
  14524. }
  14525. });
  14526. axis.minRange = Math.min(closestDataRange * 5, axis.dataMax - axis.dataMin);
  14527. }
  14528. }
  14529. // if minRange is exceeded, adjust
  14530. if (max - min < axis.minRange) {
  14531. spaceAvailable =
  14532. axis.dataMax - axis.dataMin >=
  14533. axis.minRange;
  14534. minRange = axis.minRange;
  14535. zoomOffset = (minRange - max + min) / 2;
  14536. // if min and max options have been set, don't go beyond it
  14537. minArgs = [
  14538. min - zoomOffset,
  14539. pick(options.min, min - zoomOffset)
  14540. ];
  14541. // If space is available, stay within the data range
  14542. if (spaceAvailable) {
  14543. minArgs[2] = axis.logarithmic ?
  14544. axis.logarithmic.log2lin(axis.dataMin) :
  14545. axis.dataMin;
  14546. }
  14547. min = arrayMax(minArgs);
  14548. maxArgs = [
  14549. min + minRange,
  14550. pick(options.max, min + minRange)
  14551. ];
  14552. // If space is availabe, stay within the data range
  14553. if (spaceAvailable) {
  14554. maxArgs[2] = log ?
  14555. log.log2lin(axis.dataMax) :
  14556. axis.dataMax;
  14557. }
  14558. max = arrayMin(maxArgs);
  14559. // now if the max is adjusted, adjust the min back
  14560. if (max - min < minRange) {
  14561. minArgs[0] = max - minRange;
  14562. minArgs[1] = pick(options.min, max - minRange);
  14563. min = arrayMax(minArgs);
  14564. }
  14565. }
  14566. // Record modified extremes
  14567. axis.min = min;
  14568. axis.max = max;
  14569. };
  14570. // eslint-disable-next-line valid-jsdoc
  14571. /**
  14572. * Find the closestPointRange across all series.
  14573. *
  14574. * @private
  14575. * @function Highcharts.Axis#getClosest
  14576. */
  14577. Axis.prototype.getClosest = function () {
  14578. var ret;
  14579. if (this.categories) {
  14580. ret = 1;
  14581. }
  14582. else {
  14583. this.series.forEach(function (series) {
  14584. var seriesClosest = series.closestPointRange, visible = series.visible ||
  14585. !series.chart.options.chart.ignoreHiddenSeries;
  14586. if (!series.noSharedTooltip &&
  14587. defined(seriesClosest) &&
  14588. visible) {
  14589. ret = defined(ret) ?
  14590. Math.min(ret, seriesClosest) :
  14591. seriesClosest;
  14592. }
  14593. });
  14594. }
  14595. return ret;
  14596. };
  14597. /**
  14598. * When a point name is given and no x, search for the name in the existing
  14599. * categories, or if categories aren't provided, search names or create a
  14600. * new category (#2522).
  14601. * @private
  14602. * @function Highcharts.Axis#nameToX
  14603. *
  14604. * @param {Highcharts.Point} point
  14605. * The point to inspect.
  14606. *
  14607. * @return {number}
  14608. * The X value that the point is given.
  14609. */
  14610. Axis.prototype.nameToX = function (point) {
  14611. var explicitCategories = isArray(this.categories), names = explicitCategories ? this.categories : this.names, nameX = point.options.x, x;
  14612. point.series.requireSorting = false;
  14613. if (!defined(nameX)) {
  14614. nameX = this.options.uniqueNames === false ?
  14615. point.series.autoIncrement() :
  14616. (explicitCategories ?
  14617. names.indexOf(point.name) :
  14618. pick(names.keys[point.name], -1));
  14619. }
  14620. if (nameX === -1) { // Not found in currenct categories
  14621. if (!explicitCategories) {
  14622. x = names.length;
  14623. }
  14624. }
  14625. else {
  14626. x = nameX;
  14627. }
  14628. // Write the last point's name to the names array
  14629. if (typeof x !== 'undefined') {
  14630. this.names[x] = point.name;
  14631. // Backwards mapping is much faster than array searching (#7725)
  14632. this.names.keys[point.name] = x;
  14633. }
  14634. return x;
  14635. };
  14636. /**
  14637. * When changes have been done to series data, update the axis.names.
  14638. *
  14639. * @private
  14640. * @function Highcharts.Axis#updateNames
  14641. */
  14642. Axis.prototype.updateNames = function () {
  14643. var axis = this, names = this.names, i = names.length;
  14644. if (i > 0) {
  14645. Object.keys(names.keys).forEach(function (key) {
  14646. delete (names.keys)[key];
  14647. });
  14648. names.length = 0;
  14649. this.minRange = this.userMinRange; // Reset
  14650. (this.series || []).forEach(function (series) {
  14651. // Reset incrementer (#5928)
  14652. series.xIncrement = null;
  14653. // When adding a series, points are not yet generated
  14654. if (!series.points || series.isDirtyData) {
  14655. // When we're updating the series with data that is longer
  14656. // than it was, and cropThreshold is passed, we need to make
  14657. // sure that the axis.max is increased _before_ running the
  14658. // premature processData. Otherwise this early iteration of
  14659. // processData will crop the points to axis.max, and the
  14660. // names array will be too short (#5857).
  14661. axis.max = Math.max(axis.max, series.xData.length - 1);
  14662. series.processData();
  14663. series.generatePoints();
  14664. }
  14665. series.data.forEach(function (point, i) {
  14666. var x;
  14667. if (point &&
  14668. point.options &&
  14669. typeof point.name !== 'undefined' // #9562
  14670. ) {
  14671. x = axis.nameToX(point);
  14672. if (typeof x !== 'undefined' && x !== point.x) {
  14673. point.x = x;
  14674. series.xData[i] = x;
  14675. }
  14676. }
  14677. });
  14678. });
  14679. }
  14680. };
  14681. /**
  14682. * Update translation information.
  14683. *
  14684. * @private
  14685. * @function Highcharts.Axis#setAxisTranslation
  14686. *
  14687. * @param {boolean} [saveOld]
  14688. * TO-DO: parameter description
  14689. *
  14690. * @fires Highcharts.Axis#event:afterSetAxisTranslation
  14691. */
  14692. Axis.prototype.setAxisTranslation = function (saveOld) {
  14693. var axis = this, range = axis.max - axis.min, pointRange = axis.axisPointRange || 0, closestPointRange, minPointOffset = 0, pointRangePadding = 0, linkedParent = axis.linkedParent, ordinalCorrection, hasCategories = !!axis.categories, transA = axis.transA, isXAxis = axis.isXAxis;
  14694. // Adjust translation for padding. Y axis with categories need to go
  14695. // through the same (#1784).
  14696. if (isXAxis || hasCategories || pointRange) {
  14697. // Get the closest points
  14698. closestPointRange = axis.getClosest();
  14699. if (linkedParent) {
  14700. minPointOffset = linkedParent.minPointOffset;
  14701. pointRangePadding = linkedParent.pointRangePadding;
  14702. }
  14703. else {
  14704. axis.series.forEach(function (series) {
  14705. var seriesPointRange = hasCategories ?
  14706. 1 :
  14707. (isXAxis ?
  14708. pick(series.options.pointRange, closestPointRange, 0) :
  14709. (axis.axisPointRange || 0)), // #2806
  14710. pointPlacement = series.options.pointPlacement;
  14711. pointRange = Math.max(pointRange, seriesPointRange);
  14712. if (!axis.single || hasCategories) {
  14713. // TODO: series should internally set x- and y-
  14714. // pointPlacement to simplify this logic.
  14715. var isPointPlacementAxis = series.is('xrange') ? !isXAxis : isXAxis;
  14716. // minPointOffset is the value padding to the left of
  14717. // the axis in order to make room for points with a
  14718. // pointRange, typically columns. When the
  14719. // pointPlacement option is 'between' or 'on', this
  14720. // padding does not apply.
  14721. minPointOffset = Math.max(minPointOffset, isPointPlacementAxis && isString(pointPlacement) ?
  14722. 0 :
  14723. seriesPointRange / 2);
  14724. // Determine the total padding needed to the length of
  14725. // the axis to make room for the pointRange. If the
  14726. // series' pointPlacement is 'on', no padding is added.
  14727. pointRangePadding = Math.max(pointRangePadding, isPointPlacementAxis && pointPlacement === 'on' ?
  14728. 0 :
  14729. seriesPointRange);
  14730. }
  14731. });
  14732. }
  14733. // Record minPointOffset and pointRangePadding
  14734. ordinalCorrection = axis.ordinal && axis.ordinal.slope && closestPointRange ?
  14735. axis.ordinal.slope / closestPointRange :
  14736. 1; // #988, #1853
  14737. axis.minPointOffset = minPointOffset =
  14738. minPointOffset * ordinalCorrection;
  14739. axis.pointRangePadding =
  14740. pointRangePadding = pointRangePadding * ordinalCorrection;
  14741. // pointRange means the width reserved for each point, like in a
  14742. // column chart
  14743. axis.pointRange = Math.min(pointRange, axis.single && hasCategories ? 1 : range);
  14744. // closestPointRange means the closest distance between points. In
  14745. // columns it is mostly equal to pointRange, but in lines pointRange
  14746. // is 0 while closestPointRange is some other value
  14747. if (isXAxis) {
  14748. axis.closestPointRange = closestPointRange;
  14749. }
  14750. }
  14751. // Secondary values
  14752. if (saveOld) {
  14753. axis.oldTransA = transA;
  14754. }
  14755. axis.translationSlope = axis.transA = transA =
  14756. axis.staticScale ||
  14757. axis.len / ((range + pointRangePadding) || 1);
  14758. // Translation addend
  14759. axis.transB = axis.horiz ? axis.left : axis.bottom;
  14760. axis.minPixelPadding = transA * minPointOffset;
  14761. fireEvent(this, 'afterSetAxisTranslation');
  14762. };
  14763. /**
  14764. * @private
  14765. * @function Highcharts.Axis#minFromRange
  14766. *
  14767. * @return {number}
  14768. */
  14769. Axis.prototype.minFromRange = function () {
  14770. var axis = this;
  14771. return axis.max - axis.range;
  14772. };
  14773. /**
  14774. * Set the tick positions to round values and optionally extend the extremes
  14775. * to the nearest tick.
  14776. *
  14777. * @private
  14778. * @function Highcharts.Axis#setTickInterval
  14779. *
  14780. * @param {boolean} secondPass
  14781. * TO-DO: parameter description
  14782. *
  14783. * @fires Highcharts.Axis#event:foundExtremes
  14784. */
  14785. Axis.prototype.setTickInterval = function (secondPass) {
  14786. var axis = this, chart = axis.chart, log = axis.logarithmic, options = axis.options, isXAxis = axis.isXAxis, isLinked = axis.isLinked, maxPadding = options.maxPadding, minPadding = options.minPadding, length, linkedParentExtremes, tickIntervalOption = options.tickInterval, minTickInterval, tickPixelIntervalOption = options.tickPixelInterval, categories = axis.categories, threshold = isNumber(axis.threshold) ? axis.threshold : null, softThreshold = axis.softThreshold, thresholdMin, thresholdMax, hardMin, hardMax;
  14787. if (!axis.dateTime && !categories && !isLinked) {
  14788. this.getTickAmount();
  14789. }
  14790. // Min or max set either by zooming/setExtremes or initial options
  14791. hardMin = pick(axis.userMin, options.min);
  14792. hardMax = pick(axis.userMax, options.max);
  14793. // Linked axis gets the extremes from the parent axis
  14794. if (isLinked) {
  14795. axis.linkedParent = chart[axis.coll][options.linkedTo];
  14796. linkedParentExtremes = axis.linkedParent.getExtremes();
  14797. axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
  14798. axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
  14799. if (options.type !== axis.linkedParent.options.type) {
  14800. // Can't link axes of different type
  14801. error(11, 1, chart);
  14802. }
  14803. // Initial min and max from the extreme data values
  14804. }
  14805. else {
  14806. // Adjust to hard threshold
  14807. if (!softThreshold && defined(threshold)) {
  14808. if (axis.dataMin >= threshold) {
  14809. thresholdMin = threshold;
  14810. minPadding = 0;
  14811. }
  14812. else if (axis.dataMax <= threshold) {
  14813. thresholdMax = threshold;
  14814. maxPadding = 0;
  14815. }
  14816. }
  14817. axis.min = pick(hardMin, thresholdMin, axis.dataMin);
  14818. axis.max = pick(hardMax, thresholdMax, axis.dataMax);
  14819. }
  14820. if (log) {
  14821. if (axis.positiveValuesOnly &&
  14822. !secondPass &&
  14823. Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
  14824. // Can't plot negative values on log axis
  14825. error(10, 1, chart);
  14826. }
  14827. // The correctFloat cures #934, float errors on full tens. But it
  14828. // was too aggressive for #4360 because of conversion back to lin,
  14829. // therefore use precision 15.
  14830. axis.min = correctFloat(log.log2lin(axis.min), 16);
  14831. axis.max = correctFloat(log.log2lin(axis.max), 16);
  14832. }
  14833. // handle zoomed range
  14834. if (axis.range && defined(axis.max)) {
  14835. // #618, #6773:
  14836. axis.userMin = axis.min = hardMin =
  14837. Math.max(axis.dataMin, axis.minFromRange());
  14838. axis.userMax = hardMax = axis.max;
  14839. axis.range = null; // don't use it when running setExtremes
  14840. }
  14841. // Hook for Highstock Scroller. Consider combining with beforePadding.
  14842. fireEvent(axis, 'foundExtremes');
  14843. // Hook for adjusting this.min and this.max. Used by bubble series.
  14844. if (axis.beforePadding) {
  14845. axis.beforePadding();
  14846. }
  14847. // adjust min and max for the minimum range
  14848. axis.adjustForMinRange();
  14849. // Pad the values to get clear of the chart's edges. To avoid
  14850. // tickInterval taking the padding into account, we do this after
  14851. // computing tick interval (#1337).
  14852. if (!categories &&
  14853. !axis.axisPointRange &&
  14854. !(axis.stacking && axis.stacking.usePercentage) &&
  14855. !isLinked &&
  14856. defined(axis.min) &&
  14857. defined(axis.max)) {
  14858. length = axis.max - axis.min;
  14859. if (length) {
  14860. if (!defined(hardMin) && minPadding) {
  14861. axis.min -= length * minPadding;
  14862. }
  14863. if (!defined(hardMax) && maxPadding) {
  14864. axis.max += length * maxPadding;
  14865. }
  14866. }
  14867. }
  14868. // Handle options for floor, ceiling, softMin and softMax (#6359)
  14869. if (!isNumber(axis.userMin)) {
  14870. if (isNumber(options.softMin) && options.softMin < axis.min) {
  14871. axis.min = hardMin = options.softMin; // #6894
  14872. }
  14873. if (isNumber(options.floor)) {
  14874. axis.min = Math.max(axis.min, options.floor);
  14875. }
  14876. }
  14877. if (!isNumber(axis.userMax)) {
  14878. if (isNumber(options.softMax) && options.softMax > axis.max) {
  14879. axis.max = hardMax = options.softMax; // #6894
  14880. }
  14881. if (isNumber(options.ceiling)) {
  14882. axis.max = Math.min(axis.max, options.ceiling);
  14883. }
  14884. }
  14885. // When the threshold is soft, adjust the extreme value only if the data
  14886. // extreme and the padded extreme land on either side of the threshold.
  14887. // For example, a series of [0, 1, 2, 3] would make the yAxis add a tick
  14888. // for -1 because of the default minPadding and startOnTick options.
  14889. // This is prevented by the softThreshold option.
  14890. if (softThreshold && defined(axis.dataMin)) {
  14891. threshold = threshold || 0;
  14892. if (!defined(hardMin) &&
  14893. axis.min < threshold &&
  14894. axis.dataMin >= threshold) {
  14895. axis.min = axis.options.minRange ?
  14896. Math.min(threshold, axis.max -
  14897. axis.minRange) :
  14898. threshold;
  14899. }
  14900. else if (!defined(hardMax) &&
  14901. axis.max > threshold &&
  14902. axis.dataMax <= threshold) {
  14903. axis.max = axis.options.minRange ?
  14904. Math.max(threshold, axis.min +
  14905. axis.minRange) :
  14906. threshold;
  14907. }
  14908. }
  14909. // get tickInterval
  14910. if (axis.min === axis.max ||
  14911. typeof axis.min === 'undefined' ||
  14912. typeof axis.max === 'undefined') {
  14913. axis.tickInterval = 1;
  14914. }
  14915. else if (isLinked &&
  14916. !tickIntervalOption &&
  14917. tickPixelIntervalOption ===
  14918. axis.linkedParent.options.tickPixelInterval) {
  14919. axis.tickInterval = tickIntervalOption =
  14920. axis.linkedParent.tickInterval;
  14921. }
  14922. else {
  14923. axis.tickInterval = pick(tickIntervalOption, this.tickAmount ?
  14924. ((axis.max - axis.min) /
  14925. Math.max(this.tickAmount - 1, 1)) :
  14926. void 0,
  14927. // For categoried axis, 1 is default, for linear axis use
  14928. // tickPix
  14929. categories ?
  14930. 1 :
  14931. // don't let it be more than the data range
  14932. (axis.max - axis.min) *
  14933. tickPixelIntervalOption /
  14934. Math.max(axis.len, tickPixelIntervalOption));
  14935. }
  14936. // Now we're finished detecting min and max, crop and group series data.
  14937. // This is in turn needed in order to find tick positions in ordinal
  14938. // axes.
  14939. if (isXAxis && !secondPass) {
  14940. axis.series.forEach(function (series) {
  14941. series.processData(axis.min !== axis.oldMin || axis.max !== axis.oldMax);
  14942. });
  14943. }
  14944. // set the translation factor used in translate function
  14945. axis.setAxisTranslation(true);
  14946. // hook for ordinal axes and radial axes
  14947. fireEvent(this, 'initialAxisTranslation');
  14948. // In column-like charts, don't cramp in more ticks than there are
  14949. // points (#1943, #4184)
  14950. if (axis.pointRange && !tickIntervalOption) {
  14951. axis.tickInterval = Math.max(axis.pointRange, axis.tickInterval);
  14952. }
  14953. // Before normalizing the tick interval, handle minimum tick interval.
  14954. // This applies only if tickInterval is not defined.
  14955. minTickInterval = pick(options.minTickInterval,
  14956. // In datetime axes, don't go below the data interval, except when
  14957. // there are scatter-like series involved (#13369).
  14958. axis.dateTime &&
  14959. !axis.series.some(function (s) { return s.noSharedTooltip; }) ?
  14960. axis.closestPointRange : 0);
  14961. if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
  14962. axis.tickInterval = minTickInterval;
  14963. }
  14964. // for linear axes, get magnitude and normalize the interval
  14965. if (!axis.dateTime && !axis.logarithmic && !tickIntervalOption) {
  14966. axis.tickInterval = normalizeTickInterval(axis.tickInterval, void 0, getMagnitude(axis.tickInterval), pick(options.allowDecimals,
  14967. // If the tick interval is greather than 0.5, avoid
  14968. // decimals, as linear axes are often used to render
  14969. // discrete values. #3363. If a tick amount is set, allow
  14970. // decimals by default, as it increases the chances for a
  14971. // good fit.
  14972. axis.tickInterval < 0.5 || this.tickAmount !== void 0), !!this.tickAmount);
  14973. }
  14974. // Prevent ticks from getting so close that we can't draw the labels
  14975. if (!this.tickAmount) {
  14976. axis.tickInterval = axis.unsquish();
  14977. }
  14978. this.setTickPositions();
  14979. };
  14980. /**
  14981. * Now we have computed the normalized tickInterval, get the tick positions
  14982. *
  14983. * @function Highcharts.Axis#setTickPositions
  14984. *
  14985. * @fires Highcharts.Axis#event:afterSetTickPositions
  14986. */
  14987. Axis.prototype.setTickPositions = function () {
  14988. var axis = this, options = this.options, tickPositions, tickPositionsOption = options.tickPositions, minorTickIntervalOption = this.getMinorTickInterval(), tickPositioner = options.tickPositioner, hasVerticalPanning = this.hasVerticalPanning(), isColorAxis = this.coll === 'colorAxis', startOnTick = (isColorAxis || !hasVerticalPanning) && options.startOnTick, endOnTick = (isColorAxis || !hasVerticalPanning) && options.endOnTick;
  14989. // Set the tickmarkOffset
  14990. this.tickmarkOffset = (this.categories &&
  14991. options.tickmarkPlacement === 'between' &&
  14992. this.tickInterval === 1) ? 0.5 : 0; // #3202
  14993. // get minorTickInterval
  14994. this.minorTickInterval =
  14995. minorTickIntervalOption === 'auto' &&
  14996. this.tickInterval ?
  14997. this.tickInterval / 5 :
  14998. minorTickIntervalOption;
  14999. // When there is only one point, or all points have the same value on
  15000. // this axis, then min and max are equal and tickPositions.length is 0
  15001. // or 1. In this case, add some padding in order to center the point,
  15002. // but leave it with one tick. #1337.
  15003. this.single =
  15004. this.min === this.max &&
  15005. defined(this.min) &&
  15006. !this.tickAmount &&
  15007. (
  15008. // Data is on integer (#6563)
  15009. parseInt(this.min, 10) === this.min ||
  15010. // Between integers and decimals are not allowed (#6274)
  15011. options.allowDecimals !== false);
  15012. /**
  15013. * Contains the current positions that are laid out on the axis. The
  15014. * positions are numbers in terms of axis values. In a category axis
  15015. * they are integers, in a datetime axis they are also integers, but
  15016. * designating milliseconds.
  15017. *
  15018. * This property is read only - for modifying the tick positions, use
  15019. * the `tickPositioner` callback or [axis.tickPositions(
  15020. * https://api.highcharts.com/highcharts/xAxis.tickPositions) option
  15021. * instead.
  15022. *
  15023. * @name Highcharts.Axis#tickPositions
  15024. * @type {Highcharts.AxisTickPositionsArray|undefined}
  15025. */
  15026. this.tickPositions =
  15027. // Find the tick positions. Work on a copy (#1565)
  15028. tickPositions =
  15029. (tickPositionsOption && tickPositionsOption.slice());
  15030. if (!tickPositions) {
  15031. // Too many ticks (#6405). Create a friendly warning and provide two
  15032. // ticks so at least we can show the data series.
  15033. if ((!axis.ordinal || !axis.ordinal.positions) &&
  15034. ((this.max - this.min) /
  15035. this.tickInterval >
  15036. Math.max(2 * this.len, 200))) {
  15037. tickPositions = [this.min, this.max];
  15038. error(19, false, this.chart);
  15039. }
  15040. else if (axis.dateTime) {
  15041. tickPositions = axis.getTimeTicks(axis.dateTime.normalizeTimeTickInterval(this.tickInterval, options.units), this.min, this.max, options.startOfWeek, axis.ordinal && axis.ordinal.positions, this.closestPointRange, true);
  15042. }
  15043. else if (axis.logarithmic) {
  15044. tickPositions = axis.logarithmic.getLogTickPositions(this.tickInterval, this.min, this.max);
  15045. }
  15046. else {
  15047. tickPositions = this.getLinearTickPositions(this.tickInterval, this.min, this.max);
  15048. }
  15049. // Too dense ticks, keep only the first and last (#4477)
  15050. if (tickPositions.length > this.len) {
  15051. tickPositions = [tickPositions[0], tickPositions.pop()];
  15052. // Reduce doubled value (#7339)
  15053. if (tickPositions[0] === tickPositions[1]) {
  15054. tickPositions.length = 1;
  15055. }
  15056. }
  15057. this.tickPositions = tickPositions;
  15058. // Run the tick positioner callback, that allows modifying auto tick
  15059. // positions.
  15060. if (tickPositioner) {
  15061. tickPositioner = tickPositioner.apply(axis, [this.min, this.max]);
  15062. if (tickPositioner) {
  15063. this.tickPositions = tickPositions = tickPositioner;
  15064. }
  15065. }
  15066. }
  15067. // Reset min/max or remove extremes based on start/end on tick
  15068. this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
  15069. this.trimTicks(tickPositions, startOnTick, endOnTick);
  15070. if (!this.isLinked) {
  15071. // Substract half a unit (#2619, #2846, #2515, #3390),
  15072. // but not in case of multiple ticks (#6897)
  15073. if (this.single &&
  15074. tickPositions.length < 2 &&
  15075. !this.categories &&
  15076. !this.series.some(function (s) {
  15077. return (s.is('heatmap') && s.options.pointPlacement === 'between');
  15078. })) {
  15079. this.min -= 0.5;
  15080. this.max += 0.5;
  15081. }
  15082. if (!tickPositionsOption && !tickPositioner) {
  15083. this.adjustTickAmount();
  15084. }
  15085. }
  15086. fireEvent(this, 'afterSetTickPositions');
  15087. };
  15088. /**
  15089. * Handle startOnTick and endOnTick by either adapting to padding min/max or
  15090. * rounded min/max. Also handle single data points.
  15091. *
  15092. * @private
  15093. * @function Highcharts.Axis#trimTicks
  15094. *
  15095. * @param {Array<number>} tickPositions
  15096. * TO-DO: parameter description
  15097. *
  15098. * @param {boolean} [startOnTick]
  15099. * TO-DO: parameter description
  15100. *
  15101. * @param {boolean} [endOnTick]
  15102. * TO-DO: parameter description
  15103. */
  15104. Axis.prototype.trimTicks = function (tickPositions, startOnTick, endOnTick) {
  15105. var roundedMin = tickPositions[0], roundedMax = tickPositions[tickPositions.length - 1], minPointOffset = (!this.isOrdinal && this.minPointOffset) || 0; // (#12716)
  15106. fireEvent(this, 'trimTicks');
  15107. if (!this.isLinked) {
  15108. if (startOnTick && roundedMin !== -Infinity) { // #6502
  15109. this.min = roundedMin;
  15110. }
  15111. else {
  15112. while (this.min - minPointOffset > tickPositions[0]) {
  15113. tickPositions.shift();
  15114. }
  15115. }
  15116. if (endOnTick) {
  15117. this.max = roundedMax;
  15118. }
  15119. else {
  15120. while (this.max + minPointOffset <
  15121. tickPositions[tickPositions.length - 1]) {
  15122. tickPositions.pop();
  15123. }
  15124. }
  15125. // If no tick are left, set one tick in the middle (#3195)
  15126. if (tickPositions.length === 0 &&
  15127. defined(roundedMin) &&
  15128. !this.options.tickPositions) {
  15129. tickPositions.push((roundedMax + roundedMin) / 2);
  15130. }
  15131. }
  15132. };
  15133. /**
  15134. * Check if there are multiple axes in the same pane.
  15135. *
  15136. * @private
  15137. * @function Highcharts.Axis#alignToOthers
  15138. *
  15139. * @return {boolean|undefined}
  15140. * True if there are other axes.
  15141. */
  15142. Axis.prototype.alignToOthers = function () {
  15143. var axis = this, others = // Whether there is another axis to pair with this one
  15144. {}, hasOther, options = axis.options;
  15145. if (
  15146. // Only if alignTicks is true
  15147. this.chart.options.chart.alignTicks !== false &&
  15148. options.alignTicks !== false &&
  15149. // Disabled when startOnTick or endOnTick are false (#7604)
  15150. options.startOnTick !== false &&
  15151. options.endOnTick !== false &&
  15152. // Don't try to align ticks on a log axis, they are not evenly
  15153. // spaced (#6021)
  15154. !axis.logarithmic) {
  15155. this.chart[this.coll].forEach(function (axis) {
  15156. var otherOptions = axis.options, horiz = axis.horiz, key = [
  15157. horiz ? otherOptions.left : otherOptions.top,
  15158. otherOptions.width,
  15159. otherOptions.height,
  15160. otherOptions.pane
  15161. ].join(',');
  15162. if (axis.series.length) { // #4442
  15163. if (others[key]) {
  15164. hasOther = true; // #4201
  15165. }
  15166. else {
  15167. others[key] = 1;
  15168. }
  15169. }
  15170. });
  15171. }
  15172. return hasOther;
  15173. };
  15174. /**
  15175. * Find the max ticks of either the x and y axis collection, and record it
  15176. * in `this.tickAmount`.
  15177. *
  15178. * @private
  15179. * @function Highcharts.Axis#getTickAmount
  15180. */
  15181. Axis.prototype.getTickAmount = function () {
  15182. var axis = this, options = this.options, tickAmount = options.tickAmount, tickPixelInterval = options.tickPixelInterval;
  15183. if (!defined(options.tickInterval) &&
  15184. !tickAmount && this.len < tickPixelInterval &&
  15185. !this.isRadial &&
  15186. !axis.logarithmic &&
  15187. options.startOnTick &&
  15188. options.endOnTick) {
  15189. tickAmount = 2;
  15190. }
  15191. if (!tickAmount && this.alignToOthers()) {
  15192. // Add 1 because 4 tick intervals require 5 ticks (including first
  15193. // and last)
  15194. tickAmount = Math.ceil(this.len / tickPixelInterval) + 1;
  15195. }
  15196. // For tick amounts of 2 and 3, compute five ticks and remove the
  15197. // intermediate ones. This prevents the axis from adding ticks that are
  15198. // too far away from the data extremes.
  15199. if (tickAmount < 4) {
  15200. this.finalTickAmt = tickAmount;
  15201. tickAmount = 5;
  15202. }
  15203. this.tickAmount = tickAmount;
  15204. };
  15205. /**
  15206. * When using multiple axes, adjust the number of ticks to match the highest
  15207. * number of ticks in that group.
  15208. *
  15209. * @private
  15210. * @function Highcharts.Axis#adjustTickAmount
  15211. */
  15212. Axis.prototype.adjustTickAmount = function () {
  15213. var axis = this, axisOptions = axis.options, tickInterval = axis.tickInterval, tickPositions = axis.tickPositions, tickAmount = axis.tickAmount, finalTickAmt = axis.finalTickAmt, currentTickAmount = tickPositions && tickPositions.length, threshold = pick(axis.threshold, axis.softThreshold ? 0 : null), min, len, i;
  15214. if (axis.hasData()) {
  15215. if (currentTickAmount < tickAmount) {
  15216. min = axis.min;
  15217. while (tickPositions.length < tickAmount) {
  15218. // Extend evenly for both sides unless we're on the
  15219. // threshold (#3965)
  15220. if (tickPositions.length % 2 ||
  15221. min === threshold) {
  15222. // to the end
  15223. tickPositions.push(correctFloat(tickPositions[tickPositions.length - 1] +
  15224. tickInterval));
  15225. }
  15226. else {
  15227. // to the start
  15228. tickPositions.unshift(correctFloat(tickPositions[0] - tickInterval));
  15229. }
  15230. }
  15231. axis.transA *= (currentTickAmount - 1) / (tickAmount - 1);
  15232. // Do not crop when ticks are not extremes (#9841)
  15233. axis.min = axisOptions.startOnTick ?
  15234. tickPositions[0] :
  15235. Math.min(axis.min, tickPositions[0]);
  15236. axis.max = axisOptions.endOnTick ?
  15237. tickPositions[tickPositions.length - 1] :
  15238. Math.max(axis.max, tickPositions[tickPositions.length - 1]);
  15239. // We have too many ticks, run second pass to try to reduce ticks
  15240. }
  15241. else if (currentTickAmount > tickAmount) {
  15242. axis.tickInterval *= 2;
  15243. axis.setTickPositions();
  15244. }
  15245. // The finalTickAmt property is set in getTickAmount
  15246. if (defined(finalTickAmt)) {
  15247. i = len = tickPositions.length;
  15248. while (i--) {
  15249. if (
  15250. // Remove every other tick
  15251. (finalTickAmt === 3 && i % 2 === 1) ||
  15252. // Remove all but first and last
  15253. (finalTickAmt <= 2 && i > 0 && i < len - 1)) {
  15254. tickPositions.splice(i, 1);
  15255. }
  15256. }
  15257. axis.finalTickAmt = void 0;
  15258. }
  15259. }
  15260. };
  15261. /**
  15262. * Set the scale based on data min and max, user set min and max or options.
  15263. *
  15264. * @private
  15265. * @function Highcharts.Axis#setScale
  15266. *
  15267. * @fires Highcharts.Axis#event:afterSetScale
  15268. */
  15269. Axis.prototype.setScale = function () {
  15270. var axis = this, isDirtyAxisLength, isDirtyData = false, isXAxisDirty = false;
  15271. axis.series.forEach(function (series) {
  15272. var _a;
  15273. isDirtyData = isDirtyData || series.isDirtyData || series.isDirty;
  15274. // When x axis is dirty, we need new data extremes for y as
  15275. // well:
  15276. isXAxisDirty = isXAxisDirty || ((_a = series.xAxis) === null || _a === void 0 ? void 0 : _a.isDirty) || false;
  15277. });
  15278. axis.oldMin = axis.min;
  15279. axis.oldMax = axis.max;
  15280. axis.oldAxisLength = axis.len;
  15281. // set the new axisLength
  15282. axis.setAxisSize();
  15283. isDirtyAxisLength = axis.len !== axis.oldAxisLength;
  15284. // do we really need to go through all this?
  15285. if (isDirtyAxisLength ||
  15286. isDirtyData ||
  15287. isXAxisDirty ||
  15288. axis.isLinked ||
  15289. axis.forceRedraw ||
  15290. axis.userMin !== axis.oldUserMin ||
  15291. axis.userMax !== axis.oldUserMax ||
  15292. axis.alignToOthers()) {
  15293. if (axis.stacking) {
  15294. axis.stacking.resetStacks();
  15295. }
  15296. axis.forceRedraw = false;
  15297. // get data extremes if needed
  15298. axis.getSeriesExtremes();
  15299. // get fixed positions based on tickInterval
  15300. axis.setTickInterval();
  15301. // record old values to decide whether a rescale is necessary later
  15302. // on (#540)
  15303. axis.oldUserMin = axis.userMin;
  15304. axis.oldUserMax = axis.userMax;
  15305. // Mark as dirty if it is not already set to dirty and extremes have
  15306. // changed. #595.
  15307. if (!axis.isDirty) {
  15308. axis.isDirty =
  15309. isDirtyAxisLength ||
  15310. axis.min !== axis.oldMin ||
  15311. axis.max !== axis.oldMax;
  15312. }
  15313. }
  15314. else if (axis.stacking) {
  15315. axis.stacking.cleanStacks();
  15316. }
  15317. // Recalculate panning state object, when the data
  15318. // has changed. It is required when vertical panning is enabled.
  15319. if (isDirtyData && axis.panningState) {
  15320. axis.panningState.isDirty = true;
  15321. }
  15322. fireEvent(this, 'afterSetScale');
  15323. };
  15324. /**
  15325. * Set the minimum and maximum of the axes after render time. If the
  15326. * `startOnTick` and `endOnTick` options are true, the minimum and maximum
  15327. * values are rounded off to the nearest tick. To prevent this, these
  15328. * options can be set to false before calling setExtremes. Also, setExtremes
  15329. * will not allow a range lower than the `minRange` option, which by default
  15330. * is the range of five points.
  15331. *
  15332. * @sample highcharts/members/axis-setextremes/
  15333. * Set extremes from a button
  15334. * @sample highcharts/members/axis-setextremes-datetime/
  15335. * Set extremes on a datetime axis
  15336. * @sample highcharts/members/axis-setextremes-off-ticks/
  15337. * Set extremes off ticks
  15338. * @sample stock/members/axis-setextremes/
  15339. * Set extremes in Highstock
  15340. * @sample maps/members/axis-setextremes/
  15341. * Set extremes in Highmaps
  15342. *
  15343. * @function Highcharts.Axis#setExtremes
  15344. *
  15345. * @param {number} [newMin]
  15346. * The new minimum value.
  15347. *
  15348. * @param {number} [newMax]
  15349. * The new maximum value.
  15350. *
  15351. * @param {boolean} [redraw=true]
  15352. * Whether to redraw the chart or wait for an explicit call to
  15353. * {@link Highcharts.Chart#redraw}
  15354. *
  15355. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  15356. * Enable or modify animations.
  15357. *
  15358. * @param {*} [eventArguments]
  15359. * Arguments to be accessed in event handler.
  15360. *
  15361. * @fires Highcharts.Axis#event:setExtremes
  15362. */
  15363. Axis.prototype.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  15364. var axis = this, chart = axis.chart;
  15365. redraw = pick(redraw, true); // defaults to true
  15366. axis.series.forEach(function (serie) {
  15367. delete serie.kdTree;
  15368. });
  15369. // Extend the arguments with min and max
  15370. eventArguments = extend(eventArguments, {
  15371. min: newMin,
  15372. max: newMax
  15373. });
  15374. // Fire the event
  15375. fireEvent(axis, 'setExtremes', eventArguments, function () {
  15376. axis.userMin = newMin;
  15377. axis.userMax = newMax;
  15378. axis.eventArgs = eventArguments;
  15379. if (redraw) {
  15380. chart.redraw(animation);
  15381. }
  15382. });
  15383. };
  15384. /**
  15385. * Overridable method for zooming chart. Pulled out in a separate method to
  15386. * allow overriding in stock charts.
  15387. * @private
  15388. * @function Highcharts.Axis#zoom
  15389. *
  15390. * @param {number} newMin
  15391. * TO-DO: parameter description
  15392. *
  15393. * @param {number} newMax
  15394. * TO-DO: parameter description
  15395. *
  15396. * @return {boolean}
  15397. */
  15398. Axis.prototype.zoom = function (newMin, newMax) {
  15399. var 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 = {
  15400. newMin: newMin,
  15401. newMax: newMax
  15402. };
  15403. fireEvent(this, 'zoom', evt, function (e) {
  15404. // Use e.newMin and e.newMax - event handlers may have altered them
  15405. var newMin = e.newMin, newMax = e.newMax;
  15406. if (newMin !== axis.min || newMax !== axis.max) { // #5790
  15407. // Prevent pinch zooming out of range. Check for defined is for
  15408. // #1946. #1734.
  15409. if (!axis.allowZoomOutside) {
  15410. // #6014, sometimes newMax will be smaller than min (or
  15411. // newMin will be larger than max).
  15412. if (defined(dataMin)) {
  15413. if (newMin < min) {
  15414. newMin = min;
  15415. }
  15416. if (newMin > max) {
  15417. newMin = max;
  15418. }
  15419. }
  15420. if (defined(dataMax)) {
  15421. if (newMax < min) {
  15422. newMax = min;
  15423. }
  15424. if (newMax > max) {
  15425. newMax = max;
  15426. }
  15427. }
  15428. }
  15429. // In full view, displaying the reset zoom button is not
  15430. // required
  15431. axis.displayBtn = (typeof newMin !== 'undefined' ||
  15432. typeof newMax !== 'undefined');
  15433. // Do it
  15434. axis.setExtremes(newMin, newMax, false, void 0, { trigger: 'zoom' });
  15435. }
  15436. e.zoomed = true;
  15437. });
  15438. return evt.zoomed;
  15439. };
  15440. /**
  15441. * Update the axis metrics.
  15442. *
  15443. * @private
  15444. * @function Highcharts.Axis#setAxisSize
  15445. */
  15446. Axis.prototype.setAxisSize = function () {
  15447. var chart = this.chart, options = this.options,
  15448. // [top, right, bottom, left]
  15449. offsets = options.offsets || [0, 0, 0, 0], horiz = this.horiz,
  15450. // Check for percentage based input values. Rounding fixes problems
  15451. // with column overflow and plot line filtering (#4898, #4899)
  15452. 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));
  15453. // Expose basic values to use in Series object and navigator
  15454. this.bottom = chart.chartHeight - height - top;
  15455. this.right = chart.chartWidth - width - left;
  15456. // Direction agnostic properties
  15457. this.len = Math.max(horiz ? width : height, 0); // Math.max fixes #905
  15458. this.pos = horiz ? left : top; // distance from SVG origin
  15459. };
  15460. /**
  15461. * Get the current extremes for the axis.
  15462. *
  15463. * @sample highcharts/members/axis-getextremes/
  15464. * Report extremes by click on a button
  15465. * @sample maps/members/axis-getextremes/
  15466. * Get extremes in Highmaps
  15467. *
  15468. * @function Highcharts.Axis#getExtremes
  15469. *
  15470. * @return {Highcharts.ExtremesObject}
  15471. * An object containing extremes information.
  15472. */
  15473. Axis.prototype.getExtremes = function () {
  15474. var axis = this;
  15475. var log = axis.logarithmic;
  15476. return {
  15477. min: log ?
  15478. correctFloat(log.lin2log(axis.min)) :
  15479. axis.min,
  15480. max: log ?
  15481. correctFloat(log.lin2log(axis.max)) :
  15482. axis.max,
  15483. dataMin: axis.dataMin,
  15484. dataMax: axis.dataMax,
  15485. userMin: axis.userMin,
  15486. userMax: axis.userMax
  15487. };
  15488. };
  15489. /**
  15490. * Get the zero plane either based on zero or on the min or max value.
  15491. * Used in bar and area plots.
  15492. *
  15493. * @function Highcharts.Axis#getThreshold
  15494. *
  15495. * @param {number} threshold
  15496. * The threshold in axis values.
  15497. *
  15498. * @return {number|undefined}
  15499. * The translated threshold position in terms of pixels, and corrected to
  15500. * stay within the axis bounds.
  15501. */
  15502. Axis.prototype.getThreshold = function (threshold) {
  15503. var axis = this, log = axis.logarithmic, realMin = log ? log.lin2log(axis.min) : axis.min, realMax = log ? log.lin2log(axis.max) : axis.max;
  15504. if (threshold === null || threshold === -Infinity) {
  15505. threshold = realMin;
  15506. }
  15507. else if (threshold === Infinity) {
  15508. threshold = realMax;
  15509. }
  15510. else if (realMin > threshold) {
  15511. threshold = realMin;
  15512. }
  15513. else if (realMax < threshold) {
  15514. threshold = realMax;
  15515. }
  15516. return axis.translate(threshold, 0, 1, 0, 1);
  15517. };
  15518. /**
  15519. * Compute auto alignment for the axis label based on which side the axis is
  15520. * on and the given rotation for the label.
  15521. *
  15522. * @private
  15523. * @function Highcharts.Axis#autoLabelAlign
  15524. *
  15525. * @param {number} rotation
  15526. * The rotation in degrees as set by either the `rotation` or `autoRotation`
  15527. * options.
  15528. *
  15529. * @return {Highcharts.AlignValue}
  15530. * Can be `"center"`, `"left"` or `"right"`.
  15531. */
  15532. Axis.prototype.autoLabelAlign = function (rotation) {
  15533. var angle = (pick(rotation, 0) - (this.side * 90) + 720) % 360, evt = { align: 'center' };
  15534. fireEvent(this, 'autoLabelAlign', evt, function (e) {
  15535. if (angle > 15 && angle < 165) {
  15536. e.align = 'right';
  15537. }
  15538. else if (angle > 195 && angle < 345) {
  15539. e.align = 'left';
  15540. }
  15541. });
  15542. return evt.align;
  15543. };
  15544. /**
  15545. * Get the tick length and width for the axis based on axis options.
  15546. * @private
  15547. * @function Highcharts.Axis#tickSize
  15548. *
  15549. * @param {string} [prefix]
  15550. * 'tick' or 'minorTick'
  15551. *
  15552. * @return {Array<number,number>|undefined}
  15553. * An array of tickLength and tickWidth
  15554. */
  15555. Axis.prototype.tickSize = function (prefix) {
  15556. var options = this.options, tickLength = options[prefix === 'tick' ? 'tickLength' : 'minorTickLength'], tickWidth = pick(options[prefix === 'tick' ? 'tickWidth' : 'minorTickWidth'],
  15557. // Default to 1 on linear and datetime X axes
  15558. prefix === 'tick' && this.isXAxis && !this.categories ? 1 : 0), e, tickSize;
  15559. if (tickWidth && tickLength) {
  15560. // Negate the length
  15561. if (options[prefix + 'Position'] === 'inside') {
  15562. tickLength = -tickLength;
  15563. }
  15564. tickSize = [tickLength, tickWidth];
  15565. }
  15566. e = { tickSize: tickSize };
  15567. fireEvent(this, 'afterTickSize', e);
  15568. return e.tickSize;
  15569. };
  15570. /**
  15571. * Return the size of the labels.
  15572. *
  15573. * @private
  15574. * @function Highcharts.Axis#labelMetrics
  15575. *
  15576. * @return {Highcharts.FontMetricsObject}
  15577. */
  15578. Axis.prototype.labelMetrics = function () {
  15579. var index = this.tickPositions && this.tickPositions[0] || 0;
  15580. return this.chart.renderer.fontMetrics(this.options.labels.style &&
  15581. this.options.labels.style.fontSize, this.ticks[index] && this.ticks[index].label);
  15582. };
  15583. /**
  15584. * Prevent the ticks from getting so close we can't draw the labels. On a
  15585. * horizontal axis, this is handled by rotating the labels, removing ticks
  15586. * and adding ellipsis. On a vertical axis remove ticks and add ellipsis.
  15587. *
  15588. * @private
  15589. * @function Highcharts.Axis#unsquish
  15590. *
  15591. * @return {number}
  15592. */
  15593. Axis.prototype.unsquish = function () {
  15594. var labelOptions = this.options.labels, horiz = this.horiz, tickInterval = this.tickInterval, newTickInterval = tickInterval, slotSize = this.len / (((this.categories ? 1 : 0) +
  15595. this.max -
  15596. this.min) /
  15597. tickInterval), rotation, rotationOption = labelOptions.rotation, labelMetrics = this.labelMetrics(), step, bestScore = Number.MAX_VALUE, autoRotation, range = this.max - this.min,
  15598. // Return the multiple of tickInterval that is needed to avoid
  15599. // collision
  15600. getStep = function (spaceNeeded) {
  15601. var step = spaceNeeded / (slotSize || 1);
  15602. step = step > 1 ? Math.ceil(step) : 1;
  15603. // Guard for very small or negative angles (#9835)
  15604. if (step * tickInterval > range &&
  15605. spaceNeeded !== Infinity &&
  15606. slotSize !== Infinity &&
  15607. range) {
  15608. step = Math.ceil(range / tickInterval);
  15609. }
  15610. return correctFloat(step * tickInterval);
  15611. };
  15612. if (horiz) {
  15613. autoRotation = !labelOptions.staggerLines &&
  15614. !labelOptions.step &&
  15615. ( // #3971
  15616. defined(rotationOption) ?
  15617. [rotationOption] :
  15618. slotSize < pick(labelOptions.autoRotationLimit, 80) && labelOptions.autoRotation);
  15619. if (autoRotation) {
  15620. // Loop over the given autoRotation options, and determine
  15621. // which gives the best score. The best score is that with
  15622. // the lowest number of steps and a rotation closest
  15623. // to horizontal.
  15624. autoRotation.forEach(function (rot) {
  15625. var score;
  15626. if (rot === rotationOption ||
  15627. (rot && rot >= -90 && rot <= 90)) { // #3891
  15628. step = getStep(Math.abs(labelMetrics.h / Math.sin(deg2rad * rot)));
  15629. score = step + Math.abs(rot / 360);
  15630. if (score < bestScore) {
  15631. bestScore = score;
  15632. rotation = rot;
  15633. newTickInterval = step;
  15634. }
  15635. }
  15636. });
  15637. }
  15638. }
  15639. else if (!labelOptions.step) { // #4411
  15640. newTickInterval = getStep(labelMetrics.h);
  15641. }
  15642. this.autoRotation = autoRotation;
  15643. this.labelRotation = pick(rotation, rotationOption);
  15644. return newTickInterval;
  15645. };
  15646. /**
  15647. * Get the general slot width for labels/categories on this axis. This may
  15648. * change between the pre-render (from Axis.getOffset) and the final tick
  15649. * rendering and placement.
  15650. *
  15651. * @private
  15652. * @function Highcharts.Axis#getSlotWidth
  15653. *
  15654. * @param {Highcharts.Tick} [tick] Optionally, calculate the slot width
  15655. * basing on tick label. It is used in highcharts-3d module, where the slots
  15656. * has different widths depending on perspective angles.
  15657. *
  15658. * @return {number}
  15659. * The pixel width allocated to each axis label.
  15660. */
  15661. Axis.prototype.getSlotWidth = function (tick) {
  15662. var _a;
  15663. // #5086, #1580, #1931
  15664. var 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];
  15665. // Used by grid axis
  15666. if (tick && isNumber(tick.slotWidth)) { // #13221, can be 0
  15667. return tick.slotWidth;
  15668. }
  15669. if (horiz &&
  15670. labelOptions &&
  15671. (labelOptions.step || 0) < 2) {
  15672. if (labelOptions.rotation) { // #4415
  15673. return 0;
  15674. }
  15675. return ((this.staggerLines || 1) * this.len) / slotCount;
  15676. }
  15677. if (!horiz) {
  15678. // #7028
  15679. var cssWidth = (_a = labelOptions === null || labelOptions === void 0 ? void 0 : labelOptions.style) === null || _a === void 0 ? void 0 : _a.width;
  15680. if (cssWidth !== void 0) {
  15681. return parseInt(cssWidth, 10);
  15682. }
  15683. if (marginLeft) {
  15684. return marginLeft - chart.spacing[3];
  15685. }
  15686. }
  15687. // Last resort, a fraction of the available size
  15688. return chart.chartWidth * 0.33;
  15689. };
  15690. /**
  15691. * Render the axis labels and determine whether ellipsis or rotation need to
  15692. * be applied.
  15693. *
  15694. * @private
  15695. * @function Highcharts.Axis#renderUnsquish
  15696. */
  15697. Axis.prototype.renderUnsquish = function () {
  15698. var chart = this.chart, renderer = chart.renderer, tickPositions = this.tickPositions, ticks = this.ticks, labelOptions = this.options.labels, labelStyleOptions = (labelOptions && labelOptions.style || {}), horiz = this.horiz, slotWidth = this.getSlotWidth(), innerWidth = Math.max(1, Math.round(slotWidth - 2 * (labelOptions.padding || 5))), attr = {}, labelMetrics = this.labelMetrics(), textOverflowOption = (labelOptions.style &&
  15699. labelOptions.style.textOverflow), commonWidth, commonTextOverflow, maxLabelLength = 0, label, i, pos;
  15700. // Set rotation option unless it is "auto", like in gauges
  15701. if (!isString(labelOptions.rotation)) {
  15702. // #4443:
  15703. attr.rotation = labelOptions.rotation || 0;
  15704. }
  15705. // Get the longest label length
  15706. tickPositions.forEach(function (tick) {
  15707. tick = ticks[tick];
  15708. // Replace label - sorting animation
  15709. if (tick.movedLabel) {
  15710. tick.replaceMovedLabel();
  15711. }
  15712. if (tick &&
  15713. tick.label &&
  15714. tick.label.textPxLength > maxLabelLength) {
  15715. maxLabelLength = tick.label.textPxLength;
  15716. }
  15717. });
  15718. this.maxLabelLength = maxLabelLength;
  15719. // Handle auto rotation on horizontal axis
  15720. if (this.autoRotation) {
  15721. // Apply rotation only if the label is too wide for the slot, and
  15722. // the label is wider than its height.
  15723. if (maxLabelLength > innerWidth &&
  15724. maxLabelLength > labelMetrics.h) {
  15725. attr.rotation = this.labelRotation;
  15726. }
  15727. else {
  15728. this.labelRotation = 0;
  15729. }
  15730. // Handle word-wrap or ellipsis on vertical axis
  15731. }
  15732. else if (slotWidth) {
  15733. // For word-wrap or ellipsis
  15734. commonWidth = innerWidth;
  15735. if (!textOverflowOption) {
  15736. commonTextOverflow = 'clip';
  15737. // On vertical axis, only allow word wrap if there is room
  15738. // for more lines.
  15739. i = tickPositions.length;
  15740. while (!horiz && i--) {
  15741. pos = tickPositions[i];
  15742. label = ticks[pos].label;
  15743. if (label) {
  15744. // Reset ellipsis in order to get the correct
  15745. // bounding box (#4070)
  15746. if (label.styles &&
  15747. label.styles.textOverflow === 'ellipsis') {
  15748. label.css({ textOverflow: 'clip' });
  15749. // Set the correct width in order to read
  15750. // the bounding box height (#4678, #5034)
  15751. }
  15752. else if (label.textPxLength > slotWidth) {
  15753. label.css({ width: slotWidth + 'px' });
  15754. }
  15755. if (label.getBBox().height > (this.len / tickPositions.length -
  15756. (labelMetrics.h - labelMetrics.f))) {
  15757. label.specificTextOverflow = 'ellipsis';
  15758. }
  15759. }
  15760. }
  15761. }
  15762. }
  15763. // Add ellipsis if the label length is significantly longer than ideal
  15764. if (attr.rotation) {
  15765. commonWidth = (maxLabelLength > chart.chartHeight * 0.5 ?
  15766. chart.chartHeight * 0.33 :
  15767. maxLabelLength);
  15768. if (!textOverflowOption) {
  15769. commonTextOverflow = 'ellipsis';
  15770. }
  15771. }
  15772. // Set the explicit or automatic label alignment
  15773. this.labelAlign = labelOptions.align ||
  15774. this.autoLabelAlign(this.labelRotation);
  15775. if (this.labelAlign) {
  15776. attr.align = this.labelAlign;
  15777. }
  15778. // Apply general and specific CSS
  15779. tickPositions.forEach(function (pos) {
  15780. var tick = ticks[pos], label = tick && tick.label, widthOption = labelStyleOptions.width, css = {};
  15781. if (label) {
  15782. // This needs to go before the CSS in old IE (#4502)
  15783. label.attr(attr);
  15784. if (tick.shortenLabel) {
  15785. tick.shortenLabel();
  15786. }
  15787. else if (commonWidth &&
  15788. !widthOption &&
  15789. // Setting width in this case messes with the bounding box
  15790. // (#7975)
  15791. labelStyleOptions.whiteSpace !== 'nowrap' &&
  15792. (
  15793. // Speed optimizing, #7656
  15794. commonWidth < label.textPxLength ||
  15795. // Resetting CSS, #4928
  15796. label.element.tagName === 'SPAN')) {
  15797. css.width = commonWidth + 'px';
  15798. if (!textOverflowOption) {
  15799. css.textOverflow = (label.specificTextOverflow ||
  15800. commonTextOverflow);
  15801. }
  15802. label.css(css);
  15803. // Reset previously shortened label (#8210)
  15804. }
  15805. else if (label.styles &&
  15806. label.styles.width &&
  15807. !css.width &&
  15808. !widthOption) {
  15809. label.css({ width: null });
  15810. }
  15811. delete label.specificTextOverflow;
  15812. tick.rotation = attr.rotation;
  15813. }
  15814. }, this);
  15815. // Note: Why is this not part of getLabelPosition?
  15816. this.tickRotCorr = renderer.rotCorr(labelMetrics.b, this.labelRotation || 0, this.side !== 0);
  15817. };
  15818. /**
  15819. * Return true if the axis has associated data.
  15820. *
  15821. * @function Highcharts.Axis#hasData
  15822. *
  15823. * @return {boolean}
  15824. * True if the axis has associated visible series and those series have
  15825. * either valid data points or explicit `min` and `max` settings.
  15826. */
  15827. Axis.prototype.hasData = function () {
  15828. return this.series.some(function (s) {
  15829. return s.hasData();
  15830. }) ||
  15831. (this.options.showEmpty &&
  15832. defined(this.min) &&
  15833. defined(this.max));
  15834. };
  15835. /**
  15836. * Adds the title defined in axis.options.title.
  15837. *
  15838. * @function Highcharts.Axis#addTitle
  15839. *
  15840. * @param {boolean} [display]
  15841. * Whether or not to display the title.
  15842. */
  15843. Axis.prototype.addTitle = function (display) {
  15844. var axis = this, renderer = axis.chart.renderer, horiz = axis.horiz, opposite = axis.opposite, options = axis.options, axisTitleOptions = options.title, textAlign, styledMode = axis.chart.styledMode;
  15845. if (!axis.axisTitle) {
  15846. textAlign = axisTitleOptions.textAlign;
  15847. if (!textAlign) {
  15848. textAlign = (horiz ? {
  15849. low: 'left',
  15850. middle: 'center',
  15851. high: 'right'
  15852. } : {
  15853. low: opposite ? 'right' : 'left',
  15854. middle: 'center',
  15855. high: opposite ? 'left' : 'right'
  15856. })[axisTitleOptions.align];
  15857. }
  15858. axis.axisTitle = renderer
  15859. .text(axisTitleOptions.text, 0, 0, axisTitleOptions.useHTML)
  15860. .attr({
  15861. zIndex: 7,
  15862. rotation: axisTitleOptions.rotation || 0,
  15863. align: textAlign
  15864. })
  15865. .addClass('highcharts-axis-title');
  15866. // #7814, don't mutate style option
  15867. if (!styledMode) {
  15868. axis.axisTitle.css(merge(axisTitleOptions.style));
  15869. }
  15870. axis.axisTitle.add(axis.axisGroup);
  15871. axis.axisTitle.isNew = true;
  15872. }
  15873. // Max width defaults to the length of the axis
  15874. if (!styledMode &&
  15875. !axisTitleOptions.style.width &&
  15876. !axis.isRadial) {
  15877. axis.axisTitle.css({
  15878. width: axis.len + 'px'
  15879. });
  15880. }
  15881. // hide or show the title depending on whether showEmpty is set
  15882. axis.axisTitle[display ? 'show' : 'hide'](display);
  15883. };
  15884. /**
  15885. * Generates a tick for initial positioning.
  15886. *
  15887. * @private
  15888. * @function Highcharts.Axis#generateTick
  15889. *
  15890. * @param {number} pos
  15891. * The tick position in axis values.
  15892. *
  15893. * @param {number} [i]
  15894. * The index of the tick in {@link Axis.tickPositions}.
  15895. */
  15896. Axis.prototype.generateTick = function (pos) {
  15897. var axis = this;
  15898. var ticks = axis.ticks;
  15899. if (!ticks[pos]) {
  15900. ticks[pos] = new Tick(axis, pos);
  15901. }
  15902. else {
  15903. ticks[pos].addLabel(); // update labels depending on tick interval
  15904. }
  15905. };
  15906. /**
  15907. * Render the tick labels to a preliminary position to get their sizes
  15908. *
  15909. * @private
  15910. * @function Highcharts.Axis#getOffset
  15911. *
  15912. * @fires Highcharts.Axis#event:afterGetOffset
  15913. */
  15914. Axis.prototype.getOffset = function () {
  15915. var axis = this, chart = axis.chart, renderer = chart.renderer, options = axis.options, tickPositions = axis.tickPositions, ticks = axis.ticks, horiz = axis.horiz, side = axis.side, invertedSide = chart.inverted &&
  15916. !axis.isZAxis ? [1, 0, 3, 2][side] : side, hasData, showAxis, titleOffset = 0, titleOffsetOption, titleMargin = 0, axisTitleOptions = options.title, labelOptions = options.labels, labelOffset = 0, // reset
  15917. labelOffsetPadded, axisOffset = chart.axisOffset, clipOffset = chart.clipOffset, clip, directionFactor = [-1, 1, 1, -1][side], className = options.className, axisParent = axis.axisParent, // Used in color axis
  15918. lineHeightCorrection;
  15919. // For reuse in Axis.render
  15920. hasData = axis.hasData();
  15921. axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
  15922. // Set/reset staggerLines
  15923. axis.staggerLines = axis.horiz && labelOptions.staggerLines;
  15924. // Create the axisGroup and gridGroup elements on first iteration
  15925. if (!axis.axisGroup) {
  15926. axis.gridGroup = renderer.g('grid')
  15927. .attr({ zIndex: options.gridZIndex || 1 })
  15928. .addClass('highcharts-' + this.coll.toLowerCase() + '-grid ' +
  15929. (className || ''))
  15930. .add(axisParent);
  15931. axis.axisGroup = renderer.g('axis')
  15932. .attr({ zIndex: options.zIndex || 2 })
  15933. .addClass('highcharts-' + this.coll.toLowerCase() + ' ' +
  15934. (className || ''))
  15935. .add(axisParent);
  15936. axis.labelGroup = renderer.g('axis-labels')
  15937. .attr({ zIndex: labelOptions.zIndex || 7 })
  15938. .addClass('highcharts-' + axis.coll.toLowerCase() + '-labels ' +
  15939. (className || ''))
  15940. .add(axisParent);
  15941. }
  15942. if (hasData || axis.isLinked) {
  15943. // Generate ticks
  15944. tickPositions.forEach(function (pos, i) {
  15945. // i is not used here, but may be used in overrides
  15946. axis.generateTick(pos, i);
  15947. });
  15948. axis.renderUnsquish();
  15949. // Left side must be align: right and right side must
  15950. // have align: left for labels
  15951. axis.reserveSpaceDefault = (side === 0 ||
  15952. side === 2 ||
  15953. { 1: 'left', 3: 'right' }[side] === axis.labelAlign);
  15954. if (pick(labelOptions.reserveSpace, axis.labelAlign === 'center' ? true : null, axis.reserveSpaceDefault)) {
  15955. tickPositions.forEach(function (pos) {
  15956. // get the highest offset
  15957. labelOffset = Math.max(ticks[pos].getLabelSize(), labelOffset);
  15958. });
  15959. }
  15960. if (axis.staggerLines) {
  15961. labelOffset *= axis.staggerLines;
  15962. }
  15963. axis.labelOffset = labelOffset * (axis.opposite ? -1 : 1);
  15964. }
  15965. else { // doesn't have data
  15966. objectEach(ticks, function (tick, n) {
  15967. tick.destroy();
  15968. delete ticks[n];
  15969. });
  15970. }
  15971. if (axisTitleOptions &&
  15972. axisTitleOptions.text &&
  15973. axisTitleOptions.enabled !== false) {
  15974. axis.addTitle(showAxis);
  15975. if (showAxis && axisTitleOptions.reserveSpace !== false) {
  15976. axis.titleOffset = titleOffset =
  15977. axis.axisTitle.getBBox()[horiz ? 'height' : 'width'];
  15978. titleOffsetOption = axisTitleOptions.offset;
  15979. titleMargin = defined(titleOffsetOption) ?
  15980. 0 :
  15981. pick(axisTitleOptions.margin, horiz ? 5 : 10);
  15982. }
  15983. }
  15984. // Render the axis line
  15985. axis.renderLine();
  15986. // handle automatic or user set offset
  15987. axis.offset = directionFactor * pick(options.offset, axisOffset[side] ? axisOffset[side] + (options.margin || 0) : 0);
  15988. axis.tickRotCorr = axis.tickRotCorr || { x: 0, y: 0 }; // polar
  15989. if (side === 0) {
  15990. lineHeightCorrection = -axis.labelMetrics().h;
  15991. }
  15992. else if (side === 2) {
  15993. lineHeightCorrection = axis.tickRotCorr.y;
  15994. }
  15995. else {
  15996. lineHeightCorrection = 0;
  15997. }
  15998. // Find the padded label offset
  15999. labelOffsetPadded = Math.abs(labelOffset) + titleMargin;
  16000. if (labelOffset) {
  16001. labelOffsetPadded -= lineHeightCorrection;
  16002. labelOffsetPadded += directionFactor * (horiz ?
  16003. pick(labelOptions.y, axis.tickRotCorr.y + directionFactor * 8) :
  16004. labelOptions.x);
  16005. }
  16006. axis.axisTitleMargin = pick(titleOffsetOption, labelOffsetPadded);
  16007. if (axis.getMaxLabelDimensions) {
  16008. axis.maxLabelDimensions = axis.getMaxLabelDimensions(ticks, tickPositions);
  16009. }
  16010. // Due to GridAxis.tickSize, tickSize should be calculated after ticks
  16011. // has rendered.
  16012. var tickSize = this.tickSize('tick');
  16013. axisOffset[side] = Math.max(axisOffset[side], axis.axisTitleMargin + titleOffset +
  16014. directionFactor * axis.offset, labelOffsetPadded, // #3027
  16015. tickPositions && tickPositions.length && tickSize ?
  16016. tickSize[0] + directionFactor * axis.offset :
  16017. 0 // #4866
  16018. );
  16019. // Decide the clipping needed to keep the graph inside
  16020. // the plot area and axis lines
  16021. clip = options.offset ?
  16022. 0 :
  16023. // #4308, #4371:
  16024. Math.floor(axis.axisLine.strokeWidth() / 2) * 2;
  16025. clipOffset[invertedSide] =
  16026. Math.max(clipOffset[invertedSide], clip);
  16027. fireEvent(this, 'afterGetOffset');
  16028. };
  16029. /**
  16030. * Internal function to get the path for the axis line. Extended for polar
  16031. * charts.
  16032. *
  16033. * @function Highcharts.Axis#getLinePath
  16034. *
  16035. * @param {number} lineWidth
  16036. * The line width in pixels.
  16037. *
  16038. * @return {Highcharts.SVGPathArray}
  16039. * The SVG path definition in array form.
  16040. */
  16041. Axis.prototype.getLinePath = function (lineWidth) {
  16042. var 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 -
  16043. (opposite ? this.height : 0) + offset;
  16044. if (opposite) {
  16045. lineWidth *= -1; // crispify the other way - #1480, #1687
  16046. }
  16047. return chart.renderer
  16048. .crispLine([
  16049. [
  16050. 'M',
  16051. horiz ?
  16052. this.left :
  16053. lineLeft,
  16054. horiz ?
  16055. lineTop :
  16056. this.top
  16057. ],
  16058. [
  16059. 'L',
  16060. horiz ?
  16061. chart.chartWidth - this.right :
  16062. lineLeft,
  16063. horiz ?
  16064. lineTop :
  16065. chart.chartHeight - this.bottom
  16066. ]
  16067. ], lineWidth);
  16068. };
  16069. /**
  16070. * Render the axis line. Called internally when rendering and redrawing the
  16071. * axis.
  16072. *
  16073. * @function Highcharts.Axis#renderLine
  16074. */
  16075. Axis.prototype.renderLine = function () {
  16076. if (!this.axisLine) {
  16077. this.axisLine = this.chart.renderer.path()
  16078. .addClass('highcharts-axis-line')
  16079. .add(this.axisGroup);
  16080. if (!this.chart.styledMode) {
  16081. this.axisLine.attr({
  16082. stroke: this.options.lineColor,
  16083. 'stroke-width': this.options.lineWidth,
  16084. zIndex: 7
  16085. });
  16086. }
  16087. }
  16088. };
  16089. /**
  16090. * Position the axis title.
  16091. *
  16092. * @private
  16093. * @function Highcharts.Axis#getTitlePosition
  16094. *
  16095. * @return {Highcharts.PositionObject}
  16096. * X and Y positions for the title.
  16097. */
  16098. Axis.prototype.getTitlePosition = function () {
  16099. // compute anchor points for each of the title align options
  16100. var 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 || 0, yOption = axisTitleOptions.y || 0, axisTitle = this.axisTitle, fontMetrics = this.chart.renderer.fontMetrics(axisTitleOptions.style &&
  16101. axisTitleOptions.style.fontSize, axisTitle),
  16102. // The part of a multiline text that is below the baseline of the
  16103. // first line. Subtract 1 to preserve pixel-perfectness from the
  16104. // old behaviour (v5.0.12), where only one line was allowed.
  16105. textHeightOvershoot = Math.max(axisTitle.getBBox(null, 0).height - fontMetrics.h - 1, 0),
  16106. // the position in the length direction of the axis
  16107. alongAxis = {
  16108. low: margin + (horiz ? 0 : axisLength),
  16109. middle: margin + axisLength / 2,
  16110. high: margin + (horiz ? axisLength : 0)
  16111. }[axisTitleOptions.align],
  16112. // the position in the perpendicular direction of the axis
  16113. offAxis = (horiz ? axisTop + this.height : axisLeft) +
  16114. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  16115. (opposite ? -1 : 1) * // so does opposite axes
  16116. this.axisTitleMargin +
  16117. [
  16118. -textHeightOvershoot,
  16119. textHeightOvershoot,
  16120. fontMetrics.f,
  16121. -textHeightOvershoot // left
  16122. ][this.side], titlePosition = {
  16123. x: horiz ?
  16124. alongAxis + xOption :
  16125. offAxis + (opposite ? this.width : 0) + offset + xOption,
  16126. y: horiz ?
  16127. offAxis + yOption - (opposite ? this.height : 0) + offset :
  16128. alongAxis + yOption
  16129. };
  16130. fireEvent(this, 'afterGetTitlePosition', { titlePosition: titlePosition });
  16131. return titlePosition;
  16132. };
  16133. /**
  16134. * Render a minor tick into the given position. If a minor tick already
  16135. * exists in this position, move it.
  16136. *
  16137. * @function Highcharts.Axis#renderMinorTick
  16138. *
  16139. * @param {number} pos
  16140. * The position in axis values.
  16141. */
  16142. Axis.prototype.renderMinorTick = function (pos) {
  16143. var axis = this;
  16144. var slideInTicks = axis.chart.hasRendered && isNumber(axis.oldMin);
  16145. var minorTicks = axis.minorTicks;
  16146. if (!minorTicks[pos]) {
  16147. minorTicks[pos] = new Tick(axis, pos, 'minor');
  16148. }
  16149. // Render new ticks in old position
  16150. if (slideInTicks && minorTicks[pos].isNew) {
  16151. minorTicks[pos].render(null, true);
  16152. }
  16153. minorTicks[pos].render(null, false, 1);
  16154. };
  16155. /**
  16156. * Render a major tick into the given position. If a tick already exists
  16157. * in this position, move it.
  16158. *
  16159. * @function Highcharts.Axis#renderTick
  16160. *
  16161. * @param {number} pos
  16162. * The position in axis values.
  16163. *
  16164. * @param {number} i
  16165. * The tick index.
  16166. */
  16167. Axis.prototype.renderTick = function (pos, i) {
  16168. var axis = this;
  16169. var isLinked = axis.isLinked;
  16170. var ticks = axis.ticks;
  16171. var slideInTicks = axis.chart.hasRendered && isNumber(axis.oldMin);
  16172. // Linked axes need an extra check to find out if
  16173. if (!isLinked ||
  16174. (pos >= axis.min && pos <= axis.max)) {
  16175. if (!ticks[pos]) {
  16176. ticks[pos] = new Tick(axis, pos);
  16177. }
  16178. // NOTE this seems like overkill. Could be handled in tick.render by
  16179. // setting old position in attr, then set new position in animate.
  16180. // render new ticks in old position
  16181. if (slideInTicks && ticks[pos].isNew) {
  16182. // Start with negative opacity so that it is visible from
  16183. // halfway into the animation
  16184. ticks[pos].render(i, true, -1);
  16185. }
  16186. ticks[pos].render(i);
  16187. }
  16188. };
  16189. /**
  16190. * Render the axis.
  16191. *
  16192. * @private
  16193. * @function Highcharts.Axis#render
  16194. *
  16195. * @fires Highcharts.Axis#event:afterRender
  16196. */
  16197. Axis.prototype.render = function () {
  16198. var 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, tickmarkOffset = axis.tickmarkOffset, axisLine = axis.axisLine, showAxis = axis.showAxis, animation = animObject(renderer.globalAnimation), from, to;
  16199. // Reset
  16200. axis.labelEdge.length = 0;
  16201. axis.overlap = false;
  16202. // Mark all elements inActive before we go over and mark the active ones
  16203. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  16204. objectEach(coll, function (tick) {
  16205. tick.isActive = false;
  16206. });
  16207. });
  16208. // If the series has data draw the ticks. Else only the line and title
  16209. if (axis.hasData() || isLinked) {
  16210. // minor ticks
  16211. if (axis.minorTickInterval && !axis.categories) {
  16212. axis.getMinorTickPositions().forEach(function (pos) {
  16213. axis.renderMinorTick(pos);
  16214. });
  16215. }
  16216. // Major ticks. Pull out the first item and render it last so that
  16217. // we can get the position of the neighbour label. #808.
  16218. if (tickPositions.length) { // #1300
  16219. tickPositions.forEach(function (pos, i) {
  16220. axis.renderTick(pos, i);
  16221. });
  16222. // In a categorized axis, the tick marks are displayed
  16223. // between labels. So we need to add a tick mark and
  16224. // grid line at the left edge of the X axis.
  16225. if (tickmarkOffset && (axis.min === 0 || axis.single)) {
  16226. if (!ticks[-1]) {
  16227. ticks[-1] = new Tick(axis, -1, null, true);
  16228. }
  16229. ticks[-1].render(-1);
  16230. }
  16231. }
  16232. // alternate grid color
  16233. if (alternateGridColor) {
  16234. tickPositions.forEach(function (pos, i) {
  16235. to = typeof tickPositions[i + 1] !== 'undefined' ?
  16236. tickPositions[i + 1] + tickmarkOffset :
  16237. axis.max - tickmarkOffset;
  16238. if (i % 2 === 0 &&
  16239. pos < axis.max &&
  16240. to <= axis.max + (chart.polar ?
  16241. -tickmarkOffset :
  16242. tickmarkOffset)) { // #2248, #4660
  16243. if (!alternateBands[pos]) {
  16244. // Should be imported from PlotLineOrBand.js, but
  16245. // the dependency cycle with axis is a problem
  16246. alternateBands[pos] = new H.PlotLineOrBand(axis);
  16247. }
  16248. from = pos + tickmarkOffset; // #949
  16249. alternateBands[pos].options = {
  16250. from: log ? log.lin2log(from) : from,
  16251. to: log ? log.lin2log(to) : to,
  16252. color: alternateGridColor,
  16253. className: 'highcharts-alternate-grid'
  16254. };
  16255. alternateBands[pos].render();
  16256. alternateBands[pos].isActive = true;
  16257. }
  16258. });
  16259. }
  16260. // custom plot lines and bands
  16261. if (!axis._addedPlotLB) { // only first time
  16262. (options.plotLines || [])
  16263. .concat(options.plotBands || [])
  16264. .forEach(function (plotLineOptions) {
  16265. axis.addPlotBandOrLine(plotLineOptions);
  16266. });
  16267. axis._addedPlotLB = true;
  16268. }
  16269. } // end if hasData
  16270. // Remove inactive ticks
  16271. [ticks, minorTicks, alternateBands].forEach(function (coll) {
  16272. var i, forDestruction = [], delay = animation.duration, destroyInactiveItems = function () {
  16273. i = forDestruction.length;
  16274. while (i--) {
  16275. // When resizing rapidly, the same items
  16276. // may be destroyed in different timeouts,
  16277. // or the may be reactivated
  16278. if (coll[forDestruction[i]] &&
  16279. !coll[forDestruction[i]].isActive) {
  16280. coll[forDestruction[i]].destroy();
  16281. delete coll[forDestruction[i]];
  16282. }
  16283. }
  16284. };
  16285. objectEach(coll, function (tick, pos) {
  16286. if (!tick.isActive) {
  16287. // Render to zero opacity
  16288. tick.render(pos, false, 0);
  16289. tick.isActive = false;
  16290. forDestruction.push(pos);
  16291. }
  16292. });
  16293. // When the objects are finished fading out, destroy them
  16294. syncTimeout(destroyInactiveItems, coll === alternateBands ||
  16295. !chart.hasRendered ||
  16296. !delay ?
  16297. 0 :
  16298. delay);
  16299. });
  16300. // Set the axis line path
  16301. if (axisLine) {
  16302. axisLine[axisLine.isPlaced ? 'animate' : 'attr']({
  16303. d: this.getLinePath(axisLine.strokeWidth())
  16304. });
  16305. axisLine.isPlaced = true;
  16306. // Show or hide the line depending on options.showEmpty
  16307. axisLine[showAxis ? 'show' : 'hide'](showAxis);
  16308. }
  16309. if (axisTitle && showAxis) {
  16310. var titleXy = axis.getTitlePosition();
  16311. if (isNumber(titleXy.y)) {
  16312. axisTitle[axisTitle.isNew ? 'attr' : 'animate'](titleXy);
  16313. axisTitle.isNew = false;
  16314. }
  16315. else {
  16316. axisTitle.attr('y', -9999);
  16317. axisTitle.isNew = true;
  16318. }
  16319. }
  16320. // Stacked totals:
  16321. if (stackLabelOptions && stackLabelOptions.enabled && axis.stacking) {
  16322. axis.stacking.renderStackTotals();
  16323. }
  16324. // End stacked totals
  16325. axis.isDirty = false;
  16326. fireEvent(this, 'afterRender');
  16327. };
  16328. /**
  16329. * Redraw the axis to reflect changes in the data or axis extremes. Called
  16330. * internally from Highcharts.Chart#redraw.
  16331. *
  16332. * @private
  16333. * @function Highcharts.Axis#redraw
  16334. */
  16335. Axis.prototype.redraw = function () {
  16336. if (this.visible) {
  16337. // render the axis
  16338. this.render();
  16339. // move plot lines and bands
  16340. this.plotLinesAndBands.forEach(function (plotLine) {
  16341. plotLine.render();
  16342. });
  16343. }
  16344. // mark associated series as dirty and ready for redraw
  16345. this.series.forEach(function (series) {
  16346. series.isDirty = true;
  16347. });
  16348. };
  16349. /**
  16350. * Returns an array of axis properties, that should be untouched during
  16351. * reinitialization.
  16352. *
  16353. * @private
  16354. * @function Highcharts.Axis#getKeepProps
  16355. *
  16356. * @return {Array<string>}
  16357. */
  16358. Axis.prototype.getKeepProps = function () {
  16359. return (this.keepProps || Axis.keepProps);
  16360. };
  16361. /**
  16362. * Destroys an Axis instance. See {@link Axis#remove} for the API endpoint
  16363. * to fully remove the axis.
  16364. *
  16365. * @private
  16366. * @function Highcharts.Axis#destroy
  16367. *
  16368. * @param {boolean} [keepEvents]
  16369. * Whether to preserve events, used internally in Axis.update.
  16370. */
  16371. Axis.prototype.destroy = function (keepEvents) {
  16372. var axis = this, plotLinesAndBands = axis.plotLinesAndBands, plotGroup, i;
  16373. fireEvent(this, 'destroy', { keepEvents: keepEvents });
  16374. // Remove the events
  16375. if (!keepEvents) {
  16376. removeEvent(axis);
  16377. }
  16378. // Destroy collections
  16379. [axis.ticks, axis.minorTicks, axis.alternateBands].forEach(function (coll) {
  16380. destroyObjectProperties(coll);
  16381. });
  16382. if (plotLinesAndBands) {
  16383. i = plotLinesAndBands.length;
  16384. while (i--) { // #1975
  16385. plotLinesAndBands[i].destroy();
  16386. }
  16387. }
  16388. // Destroy elements
  16389. ['axisLine', 'axisTitle', 'axisGroup',
  16390. 'gridGroup', 'labelGroup', 'cross', 'scrollbar'].forEach(function (prop) {
  16391. if (axis[prop]) {
  16392. axis[prop] = axis[prop].destroy();
  16393. }
  16394. });
  16395. // Destroy each generated group for plotlines and plotbands
  16396. for (plotGroup in axis.plotLinesAndBandsGroups) { // eslint-disable-line guard-for-in
  16397. axis.plotLinesAndBandsGroups[plotGroup] =
  16398. axis.plotLinesAndBandsGroups[plotGroup].destroy();
  16399. }
  16400. // Delete all properties and fall back to the prototype.
  16401. objectEach(axis, function (val, key) {
  16402. if (axis.getKeepProps().indexOf(key) === -1) {
  16403. delete axis[key];
  16404. }
  16405. });
  16406. };
  16407. /**
  16408. * Internal function to draw a crosshair.
  16409. *
  16410. * @function Highcharts.Axis#drawCrosshair
  16411. *
  16412. * @param {Highcharts.PointerEventObject} [e]
  16413. * The event arguments from the modified pointer event, extended with
  16414. * `chartX` and `chartY`
  16415. *
  16416. * @param {Highcharts.Point} [point]
  16417. * The Point object if the crosshair snaps to points.
  16418. *
  16419. * @fires Highcharts.Axis#event:afterDrawCrosshair
  16420. * @fires Highcharts.Axis#event:drawCrosshair
  16421. */
  16422. Axis.prototype.drawCrosshair = function (e, point) {
  16423. var path, options = this.crosshair, snap = pick(options.snap, true), pos, categorized, graphic = this.cross, crossOptions, chart = this.chart;
  16424. fireEvent(this, 'drawCrosshair', { e: e, point: point });
  16425. // Use last available event when updating non-snapped crosshairs without
  16426. // mouse interaction (#5287)
  16427. if (!e) {
  16428. e = this.cross && this.cross.e;
  16429. }
  16430. if (
  16431. // Disabled in options
  16432. !this.crosshair ||
  16433. // Snap
  16434. ((defined(point) || !snap) === false)) {
  16435. this.hideCrosshair();
  16436. }
  16437. else {
  16438. // Get the path
  16439. if (!snap) {
  16440. pos = e &&
  16441. (this.horiz ?
  16442. e.chartX - this.pos :
  16443. this.len - e.chartY + this.pos);
  16444. }
  16445. else if (defined(point)) {
  16446. // #3834
  16447. pos = pick(this.coll !== 'colorAxis' ?
  16448. point.crosshairPos : // 3D axis extension
  16449. null, this.isXAxis ?
  16450. point.plotX :
  16451. this.len - point.plotY);
  16452. }
  16453. if (defined(pos)) {
  16454. crossOptions = {
  16455. // value, only used on radial
  16456. value: point && (this.isXAxis ?
  16457. point.x :
  16458. pick(point.stackY, point.y)),
  16459. translatedValue: pos
  16460. };
  16461. if (chart.polar) {
  16462. // Additional information required for crosshairs in
  16463. // polar chart
  16464. extend(crossOptions, {
  16465. isCrosshair: true,
  16466. chartX: e && e.chartX,
  16467. chartY: e && e.chartY,
  16468. point: point
  16469. });
  16470. }
  16471. path = this.getPlotLinePath(crossOptions) ||
  16472. null; // #3189
  16473. }
  16474. if (!defined(path)) {
  16475. this.hideCrosshair();
  16476. return;
  16477. }
  16478. categorized = this.categories && !this.isRadial;
  16479. // Draw the cross
  16480. if (!graphic) {
  16481. this.cross = graphic = chart.renderer
  16482. .path()
  16483. .addClass('highcharts-crosshair highcharts-crosshair-' +
  16484. (categorized ? 'category ' : 'thin ') +
  16485. options.className)
  16486. .attr({
  16487. zIndex: pick(options.zIndex, 2)
  16488. })
  16489. .add();
  16490. // Presentational attributes
  16491. if (!chart.styledMode) {
  16492. graphic.attr({
  16493. stroke: options.color ||
  16494. (categorized ?
  16495. Color
  16496. .parse('#ccd6eb')
  16497. .setOpacity(0.25)
  16498. .get() :
  16499. '#cccccc'),
  16500. 'stroke-width': pick(options.width, 1)
  16501. }).css({
  16502. 'pointer-events': 'none'
  16503. });
  16504. if (options.dashStyle) {
  16505. graphic.attr({
  16506. dashstyle: options.dashStyle
  16507. });
  16508. }
  16509. }
  16510. }
  16511. graphic.show().attr({
  16512. d: path
  16513. });
  16514. if (categorized && !options.width) {
  16515. graphic.attr({
  16516. 'stroke-width': this.transA
  16517. });
  16518. }
  16519. this.cross.e = e;
  16520. }
  16521. fireEvent(this, 'afterDrawCrosshair', { e: e, point: point });
  16522. };
  16523. /**
  16524. * Hide the crosshair if visible.
  16525. *
  16526. * @function Highcharts.Axis#hideCrosshair
  16527. */
  16528. Axis.prototype.hideCrosshair = function () {
  16529. if (this.cross) {
  16530. this.cross.hide();
  16531. }
  16532. fireEvent(this, 'afterHideCrosshair');
  16533. };
  16534. /**
  16535. * Check whether the chart has vertical panning ('y' or 'xy' type).
  16536. *
  16537. * @private
  16538. * @function Highcharts.Axis#hasVerticalPanning
  16539. * @return {boolean}
  16540. *
  16541. */
  16542. Axis.prototype.hasVerticalPanning = function () {
  16543. var _a, _b;
  16544. return /y/.test(((_b = (_a = this.chart.options.chart) === null || _a === void 0 ? void 0 : _a.panning) === null || _b === void 0 ? void 0 : _b.type) || '');
  16545. };
  16546. /* *
  16547. *
  16548. * Static Properties
  16549. *
  16550. * */
  16551. /**
  16552. * The X axis or category axis. Normally this is the horizontal axis,
  16553. * though if the chart is inverted this is the vertical axis. In case of
  16554. * multiple axes, the xAxis node is an array of configuration objects.
  16555. *
  16556. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  16557. * access to the axis.
  16558. *
  16559. * @productdesc {highmaps}
  16560. * In Highmaps, the axis is hidden, but it is used behind the scenes to
  16561. * control features like zooming and panning. Zooming is in effect the same
  16562. * as setting the extremes of one of the exes.
  16563. *
  16564. * @type {*|Array<*>}
  16565. * @optionparent xAxis
  16566. *
  16567. * @private
  16568. */
  16569. Axis.defaultOptions = {
  16570. /**
  16571. * When using multiple axis, the ticks of two or more opposite axes
  16572. * will automatically be aligned by adding ticks to the axis or axes
  16573. * with the least ticks, as if `tickAmount` were specified.
  16574. *
  16575. * This can be prevented by setting `alignTicks` to false. If the grid
  16576. * lines look messy, it's a good idea to hide them for the secondary
  16577. * axis by setting `gridLineWidth` to 0.
  16578. *
  16579. * If `startOnTick` or `endOnTick` in an Axis options are set to false,
  16580. * then the `alignTicks ` will be disabled for the Axis.
  16581. *
  16582. * Disabled for logarithmic axes.
  16583. *
  16584. * @type {boolean}
  16585. * @default true
  16586. * @product highcharts highstock gantt
  16587. * @apioption xAxis.alignTicks
  16588. */
  16589. /**
  16590. * Whether to allow decimals in this axis' ticks. When counting
  16591. * integers, like persons or hits on a web page, decimals should
  16592. * be avoided in the labels.
  16593. *
  16594. * @see [minTickInterval](#xAxis.minTickInterval)
  16595. *
  16596. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-true/
  16597. * True by default
  16598. * @sample {highcharts|highstock} highcharts/yaxis/allowdecimals-false/
  16599. * False
  16600. *
  16601. * @type {boolean}
  16602. * @default true
  16603. * @since 2.0
  16604. * @apioption xAxis.allowDecimals
  16605. */
  16606. /**
  16607. * When using an alternate grid color, a band is painted across the
  16608. * plot area between every other grid line.
  16609. *
  16610. * @sample {highcharts} highcharts/yaxis/alternategridcolor/
  16611. * Alternate grid color on the Y axis
  16612. * @sample {highstock} stock/xaxis/alternategridcolor/
  16613. * Alternate grid color on the Y axis
  16614. *
  16615. * @type {Highcharts.ColorType}
  16616. * @apioption xAxis.alternateGridColor
  16617. */
  16618. /**
  16619. * An array defining breaks in the axis, the sections defined will be
  16620. * left out and all the points shifted closer to each other.
  16621. *
  16622. * @productdesc {highcharts}
  16623. * Requires that the broken-axis.js module is loaded.
  16624. *
  16625. * @sample {highcharts} highcharts/axisbreak/break-simple/
  16626. * Simple break
  16627. * @sample {highcharts|highstock} highcharts/axisbreak/break-visualized/
  16628. * Advanced with callback
  16629. * @sample {highstock} stock/demo/intraday-breaks/
  16630. * Break on nights and weekends
  16631. *
  16632. * @type {Array<*>}
  16633. * @since 4.1.0
  16634. * @product highcharts highstock gantt
  16635. * @apioption xAxis.breaks
  16636. */
  16637. /**
  16638. * A number indicating how much space should be left between the start
  16639. * and the end of the break. The break size is given in axis units,
  16640. * so for instance on a `datetime` axis, a break size of 3600000 would
  16641. * indicate the equivalent of an hour.
  16642. *
  16643. * @type {number}
  16644. * @default 0
  16645. * @since 4.1.0
  16646. * @product highcharts highstock gantt
  16647. * @apioption xAxis.breaks.breakSize
  16648. */
  16649. /**
  16650. * The point where the break starts.
  16651. *
  16652. * @type {number}
  16653. * @since 4.1.0
  16654. * @product highcharts highstock gantt
  16655. * @apioption xAxis.breaks.from
  16656. */
  16657. /**
  16658. * Defines an interval after which the break appears again. By default
  16659. * the breaks do not repeat.
  16660. *
  16661. * @type {number}
  16662. * @default 0
  16663. * @since 4.1.0
  16664. * @product highcharts highstock gantt
  16665. * @apioption xAxis.breaks.repeat
  16666. */
  16667. /**
  16668. * The point where the break ends.
  16669. *
  16670. * @type {number}
  16671. * @since 4.1.0
  16672. * @product highcharts highstock gantt
  16673. * @apioption xAxis.breaks.to
  16674. */
  16675. /**
  16676. * If categories are present for the xAxis, names are used instead of
  16677. * numbers for that axis.
  16678. *
  16679. * Since Highcharts 3.0, categories can also
  16680. * be extracted by giving each point a [name](#series.data) and setting
  16681. * axis [type](#xAxis.type) to `category`. However, if you have multiple
  16682. * series, best practice remains defining the `categories` array.
  16683. *
  16684. * Example: `categories: ['Apples', 'Bananas', 'Oranges']`
  16685. *
  16686. * @sample {highcharts} highcharts/demo/line-labels/
  16687. * With
  16688. * @sample {highcharts} highcharts/xaxis/categories/
  16689. * Without
  16690. *
  16691. * @type {Array<string>}
  16692. * @product highcharts gantt
  16693. * @apioption xAxis.categories
  16694. */
  16695. /**
  16696. * The highest allowed value for automatically computed axis extremes.
  16697. *
  16698. * @see [floor](#xAxis.floor)
  16699. *
  16700. * @sample {highcharts|highstock} highcharts/yaxis/floor-ceiling/
  16701. * Floor and ceiling
  16702. *
  16703. * @type {number}
  16704. * @since 4.0
  16705. * @product highcharts highstock gantt
  16706. * @apioption xAxis.ceiling
  16707. */
  16708. /**
  16709. * A class name that opens for styling the axis by CSS, especially in
  16710. * Highcharts styled mode. The class name is applied to group elements
  16711. * for the grid, axis elements and labels.
  16712. *
  16713. * @sample {highcharts|highstock|highmaps} highcharts/css/axis/
  16714. * Multiple axes with separate styling
  16715. *
  16716. * @type {string}
  16717. * @since 5.0.0
  16718. * @apioption xAxis.className
  16719. */
  16720. /**
  16721. * Configure a crosshair that follows either the mouse pointer or the
  16722. * hovered point.
  16723. *
  16724. * In styled mode, the crosshairs are styled in the
  16725. * `.highcharts-crosshair`, `.highcharts-crosshair-thin` or
  16726. * `.highcharts-xaxis-category` classes.
  16727. *
  16728. * @productdesc {highstock}
  16729. * In Highstock, by default, the crosshair is enabled on the X axis and
  16730. * disabled on the Y axis.
  16731. *
  16732. * @sample {highcharts} highcharts/xaxis/crosshair-both/
  16733. * Crosshair on both axes
  16734. * @sample {highstock} stock/xaxis/crosshairs-xy/
  16735. * Crosshair on both axes
  16736. * @sample {highmaps} highcharts/xaxis/crosshair-both/
  16737. * Crosshair on both axes
  16738. *
  16739. * @declare Highcharts.AxisCrosshairOptions
  16740. * @type {boolean|*}
  16741. * @default false
  16742. * @since 4.1
  16743. * @apioption xAxis.crosshair
  16744. */
  16745. /**
  16746. * A class name for the crosshair, especially as a hook for styling.
  16747. *
  16748. * @type {string}
  16749. * @since 5.0.0
  16750. * @apioption xAxis.crosshair.className
  16751. */
  16752. /**
  16753. * The color of the crosshair. Defaults to `#cccccc` for numeric and
  16754. * datetime axes, and `rgba(204,214,235,0.25)` for category axes, where
  16755. * the crosshair by default highlights the whole category.
  16756. *
  16757. * @sample {highcharts|highstock|highmaps} highcharts/xaxis/crosshair-customized/
  16758. * Customized crosshairs
  16759. *
  16760. * @type {Highcharts.ColorType}
  16761. * @default #cccccc
  16762. * @since 4.1
  16763. * @apioption xAxis.crosshair.color
  16764. */
  16765. /**
  16766. * The dash style for the crosshair. See
  16767. * [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  16768. * for possible values.
  16769. *
  16770. * @sample {highcharts|highmaps} highcharts/xaxis/crosshair-dotted/
  16771. * Dotted crosshair
  16772. * @sample {highstock} stock/xaxis/crosshair-dashed/
  16773. * Dashed X axis crosshair
  16774. *
  16775. * @type {Highcharts.DashStyleValue}
  16776. * @default Solid
  16777. * @since 4.1
  16778. * @apioption xAxis.crosshair.dashStyle
  16779. */
  16780. /**
  16781. * A label on the axis next to the crosshair.
  16782. *
  16783. * In styled mode, the label is styled with the
  16784. * `.highcharts-crosshair-label` class.
  16785. *
  16786. * @sample {highstock} stock/xaxis/crosshair-label/
  16787. * Crosshair labels
  16788. * @sample {highstock} highcharts/css/crosshair-label/
  16789. * Style mode
  16790. *
  16791. * @declare Highcharts.AxisCrosshairLabelOptions
  16792. * @since 2.1
  16793. * @product highstock
  16794. * @apioption xAxis.crosshair.label
  16795. */
  16796. /**
  16797. * Alignment of the label compared to the axis. Defaults to `"left"` for
  16798. * right-side axes, `"right"` for left-side axes and `"center"` for
  16799. * horizontal axes.
  16800. *
  16801. * @type {Highcharts.AlignValue}
  16802. * @since 2.1
  16803. * @product highstock
  16804. * @apioption xAxis.crosshair.label.align
  16805. */
  16806. /**
  16807. * The background color for the label. Defaults to the related series
  16808. * color, or `#666666` if that is not available.
  16809. *
  16810. * @type {Highcharts.ColorType}
  16811. * @since 2.1
  16812. * @product highstock
  16813. * @apioption xAxis.crosshair.label.backgroundColor
  16814. */
  16815. /**
  16816. * The border color for the crosshair label
  16817. *
  16818. * @type {Highcharts.ColorType}
  16819. * @since 2.1
  16820. * @product highstock
  16821. * @apioption xAxis.crosshair.label.borderColor
  16822. */
  16823. /**
  16824. * The border corner radius of the crosshair label.
  16825. *
  16826. * @type {number}
  16827. * @default 3
  16828. * @since 2.1.10
  16829. * @product highstock
  16830. * @apioption xAxis.crosshair.label.borderRadius
  16831. */
  16832. /**
  16833. * The border width for the crosshair label.
  16834. *
  16835. * @type {number}
  16836. * @default 0
  16837. * @since 2.1
  16838. * @product highstock
  16839. * @apioption xAxis.crosshair.label.borderWidth
  16840. */
  16841. /**
  16842. * Flag to enable crosshair's label.
  16843. *
  16844. * @sample {highstock} stock/xaxis/crosshairs-xy/
  16845. * Enabled label for yAxis' crosshair
  16846. *
  16847. * @type {boolean}
  16848. * @default false
  16849. * @since 2.1
  16850. * @product highstock
  16851. * @apioption xAxis.crosshair.label.enabled
  16852. */
  16853. /**
  16854. * A format string for the crosshair label. Defaults to `{value}` for
  16855. * numeric axes and `{value:%b %d, %Y}` for datetime axes.
  16856. *
  16857. * @type {string}
  16858. * @since 2.1
  16859. * @product highstock
  16860. * @apioption xAxis.crosshair.label.format
  16861. */
  16862. /**
  16863. * Formatter function for the label text.
  16864. *
  16865. * @type {Highcharts.XAxisCrosshairLabelFormatterCallbackFunction}
  16866. * @since 2.1
  16867. * @product highstock
  16868. * @apioption xAxis.crosshair.label.formatter
  16869. */
  16870. /**
  16871. * Padding inside the crosshair label.
  16872. *
  16873. * @type {number}
  16874. * @default 8
  16875. * @since 2.1
  16876. * @product highstock
  16877. * @apioption xAxis.crosshair.label.padding
  16878. */
  16879. /**
  16880. * The shape to use for the label box.
  16881. *
  16882. * @type {string}
  16883. * @default callout
  16884. * @since 2.1
  16885. * @product highstock
  16886. * @apioption xAxis.crosshair.label.shape
  16887. */
  16888. /**
  16889. * Text styles for the crosshair label.
  16890. *
  16891. * @type {Highcharts.CSSObject}
  16892. * @default {"color": "white", "fontWeight": "normal", "fontSize": "11px", "textAlign": "center"}
  16893. * @since 2.1
  16894. * @product highstock
  16895. * @apioption xAxis.crosshair.label.style
  16896. */
  16897. /**
  16898. * Whether the crosshair should snap to the point or follow the pointer
  16899. * independent of points.
  16900. *
  16901. * @sample {highcharts|highstock} highcharts/xaxis/crosshair-snap-false/
  16902. * True by default
  16903. * @sample {highmaps} maps/demo/latlon-advanced/
  16904. * Snap is false
  16905. *
  16906. * @type {boolean}
  16907. * @default true
  16908. * @since 4.1
  16909. * @apioption xAxis.crosshair.snap
  16910. */
  16911. /**
  16912. * The pixel width of the crosshair. Defaults to 1 for numeric or
  16913. * datetime axes, and for one category width for category axes.
  16914. *
  16915. * @sample {highcharts} highcharts/xaxis/crosshair-customized/
  16916. * Customized crosshairs
  16917. * @sample {highstock} highcharts/xaxis/crosshair-customized/
  16918. * Customized crosshairs
  16919. * @sample {highmaps} highcharts/xaxis/crosshair-customized/
  16920. * Customized crosshairs
  16921. *
  16922. * @type {number}
  16923. * @default 1
  16924. * @since 4.1
  16925. * @apioption xAxis.crosshair.width
  16926. */
  16927. /**
  16928. * The Z index of the crosshair. Higher Z indices allow drawing the
  16929. * crosshair on top of the series or behind the grid lines.
  16930. *
  16931. * @type {number}
  16932. * @default 2
  16933. * @since 4.1
  16934. * @apioption xAxis.crosshair.zIndex
  16935. */
  16936. /**
  16937. * Whether to zoom axis. If `chart.zoomType` is set, the option allows
  16938. * to disable zooming on an individual axis.
  16939. *
  16940. * @sample {highcharts} highcharts/xaxis/zoomenabled/
  16941. * Zoom enabled is false
  16942. *
  16943. *
  16944. * @type {boolean}
  16945. * @default enabled
  16946. * @apioption xAxis.zoomEnabled
  16947. */
  16948. /**
  16949. * For a datetime axis, the scale will automatically adjust to the
  16950. * appropriate unit. This member gives the default string
  16951. * representations used for each unit. For intermediate values,
  16952. * different units may be used, for example the `day` unit can be used
  16953. * on midnight and `hour` unit be used for intermediate values on the
  16954. * same axis. For an overview of the replacement codes, see
  16955. * [dateFormat](/class-reference/Highcharts#dateFormat).
  16956. *
  16957. * Defaults to:
  16958. * ```js
  16959. * {
  16960. * millisecond: '%H:%M:%S.%L',
  16961. * second: '%H:%M:%S',
  16962. * minute: '%H:%M',
  16963. * hour: '%H:%M',
  16964. * day: '%e. %b',
  16965. * week: '%e. %b',
  16966. * month: '%b \'%y',
  16967. * year: '%Y'
  16968. * }
  16969. * ```
  16970. *
  16971. * @sample {highcharts} highcharts/xaxis/datetimelabelformats/
  16972. * Different day format on X axis
  16973. * @sample {highstock} stock/xaxis/datetimelabelformats/
  16974. * More information in x axis labels
  16975. *
  16976. * @declare Highcharts.AxisDateTimeLabelFormatsOptions
  16977. * @product highcharts highstock gantt
  16978. */
  16979. dateTimeLabelFormats: {
  16980. /**
  16981. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  16982. * @type {string|*}
  16983. */
  16984. millisecond: {
  16985. main: '%H:%M:%S.%L',
  16986. range: false
  16987. },
  16988. /**
  16989. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  16990. * @type {string|*}
  16991. */
  16992. second: {
  16993. main: '%H:%M:%S',
  16994. range: false
  16995. },
  16996. /**
  16997. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  16998. * @type {string|*}
  16999. */
  17000. minute: {
  17001. main: '%H:%M',
  17002. range: false
  17003. },
  17004. /**
  17005. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17006. * @type {string|*}
  17007. */
  17008. hour: {
  17009. main: '%H:%M',
  17010. range: false
  17011. },
  17012. /**
  17013. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17014. * @type {string|*}
  17015. */
  17016. day: {
  17017. main: '%e. %b'
  17018. },
  17019. /**
  17020. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17021. * @type {string|*}
  17022. */
  17023. week: {
  17024. main: '%e. %b'
  17025. },
  17026. /**
  17027. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17028. * @type {string|*}
  17029. */
  17030. month: {
  17031. main: '%b \'%y'
  17032. },
  17033. /**
  17034. * @declare Highcharts.AxisDateTimeLabelFormatsOptionsObject
  17035. * @type {string|*}
  17036. */
  17037. year: {
  17038. main: '%Y'
  17039. }
  17040. },
  17041. /**
  17042. * Whether to force the axis to end on a tick. Use this option with
  17043. * the `maxPadding` option to control the axis end.
  17044. *
  17045. * @productdesc {highstock}
  17046. * In Highstock, `endOnTick` is always `false` when the navigator
  17047. * is enabled, to prevent jumpy scrolling.
  17048. *
  17049. * @sample {highcharts} highcharts/chart/reflow-true/
  17050. * True by default
  17051. * @sample {highcharts} highcharts/yaxis/endontick/
  17052. * False
  17053. * @sample {highstock} stock/demo/basic-line/
  17054. * True by default
  17055. * @sample {highstock} stock/xaxis/endontick/
  17056. * False
  17057. *
  17058. * @since 1.2.0
  17059. */
  17060. endOnTick: false,
  17061. /**
  17062. * Event handlers for the axis.
  17063. *
  17064. * @type {*}
  17065. * @apioption xAxis.events
  17066. */
  17067. /**
  17068. * An event fired after the breaks have rendered.
  17069. *
  17070. * @see [breaks](#xAxis.breaks)
  17071. *
  17072. * @sample {highcharts} highcharts/axisbreak/break-event/
  17073. * AfterBreak Event
  17074. *
  17075. * @type {Highcharts.AxisEventCallbackFunction}
  17076. * @since 4.1.0
  17077. * @product highcharts gantt
  17078. * @apioption xAxis.events.afterBreaks
  17079. */
  17080. /**
  17081. * As opposed to the `setExtremes` event, this event fires after the
  17082. * final min and max values are computed and corrected for `minRange`.
  17083. *
  17084. * Fires when the minimum and maximum is set for the axis, either by
  17085. * calling the `.setExtremes()` method or by selecting an area in the
  17086. * chart. One parameter, `event`, is passed to the function, containing
  17087. * common event information.
  17088. *
  17089. * The new user set minimum and maximum values can be found by
  17090. * `event.min` and `event.max`. These reflect the axis minimum and
  17091. * maximum in axis values. The actual data extremes are found in
  17092. * `event.dataMin` and `event.dataMax`.
  17093. *
  17094. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  17095. * @since 2.3
  17096. * @context Highcharts.Axis
  17097. * @apioption xAxis.events.afterSetExtremes
  17098. */
  17099. /**
  17100. * An event fired when a break from this axis occurs on a point.
  17101. *
  17102. * @see [breaks](#xAxis.breaks)
  17103. *
  17104. * @sample {highcharts} highcharts/axisbreak/break-visualized/
  17105. * Visualization of a Break
  17106. *
  17107. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  17108. * @since 4.1.0
  17109. * @product highcharts gantt
  17110. * @context Highcharts.Axis
  17111. * @apioption xAxis.events.pointBreak
  17112. */
  17113. /**
  17114. * An event fired when a point falls inside a break from this axis.
  17115. *
  17116. * @type {Highcharts.AxisPointBreakEventCallbackFunction}
  17117. * @product highcharts highstock gantt
  17118. * @context Highcharts.Axis
  17119. * @apioption xAxis.events.pointInBreak
  17120. */
  17121. /**
  17122. * Fires when the minimum and maximum is set for the axis, either by
  17123. * calling the `.setExtremes()` method or by selecting an area in the
  17124. * chart. One parameter, `event`, is passed to the function,
  17125. * containing common event information.
  17126. *
  17127. * The new user set minimum and maximum values can be found by
  17128. * `event.min` and `event.max`. These reflect the axis minimum and
  17129. * maximum in data values. When an axis is zoomed all the way out from
  17130. * the "Reset zoom" button, `event.min` and `event.max` are null, and
  17131. * the new extremes are set based on `this.dataMin` and `this.dataMax`.
  17132. *
  17133. * @sample {highstock} stock/xaxis/events-setextremes/
  17134. * Log new extremes on x axis
  17135. *
  17136. * @type {Highcharts.AxisSetExtremesEventCallbackFunction}
  17137. * @since 1.2.0
  17138. * @context Highcharts.Axis
  17139. * @apioption xAxis.events.setExtremes
  17140. */
  17141. /**
  17142. * The lowest allowed value for automatically computed axis extremes.
  17143. *
  17144. * @see [ceiling](#yAxis.ceiling)
  17145. *
  17146. * @sample {highcharts} highcharts/yaxis/floor-ceiling/
  17147. * Floor and ceiling
  17148. * @sample {highstock} stock/demo/lazy-loading/
  17149. * Prevent negative stock price on Y axis
  17150. *
  17151. * @type {number}
  17152. * @since 4.0
  17153. * @product highcharts highstock gantt
  17154. * @apioption xAxis.floor
  17155. */
  17156. /**
  17157. * The dash or dot style of the grid lines. For possible values, see
  17158. * [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  17159. *
  17160. * @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
  17161. * Long dashes
  17162. * @sample {highstock} stock/xaxis/gridlinedashstyle/
  17163. * Long dashes
  17164. *
  17165. * @type {Highcharts.DashStyleValue}
  17166. * @default Solid
  17167. * @since 1.2
  17168. * @apioption xAxis.gridLineDashStyle
  17169. */
  17170. /**
  17171. * The Z index of the grid lines.
  17172. *
  17173. * @sample {highcharts|highstock} highcharts/xaxis/gridzindex/
  17174. * A Z index of 4 renders the grid above the graph
  17175. *
  17176. * @type {number}
  17177. * @default 1
  17178. * @product highcharts highstock gantt
  17179. * @apioption xAxis.gridZIndex
  17180. */
  17181. /**
  17182. * An id for the axis. This can be used after render time to get
  17183. * a pointer to the axis object through `chart.get()`.
  17184. *
  17185. * @sample {highcharts} highcharts/xaxis/id/
  17186. * Get the object
  17187. * @sample {highstock} stock/xaxis/id/
  17188. * Get the object
  17189. *
  17190. * @type {string}
  17191. * @since 1.2.0
  17192. * @apioption xAxis.id
  17193. */
  17194. /**
  17195. * The axis labels show the number or category for each tick.
  17196. *
  17197. * Since v8.0.0: Labels are animated in categorized x-axis with
  17198. * updating data if `tickInterval` and `step` is set to 1.
  17199. *
  17200. * @productdesc {highmaps}
  17201. * X and Y axis labels are by default disabled in Highmaps, but the
  17202. * functionality is inherited from Highcharts and used on `colorAxis`,
  17203. * and can be enabled on X and Y axes too.
  17204. */
  17205. labels: {
  17206. /**
  17207. * What part of the string the given position is anchored to.
  17208. * If `left`, the left side of the string is at the axis position.
  17209. * Can be one of `"left"`, `"center"` or `"right"`. Defaults to
  17210. * an intelligent guess based on which side of the chart the axis
  17211. * is on and the rotation of the label.
  17212. *
  17213. * @see [reserveSpace](#xAxis.labels.reserveSpace)
  17214. *
  17215. * @sample {highcharts} highcharts/xaxis/labels-align-left/
  17216. * Left
  17217. * @sample {highcharts} highcharts/xaxis/labels-align-right/
  17218. * Right
  17219. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  17220. * Left-aligned labels on a vertical category axis
  17221. *
  17222. * @type {Highcharts.AlignValue}
  17223. * @apioption xAxis.labels.align
  17224. */
  17225. /**
  17226. * For horizontal axes, the allowed degrees of label rotation
  17227. * to prevent overlapping labels. If there is enough space,
  17228. * labels are not rotated. As the chart gets narrower, it
  17229. * will start rotating the labels -45 degrees, then remove
  17230. * every second label and try again with rotations 0 and -45 etc.
  17231. * Set it to `false` to disable rotation, which will
  17232. * cause the labels to word-wrap if possible.
  17233. *
  17234. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-default/
  17235. * Default auto rotation of 0 or -45
  17236. * @sample {highcharts|highstock} highcharts/xaxis/labels-autorotation-0-90/
  17237. * Custom graded auto rotation
  17238. *
  17239. * @type {Array<number>|false}
  17240. * @default [-45]
  17241. * @since 4.1.0
  17242. * @product highcharts highstock gantt
  17243. * @apioption xAxis.labels.autoRotation
  17244. */
  17245. /**
  17246. * When each category width is more than this many pixels, we don't
  17247. * apply auto rotation. Instead, we lay out the axis label with word
  17248. * wrap. A lower limit makes sense when the label contains multiple
  17249. * short words that don't extend the available horizontal space for
  17250. * each label.
  17251. *
  17252. * @sample {highcharts} highcharts/xaxis/labels-autorotationlimit/
  17253. * Lower limit
  17254. *
  17255. * @type {number}
  17256. * @default 80
  17257. * @since 4.1.5
  17258. * @product highcharts gantt
  17259. * @apioption xAxis.labels.autoRotationLimit
  17260. */
  17261. /**
  17262. * Polar charts only. The label's pixel distance from the perimeter
  17263. * of the plot area.
  17264. *
  17265. * @type {number}
  17266. * @default 15
  17267. * @product highcharts gantt
  17268. * @apioption xAxis.labels.distance
  17269. */
  17270. /**
  17271. * Enable or disable the axis labels.
  17272. *
  17273. * @sample {highcharts} highcharts/xaxis/labels-enabled/
  17274. * X axis labels disabled
  17275. * @sample {highstock} stock/xaxis/labels-enabled/
  17276. * X axis labels disabled
  17277. *
  17278. * @default {highcharts|highstock|gantt} true
  17279. * @default {highmaps} false
  17280. */
  17281. enabled: true,
  17282. /**
  17283. * A [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  17284. * for the axis label.
  17285. *
  17286. * @sample {highcharts|highstock} highcharts/yaxis/labels-format/
  17287. * Add units to Y axis label
  17288. *
  17289. * @type {string}
  17290. * @default {value}
  17291. * @since 3.0
  17292. * @apioption xAxis.labels.format
  17293. */
  17294. /**
  17295. * Callback JavaScript function to format the label. The value
  17296. * is given by `this.value`. Additional properties for `this` are
  17297. * `axis`, `chart`, `isFirst` and `isLast`. The value of the default
  17298. * label formatter can be retrieved by calling
  17299. * `this.axis.defaultLabelFormatter.call(this)` within the function.
  17300. *
  17301. * Defaults to:
  17302. * ```js
  17303. * function() {
  17304. * return this.value;
  17305. * }
  17306. * ```
  17307. *
  17308. * @sample {highcharts} highcharts/xaxis/labels-formatter-linked/
  17309. * Linked category names
  17310. * @sample {highcharts} highcharts/xaxis/labels-formatter-extended/
  17311. * Modified numeric labels
  17312. * @sample {highstock} stock/xaxis/labels-formatter/
  17313. * Added units on Y axis
  17314. *
  17315. * @type {Highcharts.AxisLabelsFormatterCallbackFunction}
  17316. * @apioption xAxis.labels.formatter
  17317. */
  17318. /**
  17319. * The number of pixels to indent the labels per level in a treegrid
  17320. * axis.
  17321. *
  17322. * @sample gantt/treegrid-axis/demo
  17323. * Indentation 10px by default.
  17324. * @sample gantt/treegrid-axis/indentation-0px
  17325. * Indentation set to 0px.
  17326. *
  17327. * @product gantt
  17328. */
  17329. indentation: 10,
  17330. /**
  17331. * Horizontal axis only. When `staggerLines` is not set,
  17332. * `maxStaggerLines` defines how many lines the axis is allowed to
  17333. * add to automatically avoid overlapping X labels. Set to `1` to
  17334. * disable overlap detection.
  17335. *
  17336. * @deprecated
  17337. * @type {number}
  17338. * @default 5
  17339. * @since 1.3.3
  17340. * @apioption xAxis.labels.maxStaggerLines
  17341. */
  17342. /**
  17343. * How to handle overflowing labels on horizontal axis. If set to
  17344. * `"allow"`, it will not be aligned at all. By default it
  17345. * `"justify"` labels inside the chart area. If there is room to
  17346. * move it, it will be aligned to the edge, else it will be removed.
  17347. *
  17348. * @type {string}
  17349. * @default justify
  17350. * @since 2.2.5
  17351. * @validvalue ["allow", "justify"]
  17352. * @apioption xAxis.labels.overflow
  17353. */
  17354. /**
  17355. * The pixel padding for axis labels, to ensure white space between
  17356. * them.
  17357. *
  17358. * @type {number}
  17359. * @default 5
  17360. * @product highcharts gantt
  17361. * @apioption xAxis.labels.padding
  17362. */
  17363. /**
  17364. * Whether to reserve space for the labels. By default, space is
  17365. * reserved for the labels in these cases:
  17366. *
  17367. * * On all horizontal axes.
  17368. * * On vertical axes if `label.align` is `right` on a left-side
  17369. * axis or `left` on a right-side axis.
  17370. * * On vertical axes if `label.align` is `center`.
  17371. *
  17372. * This can be turned off when for example the labels are rendered
  17373. * inside the plot area instead of outside.
  17374. *
  17375. * @see [labels.align](#xAxis.labels.align)
  17376. *
  17377. * @sample {highcharts} highcharts/xaxis/labels-reservespace/
  17378. * No reserved space, labels inside plot
  17379. * @sample {highcharts} highcharts/xaxis/labels-reservespace-true/
  17380. * Left-aligned labels on a vertical category axis
  17381. *
  17382. * @type {boolean}
  17383. * @since 4.1.10
  17384. * @product highcharts gantt
  17385. * @apioption xAxis.labels.reserveSpace
  17386. */
  17387. /**
  17388. * Rotation of the labels in degrees.
  17389. *
  17390. * @sample {highcharts} highcharts/xaxis/labels-rotation/
  17391. * X axis labels rotated 90°
  17392. *
  17393. * @type {number}
  17394. * @default 0
  17395. * @apioption xAxis.labels.rotation
  17396. */
  17397. /**
  17398. * Horizontal axes only. The number of lines to spread the labels
  17399. * over to make room or tighter labels.
  17400. *
  17401. * @sample {highcharts} highcharts/xaxis/labels-staggerlines/
  17402. * Show labels over two lines
  17403. * @sample {highstock} stock/xaxis/labels-staggerlines/
  17404. * Show labels over two lines
  17405. *
  17406. * @type {number}
  17407. * @since 2.1
  17408. * @apioption xAxis.labels.staggerLines
  17409. */
  17410. /**
  17411. * To show only every _n_'th label on the axis, set the step to _n_.
  17412. * Setting the step to 2 shows every other label.
  17413. *
  17414. * By default, the step is calculated automatically to avoid
  17415. * overlap. To prevent this, set it to 1\. This usually only
  17416. * happens on a category axis, and is often a sign that you have
  17417. * chosen the wrong axis type.
  17418. *
  17419. * Read more at
  17420. * [Axis docs](https://www.highcharts.com/docs/chart-concepts/axes)
  17421. * => What axis should I use?
  17422. *
  17423. * @sample {highcharts} highcharts/xaxis/labels-step/
  17424. * Showing only every other axis label on a categorized
  17425. * x-axis
  17426. * @sample {highcharts} highcharts/xaxis/labels-step-auto/
  17427. * Auto steps on a category axis
  17428. *
  17429. * @type {number}
  17430. * @since 2.1
  17431. * @apioption xAxis.labels.step
  17432. */
  17433. /**
  17434. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  17435. * to render the labels.
  17436. *
  17437. * @type {boolean}
  17438. * @default false
  17439. * @apioption xAxis.labels.useHTML
  17440. */
  17441. /**
  17442. * The x position offset of the label relative to the tick position
  17443. * on the axis.
  17444. *
  17445. * @sample {highcharts} highcharts/xaxis/labels-x/
  17446. * Y axis labels placed on grid lines
  17447. */
  17448. x: 0,
  17449. /**
  17450. * The y position offset of the label relative to the tick position
  17451. * on the axis. The default makes it adapt to the font size on
  17452. * bottom axis.
  17453. *
  17454. * @sample {highcharts} highcharts/xaxis/labels-x/
  17455. * Y axis labels placed on grid lines
  17456. *
  17457. * @type {number}
  17458. * @apioption xAxis.labels.y
  17459. */
  17460. /**
  17461. * The Z index for the axis labels.
  17462. *
  17463. * @type {number}
  17464. * @default 7
  17465. * @apioption xAxis.labels.zIndex
  17466. */
  17467. /**
  17468. * CSS styles for the label. Use `whiteSpace: 'nowrap'` to prevent
  17469. * wrapping of category labels. Use `textOverflow: 'none'` to
  17470. * prevent ellipsis (dots).
  17471. *
  17472. * In styled mode, the labels are styled with the
  17473. * `.highcharts-axis-labels` class.
  17474. *
  17475. * @sample {highcharts} highcharts/xaxis/labels-style/
  17476. * Red X axis labels
  17477. *
  17478. * @type {Highcharts.CSSObject}
  17479. */
  17480. style: {
  17481. /** @internal */
  17482. color: '#666666',
  17483. /** @internal */
  17484. cursor: 'default',
  17485. /** @internal */
  17486. fontSize: '11px'
  17487. }
  17488. },
  17489. /**
  17490. * The left position as the horizontal axis. If it's a number, it is
  17491. * interpreted as pixel position relative to the chart.
  17492. *
  17493. * Since Highcharts v5.0.13: If it's a percentage string, it is
  17494. * interpreted as percentages of the plot width, offset from plot area
  17495. * left.
  17496. *
  17497. * @type {number|string}
  17498. * @product highcharts highstock
  17499. * @apioption xAxis.left
  17500. */
  17501. /**
  17502. * The top position as the vertical axis. If it's a number, it is
  17503. * interpreted as pixel position relative to the chart.
  17504. *
  17505. * Since Highcharts 2: If it's a percentage string, it is interpreted
  17506. * as percentages of the plot height, offset from plot area top.
  17507. *
  17508. * @type {number|string}
  17509. * @product highcharts highstock
  17510. * @apioption xAxis.top
  17511. */
  17512. /**
  17513. * Index of another axis that this axis is linked to. When an axis is
  17514. * linked to a master axis, it will take the same extremes as
  17515. * the master, but as assigned by min or max or by setExtremes.
  17516. * It can be used to show additional info, or to ease reading the
  17517. * chart by duplicating the scales.
  17518. *
  17519. * @sample {highcharts} highcharts/xaxis/linkedto/
  17520. * Different string formats of the same date
  17521. * @sample {highcharts} highcharts/yaxis/linkedto/
  17522. * Y values on both sides
  17523. *
  17524. * @type {number}
  17525. * @since 2.0.2
  17526. * @product highcharts highstock gantt
  17527. * @apioption xAxis.linkedTo
  17528. */
  17529. /**
  17530. * The maximum value of the axis. If `null`, the max value is
  17531. * automatically calculated.
  17532. *
  17533. * If the [endOnTick](#yAxis.endOnTick) option is true, the `max` value
  17534. * might be rounded up.
  17535. *
  17536. * If a [tickAmount](#yAxis.tickAmount) is set, the axis may be extended
  17537. * beyond the set max in order to reach the given number of ticks. The
  17538. * same may happen in a chart with multiple axes, determined by [chart.
  17539. * alignTicks](#chart), where a `tickAmount` is applied internally.
  17540. *
  17541. * @sample {highcharts} highcharts/yaxis/max-200/
  17542. * Y axis max of 200
  17543. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  17544. * Y axis max on logarithmic axis
  17545. * @sample {highstock} stock/xaxis/min-max/
  17546. * Fixed min and max on X axis
  17547. * @sample {highmaps} maps/axis/min-max/
  17548. * Pre-zoomed to a specific area
  17549. *
  17550. * @type {number|null}
  17551. * @apioption xAxis.max
  17552. */
  17553. /**
  17554. * Padding of the max value relative to the length of the axis. A
  17555. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  17556. * when you don't want the highest data value to appear on the edge
  17557. * of the plot area. When the axis' `max` option is set or a max extreme
  17558. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  17559. *
  17560. * @sample {highcharts} highcharts/yaxis/maxpadding/
  17561. * Max padding of 0.25 on y axis
  17562. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  17563. * Greater min- and maxPadding
  17564. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  17565. * Add some padding
  17566. *
  17567. * @default {highcharts} 0.01
  17568. * @default {highstock|highmaps} 0
  17569. * @since 1.2.0
  17570. */
  17571. maxPadding: 0.01,
  17572. /**
  17573. * Deprecated. Use `minRange` instead.
  17574. *
  17575. * @deprecated
  17576. * @type {number}
  17577. * @product highcharts highstock
  17578. * @apioption xAxis.maxZoom
  17579. */
  17580. /**
  17581. * The minimum value of the axis. If `null` the min value is
  17582. * automatically calculated.
  17583. *
  17584. * If the [startOnTick](#yAxis.startOnTick) option is true (default),
  17585. * the `min` value might be rounded down.
  17586. *
  17587. * The automatically calculated minimum value is also affected by
  17588. * [floor](#yAxis.floor), [softMin](#yAxis.softMin),
  17589. * [minPadding](#yAxis.minPadding), [minRange](#yAxis.minRange)
  17590. * as well as [series.threshold](#plotOptions.series.threshold)
  17591. * and [series.softThreshold](#plotOptions.series.softThreshold).
  17592. *
  17593. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  17594. * -50 with startOnTick to false
  17595. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  17596. * -50 with startOnTick true by default
  17597. * @sample {highstock} stock/xaxis/min-max/
  17598. * Set min and max on X axis
  17599. * @sample {highmaps} maps/axis/min-max/
  17600. * Pre-zoomed to a specific area
  17601. *
  17602. * @type {number|null}
  17603. * @apioption xAxis.min
  17604. */
  17605. /**
  17606. * The dash or dot style of the minor grid lines. For possible values,
  17607. * see [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  17608. *
  17609. * @sample {highcharts} highcharts/yaxis/minorgridlinedashstyle/
  17610. * Long dashes on minor grid lines
  17611. * @sample {highstock} stock/xaxis/minorgridlinedashstyle/
  17612. * Long dashes on minor grid lines
  17613. *
  17614. * @type {Highcharts.DashStyleValue}
  17615. * @default Solid
  17616. * @since 1.2
  17617. * @apioption xAxis.minorGridLineDashStyle
  17618. */
  17619. /**
  17620. * Specific tick interval in axis units for the minor ticks. On a linear
  17621. * axis, if `"auto"`, the minor tick interval is calculated as a fifth
  17622. * of the tickInterval. If `null` or `undefined`, minor ticks are not
  17623. * shown.
  17624. *
  17625. * On logarithmic axes, the unit is the power of the value. For example,
  17626. * setting the minorTickInterval to 1 puts one tick on each of 0.1, 1,
  17627. * 10, 100 etc. Setting the minorTickInterval to 0.1 produces 9 ticks
  17628. * between 1 and 10, 10 and 100 etc.
  17629. *
  17630. * If user settings dictate minor ticks to become too dense, they don't
  17631. * make sense, and will be ignored to prevent performance problems.
  17632. *
  17633. * @sample {highcharts} highcharts/yaxis/minortickinterval-null/
  17634. * Null by default
  17635. * @sample {highcharts} highcharts/yaxis/minortickinterval-5/
  17636. * 5 units
  17637. * @sample {highcharts} highcharts/yaxis/minortickinterval-log-auto/
  17638. * "auto"
  17639. * @sample {highcharts} highcharts/yaxis/minortickinterval-log/
  17640. * 0.1
  17641. * @sample {highstock} stock/demo/basic-line/
  17642. * Null by default
  17643. * @sample {highstock} stock/xaxis/minortickinterval-auto/
  17644. * "auto"
  17645. *
  17646. * @type {number|string|null}
  17647. * @apioption xAxis.minorTickInterval
  17648. */
  17649. /**
  17650. * The pixel length of the minor tick marks.
  17651. *
  17652. * @sample {highcharts} highcharts/yaxis/minorticklength/
  17653. * 10px on Y axis
  17654. * @sample {highstock} stock/xaxis/minorticks/
  17655. * 10px on Y axis
  17656. */
  17657. minorTickLength: 2,
  17658. /**
  17659. * The position of the minor tick marks relative to the axis line.
  17660. * Can be one of `inside` and `outside`.
  17661. *
  17662. * @sample {highcharts} highcharts/yaxis/minortickposition-outside/
  17663. * Outside by default
  17664. * @sample {highcharts} highcharts/yaxis/minortickposition-inside/
  17665. * Inside
  17666. * @sample {highstock} stock/xaxis/minorticks/
  17667. * Inside
  17668. *
  17669. * @validvalue ["inside", "outside"]
  17670. */
  17671. minorTickPosition: 'outside',
  17672. /**
  17673. * Enable or disable minor ticks. Unless
  17674. * [minorTickInterval](#xAxis.minorTickInterval) is set, the tick
  17675. * interval is calculated as a fifth of the `tickInterval`.
  17676. *
  17677. * On a logarithmic axis, minor ticks are laid out based on a best
  17678. * guess, attempting to enter approximately 5 minor ticks between
  17679. * each major tick.
  17680. *
  17681. * Prior to v6.0.0, ticks were unabled in auto layout by setting
  17682. * `minorTickInterval` to `"auto"`.
  17683. *
  17684. * @productdesc {highcharts}
  17685. * On axes using [categories](#xAxis.categories), minor ticks are not
  17686. * supported.
  17687. *
  17688. * @sample {highcharts} highcharts/yaxis/minorticks-true/
  17689. * Enabled on linear Y axis
  17690. *
  17691. * @type {boolean}
  17692. * @default false
  17693. * @since 6.0.0
  17694. * @apioption xAxis.minorTicks
  17695. */
  17696. /**
  17697. * The pixel width of the minor tick mark.
  17698. *
  17699. * @sample {highcharts} highcharts/yaxis/minortickwidth/
  17700. * 3px width
  17701. * @sample {highstock} stock/xaxis/minorticks/
  17702. * 1px width
  17703. *
  17704. * @type {number}
  17705. * @default 0
  17706. * @apioption xAxis.minorTickWidth
  17707. */
  17708. /**
  17709. * Padding of the min value relative to the length of the axis. A
  17710. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  17711. * when you don't want the lowest data value to appear on the edge
  17712. * of the plot area. When the axis' `min` option is set or a min extreme
  17713. * is set using `axis.setExtremes()`, the minPadding will be ignored.
  17714. *
  17715. * @sample {highcharts} highcharts/yaxis/minpadding/
  17716. * Min padding of 0.2
  17717. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  17718. * Greater min- and maxPadding
  17719. * @sample {highmaps} maps/chart/plotbackgroundcolor-gradient/
  17720. * Add some padding
  17721. *
  17722. * @default {highcharts} 0.01
  17723. * @default {highstock|highmaps} 0
  17724. * @since 1.2.0
  17725. * @product highcharts highstock gantt
  17726. */
  17727. minPadding: 0.01,
  17728. /**
  17729. * The minimum range to display on this axis. The entire axis will not
  17730. * be allowed to span over a smaller interval than this. For example,
  17731. * for a datetime axis the main unit is milliseconds. If minRange is
  17732. * set to 3600000, you can't zoom in more than to one hour.
  17733. *
  17734. * The default minRange for the x axis is five times the smallest
  17735. * interval between any of the data points.
  17736. *
  17737. * On a logarithmic axis, the unit for the minimum range is the power.
  17738. * So a minRange of 1 means that the axis can be zoomed to 10-100,
  17739. * 100-1000, 1000-10000 etc.
  17740. *
  17741. * **Note**: The `minPadding`, `maxPadding`, `startOnTick` and
  17742. * `endOnTick` settings also affect how the extremes of the axis
  17743. * are computed.
  17744. *
  17745. * @sample {highcharts} highcharts/xaxis/minrange/
  17746. * Minimum range of 5
  17747. * @sample {highstock} stock/xaxis/minrange/
  17748. * Max zoom of 6 months overrides user selections
  17749. * @sample {highmaps} maps/axis/minrange/
  17750. * Minimum range of 1000
  17751. *
  17752. * @type {number}
  17753. * @apioption xAxis.minRange
  17754. */
  17755. /**
  17756. * The minimum tick interval allowed in axis values. For example on
  17757. * zooming in on an axis with daily data, this can be used to prevent
  17758. * the axis from showing hours. Defaults to the closest distance between
  17759. * two points on the axis.
  17760. *
  17761. * @type {number}
  17762. * @since 2.3.0
  17763. * @apioption xAxis.minTickInterval
  17764. */
  17765. /**
  17766. * The distance in pixels from the plot area to the axis line.
  17767. * A positive offset moves the axis with it's line, labels and ticks
  17768. * away from the plot area. This is typically used when two or more
  17769. * axes are displayed on the same side of the plot. With multiple
  17770. * axes the offset is dynamically adjusted to avoid collision, this
  17771. * can be overridden by setting offset explicitly.
  17772. *
  17773. * @sample {highcharts} highcharts/yaxis/offset/
  17774. * Y axis offset of 70
  17775. * @sample {highcharts} highcharts/yaxis/offset-centered/
  17776. * Axes positioned in the center of the plot
  17777. * @sample {highstock} stock/xaxis/offset/
  17778. * Y axis offset by 70 px
  17779. *
  17780. * @type {number}
  17781. * @default 0
  17782. * @apioption xAxis.offset
  17783. */
  17784. /**
  17785. * Whether to display the axis on the opposite side of the normal. The
  17786. * normal is on the left side for vertical axes and bottom for
  17787. * horizontal, so the opposite sides will be right and top respectively.
  17788. * This is typically used with dual or multiple axes.
  17789. *
  17790. * @sample {highcharts} highcharts/yaxis/opposite/
  17791. * Secondary Y axis opposite
  17792. * @sample {highstock} stock/xaxis/opposite/
  17793. * Y axis on left side
  17794. *
  17795. * @type {boolean}
  17796. * @default false
  17797. * @apioption xAxis.opposite
  17798. */
  17799. /**
  17800. * In an ordinal axis, the points are equally spaced in the chart
  17801. * regardless of the actual time or x distance between them. This means
  17802. * that missing data periods (e.g. nights or weekends for a stock chart)
  17803. * will not take up space in the chart.
  17804. * Having `ordinal: false` will show any gaps created by the `gapSize`
  17805. * setting proportionate to their duration.
  17806. *
  17807. * In stock charts the X axis is ordinal by default, unless
  17808. * the boost module is used and at least one of the series' data length
  17809. * exceeds the [boostThreshold](#series.line.boostThreshold).
  17810. *
  17811. * @sample {highstock} stock/xaxis/ordinal-true/
  17812. * True by default
  17813. * @sample {highstock} stock/xaxis/ordinal-false/
  17814. * False
  17815. *
  17816. * @type {boolean}
  17817. * @default true
  17818. * @since 1.1
  17819. * @product highstock
  17820. * @apioption xAxis.ordinal
  17821. */
  17822. /**
  17823. * Additional range on the right side of the xAxis. Works similar to
  17824. * `xAxis.maxPadding`, but value is set in milliseconds. Can be set for
  17825. * both main `xAxis` and the navigator's `xAxis`.
  17826. *
  17827. * @sample {highstock} stock/xaxis/overscroll/
  17828. * One minute overscroll with live data
  17829. *
  17830. * @type {number}
  17831. * @default 0
  17832. * @since 6.0.0
  17833. * @product highstock
  17834. * @apioption xAxis.overscroll
  17835. */
  17836. /**
  17837. * Refers to the index in the [panes](#panes) array. Used for circular
  17838. * gauges and polar charts. When the option is not set then first pane
  17839. * will be used.
  17840. *
  17841. * @sample highcharts/demo/gauge-vu-meter
  17842. * Two gauges with different center
  17843. *
  17844. * @type {number}
  17845. * @product highcharts
  17846. * @apioption xAxis.pane
  17847. */
  17848. /**
  17849. * The zoomed range to display when only defining one or none of `min`
  17850. * or `max`. For example, to show the latest month, a range of one month
  17851. * can be set.
  17852. *
  17853. * @sample {highstock} stock/xaxis/range/
  17854. * Setting a zoomed range when the rangeSelector is disabled
  17855. *
  17856. * @type {number}
  17857. * @product highstock
  17858. * @apioption xAxis.range
  17859. */
  17860. /**
  17861. * Whether to reverse the axis so that the highest number is closest
  17862. * to the origin. If the chart is inverted, the x axis is reversed by
  17863. * default.
  17864. *
  17865. * @sample {highcharts} highcharts/yaxis/reversed/
  17866. * Reversed Y axis
  17867. * @sample {highstock} stock/xaxis/reversed/
  17868. * Reversed Y axis
  17869. *
  17870. * @type {boolean}
  17871. * @default false
  17872. * @apioption xAxis.reversed
  17873. */
  17874. // reversed: false,
  17875. /**
  17876. * This option determines how stacks should be ordered within a group.
  17877. * For example reversed xAxis also reverses stacks, so first series
  17878. * comes last in a group. To keep order like for non-reversed xAxis
  17879. * enable this option.
  17880. *
  17881. * @sample {highcharts} highcharts/xaxis/reversedstacks/
  17882. * Reversed stacks comparison
  17883. * @sample {highstock} highcharts/xaxis/reversedstacks/
  17884. * Reversed stacks comparison
  17885. *
  17886. * @type {boolean}
  17887. * @default false
  17888. * @since 6.1.1
  17889. * @product highcharts highstock
  17890. * @apioption xAxis.reversedStacks
  17891. */
  17892. /**
  17893. * An optional scrollbar to display on the X axis in response to
  17894. * limiting the minimum and maximum of the axis values.
  17895. *
  17896. * In styled mode, all the presentational options for the scrollbar are
  17897. * replaced by the classes `.highcharts-scrollbar-thumb`,
  17898. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  17899. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  17900. *
  17901. * @sample {highstock} stock/yaxis/heatmap-scrollbars/
  17902. * Heatmap with both scrollbars
  17903. *
  17904. * @extends scrollbar
  17905. * @since 4.2.6
  17906. * @product highstock
  17907. * @apioption xAxis.scrollbar
  17908. */
  17909. /**
  17910. * Whether to show the axis line and title when the axis has no data.
  17911. *
  17912. * @sample {highcharts} highcharts/yaxis/showempty/
  17913. * When clicking the legend to hide series, one axis preserves
  17914. * line and title, the other doesn't
  17915. * @sample {highstock} highcharts/yaxis/showempty/
  17916. * When clicking the legend to hide series, one axis preserves
  17917. * line and title, the other doesn't
  17918. *
  17919. * @since 1.1
  17920. */
  17921. showEmpty: true,
  17922. /**
  17923. * Whether to show the first tick label.
  17924. *
  17925. * @sample {highcharts} highcharts/xaxis/showfirstlabel-false/
  17926. * Set to false on X axis
  17927. * @sample {highstock} stock/xaxis/showfirstlabel/
  17928. * Labels below plot lines on Y axis
  17929. *
  17930. * @type {boolean}
  17931. * @default true
  17932. * @apioption xAxis.showFirstLabel
  17933. */
  17934. /**
  17935. * Whether to show the last tick label. Defaults to `true` on cartesian
  17936. * charts, and `false` on polar charts.
  17937. *
  17938. * @sample {highcharts} highcharts/xaxis/showlastlabel-true/
  17939. * Set to true on X axis
  17940. * @sample {highstock} stock/xaxis/showfirstlabel/
  17941. * Labels below plot lines on Y axis
  17942. *
  17943. * @type {boolean}
  17944. * @default true
  17945. * @product highcharts highstock gantt
  17946. * @apioption xAxis.showLastLabel
  17947. */
  17948. /**
  17949. * A soft maximum for the axis. If the series data maximum is less than
  17950. * this, the axis will stay at this maximum, but if the series data
  17951. * maximum is higher, the axis will flex to show all data.
  17952. *
  17953. * @sample highcharts/yaxis/softmin-softmax/
  17954. * Soft min and max
  17955. *
  17956. * @type {number}
  17957. * @since 5.0.1
  17958. * @product highcharts highstock gantt
  17959. * @apioption xAxis.softMax
  17960. */
  17961. /**
  17962. * A soft minimum for the axis. If the series data minimum is greater
  17963. * than this, the axis will stay at this minimum, but if the series
  17964. * data minimum is lower, the axis will flex to show all data.
  17965. *
  17966. * @sample highcharts/yaxis/softmin-softmax/
  17967. * Soft min and max
  17968. *
  17969. * @type {number}
  17970. * @since 5.0.1
  17971. * @product highcharts highstock gantt
  17972. * @apioption xAxis.softMin
  17973. */
  17974. /**
  17975. * For datetime axes, this decides where to put the tick between weeks.
  17976. * 0 = Sunday, 1 = Monday.
  17977. *
  17978. * @sample {highcharts} highcharts/xaxis/startofweek-monday/
  17979. * Monday by default
  17980. * @sample {highcharts} highcharts/xaxis/startofweek-sunday/
  17981. * Sunday
  17982. * @sample {highstock} stock/xaxis/startofweek-1
  17983. * Monday by default
  17984. * @sample {highstock} stock/xaxis/startofweek-0
  17985. * Sunday
  17986. *
  17987. * @product highcharts highstock gantt
  17988. */
  17989. startOfWeek: 1,
  17990. /**
  17991. * Whether to force the axis to start on a tick. Use this option with
  17992. * the `minPadding` option to control the axis start.
  17993. *
  17994. * @productdesc {highstock}
  17995. * In Highstock, `startOnTick` is always `false` when the navigator
  17996. * is enabled, to prevent jumpy scrolling.
  17997. *
  17998. * @sample {highcharts} highcharts/xaxis/startontick-false/
  17999. * False by default
  18000. * @sample {highcharts} highcharts/xaxis/startontick-true/
  18001. * True
  18002. *
  18003. * @since 1.2.0
  18004. */
  18005. startOnTick: false,
  18006. /**
  18007. * The amount of ticks to draw on the axis. This opens up for aligning
  18008. * the ticks of multiple charts or panes within a chart. This option
  18009. * overrides the `tickPixelInterval` option.
  18010. *
  18011. * This option only has an effect on linear axes. Datetime, logarithmic
  18012. * or category axes are not affected.
  18013. *
  18014. * @sample {highcharts} highcharts/yaxis/tickamount/
  18015. * 8 ticks on Y axis
  18016. * @sample {highstock} highcharts/yaxis/tickamount/
  18017. * 8 ticks on Y axis
  18018. *
  18019. * @type {number}
  18020. * @since 4.1.0
  18021. * @product highcharts highstock gantt
  18022. * @apioption xAxis.tickAmount
  18023. */
  18024. /**
  18025. * The interval of the tick marks in axis units. When `undefined`, the
  18026. * tick interval is computed to approximately follow the
  18027. * [tickPixelInterval](#xAxis.tickPixelInterval) on linear and datetime
  18028. * axes. On categorized axes, a `undefined` tickInterval will default to
  18029. * 1, one category. Note that datetime axes are based on milliseconds,
  18030. * so for example an interval of one day is expressed as
  18031. * `24 * 3600 * 1000`.
  18032. *
  18033. * On logarithmic axes, the tickInterval is based on powers, so a
  18034. * tickInterval of 1 means one tick on each of 0.1, 1, 10, 100 etc. A
  18035. * tickInterval of 2 means a tick of 0.1, 10, 1000 etc. A tickInterval
  18036. * of 0.2 puts a tick on 0.1, 0.2, 0.4, 0.6, 0.8, 1, 2, 4, 6, 8, 10, 20,
  18037. * 40 etc.
  18038. *
  18039. *
  18040. * If the tickInterval is too dense for labels to be drawn, Highcharts
  18041. * may remove ticks.
  18042. *
  18043. * If the chart has multiple axes, the [alignTicks](#chart.alignTicks)
  18044. * option may interfere with the `tickInterval` setting.
  18045. *
  18046. * @see [tickPixelInterval](#xAxis.tickPixelInterval)
  18047. * @see [tickPositions](#xAxis.tickPositions)
  18048. * @see [tickPositioner](#xAxis.tickPositioner)
  18049. *
  18050. * @sample {highcharts} highcharts/xaxis/tickinterval-5/
  18051. * Tick interval of 5 on a linear axis
  18052. * @sample {highstock} stock/xaxis/tickinterval/
  18053. * Tick interval of 0.01 on Y axis
  18054. *
  18055. * @type {number}
  18056. * @apioption xAxis.tickInterval
  18057. */
  18058. /**
  18059. * The pixel length of the main tick marks.
  18060. *
  18061. * @sample {highcharts} highcharts/xaxis/ticklength/
  18062. * 20 px tick length on the X axis
  18063. * @sample {highstock} stock/xaxis/ticks/
  18064. * Formatted ticks on X axis
  18065. */
  18066. tickLength: 10,
  18067. /**
  18068. * If tickInterval is `null` this option sets the approximate pixel
  18069. * interval of the tick marks. Not applicable to categorized axis.
  18070. *
  18071. * The tick interval is also influenced by the [minTickInterval](
  18072. * #xAxis.minTickInterval) option, that, by default prevents ticks from
  18073. * being denser than the data points.
  18074. *
  18075. * @see [tickInterval](#xAxis.tickInterval)
  18076. * @see [tickPositioner](#xAxis.tickPositioner)
  18077. * @see [tickPositions](#xAxis.tickPositions)
  18078. *
  18079. * @sample {highcharts} highcharts/xaxis/tickpixelinterval-50/
  18080. * 50 px on X axis
  18081. * @sample {highstock} stock/xaxis/tickpixelinterval/
  18082. * 200 px on X axis
  18083. */
  18084. tickPixelInterval: 100,
  18085. /**
  18086. * For categorized axes only. If `on` the tick mark is placed in the
  18087. * center of the category, if `between` the tick mark is placed between
  18088. * categories. The default is `between` if the `tickInterval` is 1, else
  18089. * `on`.
  18090. *
  18091. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-between/
  18092. * "between" by default
  18093. * @sample {highcharts} highcharts/xaxis/tickmarkplacement-on/
  18094. * "on"
  18095. *
  18096. * @product highcharts gantt
  18097. * @validvalue ["on", "between"]
  18098. */
  18099. tickmarkPlacement: 'between',
  18100. /**
  18101. * The position of the major tick marks relative to the axis line.
  18102. * Can be one of `inside` and `outside`.
  18103. *
  18104. * @sample {highcharts} highcharts/xaxis/tickposition-outside/
  18105. * "outside" by default
  18106. * @sample {highcharts} highcharts/xaxis/tickposition-inside/
  18107. * "inside"
  18108. * @sample {highstock} stock/xaxis/ticks/
  18109. * Formatted ticks on X axis
  18110. *
  18111. * @validvalue ["inside", "outside"]
  18112. */
  18113. tickPosition: 'outside',
  18114. /**
  18115. * A callback function returning array defining where the ticks are
  18116. * laid out on the axis. This overrides the default behaviour of
  18117. * [tickPixelInterval](#xAxis.tickPixelInterval) and [tickInterval](
  18118. * #xAxis.tickInterval). The automatic tick positions are accessible
  18119. * through `this.tickPositions` and can be modified by the callback.
  18120. *
  18121. * @see [tickPositions](#xAxis.tickPositions)
  18122. *
  18123. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  18124. * Demo of tickPositions and tickPositioner
  18125. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  18126. * Demo of tickPositions and tickPositioner
  18127. *
  18128. * @type {Highcharts.AxisTickPositionerCallbackFunction}
  18129. * @apioption xAxis.tickPositioner
  18130. */
  18131. /**
  18132. * An array defining where the ticks are laid out on the axis. This
  18133. * overrides the default behaviour of [tickPixelInterval](
  18134. * #xAxis.tickPixelInterval) and [tickInterval](#xAxis.tickInterval).
  18135. *
  18136. * @see [tickPositioner](#xAxis.tickPositioner)
  18137. *
  18138. * @sample {highcharts} highcharts/xaxis/tickpositions-tickpositioner/
  18139. * Demo of tickPositions and tickPositioner
  18140. * @sample {highstock} highcharts/xaxis/tickpositions-tickpositioner/
  18141. * Demo of tickPositions and tickPositioner
  18142. *
  18143. * @type {Array<number>}
  18144. * @apioption xAxis.tickPositions
  18145. */
  18146. /**
  18147. * The pixel width of the major tick marks. Defaults to 0 on category
  18148. * axes, otherwise 1.
  18149. *
  18150. * In styled mode, the stroke width is given in the `.highcharts-tick`
  18151. * class, but in order for the element to be generated on category axes,
  18152. * the option must be explicitly set to 1.
  18153. *
  18154. * @sample {highcharts} highcharts/xaxis/tickwidth/
  18155. * 10 px width
  18156. * @sample {highcharts} highcharts/css/axis-grid/
  18157. * Styled mode
  18158. * @sample {highstock} stock/xaxis/ticks/
  18159. * Formatted ticks on X axis
  18160. * @sample {highstock} highcharts/css/axis-grid/
  18161. * Styled mode
  18162. *
  18163. * @type {undefined|number}
  18164. * @default {highstock} 1
  18165. * @default {highmaps} 0
  18166. * @apioption xAxis.tickWidth
  18167. */
  18168. /**
  18169. * The axis title, showing next to the axis line.
  18170. *
  18171. * @productdesc {highmaps}
  18172. * In Highmaps, the axis is hidden by default, but adding an axis title
  18173. * is still possible. X axis and Y axis titles will appear at the bottom
  18174. * and left by default.
  18175. */
  18176. title: {
  18177. /**
  18178. * Deprecated. Set the `text` to `null` to disable the title.
  18179. *
  18180. * @deprecated
  18181. * @type {boolean}
  18182. * @product highcharts
  18183. * @apioption xAxis.title.enabled
  18184. */
  18185. /**
  18186. * The pixel distance between the axis labels or line and the title.
  18187. * Defaults to 0 for horizontal axes, 10 for vertical
  18188. *
  18189. * @sample {highcharts} highcharts/xaxis/title-margin/
  18190. * Y axis title margin of 60
  18191. *
  18192. * @type {number}
  18193. * @apioption xAxis.title.margin
  18194. */
  18195. /**
  18196. * The distance of the axis title from the axis line. By default,
  18197. * this distance is computed from the offset width of the labels,
  18198. * the labels' distance from the axis and the title's margin.
  18199. * However when the offset option is set, it overrides all this.
  18200. *
  18201. * @sample {highcharts} highcharts/yaxis/title-offset/
  18202. * Place the axis title on top of the axis
  18203. * @sample {highstock} highcharts/yaxis/title-offset/
  18204. * Place the axis title on top of the Y axis
  18205. *
  18206. * @type {number}
  18207. * @since 2.2.0
  18208. * @apioption xAxis.title.offset
  18209. */
  18210. /**
  18211. * Whether to reserve space for the title when laying out the axis.
  18212. *
  18213. * @type {boolean}
  18214. * @default true
  18215. * @since 5.0.11
  18216. * @product highcharts highstock gantt
  18217. * @apioption xAxis.title.reserveSpace
  18218. */
  18219. /**
  18220. * The rotation of the text in degrees. 0 is horizontal, 270 is
  18221. * vertical reading from bottom to top.
  18222. *
  18223. * @sample {highcharts} highcharts/yaxis/title-offset/
  18224. * Horizontal
  18225. *
  18226. * @type {number}
  18227. * @default 0
  18228. * @apioption xAxis.title.rotation
  18229. */
  18230. /**
  18231. * The actual text of the axis title. It can contain basic HTML tags
  18232. * like `b`, `i` and `span` with style.
  18233. *
  18234. * @sample {highcharts} highcharts/xaxis/title-text/
  18235. * Custom HTML
  18236. * @sample {highstock} stock/xaxis/title-text/
  18237. * Titles for both axes
  18238. *
  18239. * @type {string|null}
  18240. * @apioption xAxis.title.text
  18241. */
  18242. /**
  18243. * Alignment of the text, can be `"left"`, `"right"` or `"center"`.
  18244. * Default alignment depends on the
  18245. * [title.align](xAxis.title.align):
  18246. *
  18247. * Horizontal axes:
  18248. * - for `align` = `"low"`, `textAlign` is set to `left`
  18249. * - for `align` = `"middle"`, `textAlign` is set to `center`
  18250. * - for `align` = `"high"`, `textAlign` is set to `right`
  18251. *
  18252. * Vertical axes:
  18253. * - for `align` = `"low"` and `opposite` = `true`, `textAlign` is
  18254. * set to `right`
  18255. * - for `align` = `"low"` and `opposite` = `false`, `textAlign` is
  18256. * set to `left`
  18257. * - for `align` = `"middle"`, `textAlign` is set to `center`
  18258. * - for `align` = `"high"` and `opposite` = `true` `textAlign` is
  18259. * set to `left`
  18260. * - for `align` = `"high"` and `opposite` = `false` `textAlign` is
  18261. * set to `right`
  18262. *
  18263. * @type {Highcharts.AlignValue}
  18264. * @apioption xAxis.title.textAlign
  18265. */
  18266. /**
  18267. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  18268. * to render the axis title.
  18269. *
  18270. * @type {boolean}
  18271. * @default false
  18272. * @product highcharts highstock gantt
  18273. * @apioption xAxis.title.useHTML
  18274. */
  18275. /**
  18276. * Horizontal pixel offset of the title position.
  18277. *
  18278. * @type {number}
  18279. * @default 0
  18280. * @since 4.1.6
  18281. * @product highcharts highstock gantt
  18282. * @apioption xAxis.title.x
  18283. */
  18284. /**
  18285. * Vertical pixel offset of the title position.
  18286. *
  18287. * @type {number}
  18288. * @product highcharts highstock gantt
  18289. * @apioption xAxis.title.y
  18290. */
  18291. /**
  18292. * Alignment of the title relative to the axis values. Possible
  18293. * values are "low", "middle" or "high".
  18294. *
  18295. * @sample {highcharts} highcharts/xaxis/title-align-low/
  18296. * "low"
  18297. * @sample {highcharts} highcharts/xaxis/title-align-center/
  18298. * "middle" by default
  18299. * @sample {highcharts} highcharts/xaxis/title-align-high/
  18300. * "high"
  18301. * @sample {highcharts} highcharts/yaxis/title-offset/
  18302. * Place the Y axis title on top of the axis
  18303. * @sample {highstock} stock/xaxis/title-align/
  18304. * Aligned to "high" value
  18305. *
  18306. * @type {Highcharts.AxisTitleAlignValue}
  18307. */
  18308. align: 'middle',
  18309. /**
  18310. * CSS styles for the title. If the title text is longer than the
  18311. * axis length, it will wrap to multiple lines by default. This can
  18312. * be customized by setting `textOverflow: 'ellipsis'`, by
  18313. * setting a specific `width` or by setting `whiteSpace: 'nowrap'`.
  18314. *
  18315. * In styled mode, the stroke width is given in the
  18316. * `.highcharts-axis-title` class.
  18317. *
  18318. * @sample {highcharts} highcharts/xaxis/title-style/
  18319. * Red
  18320. * @sample {highcharts} highcharts/css/axis/
  18321. * Styled mode
  18322. *
  18323. * @type {Highcharts.CSSObject}
  18324. */
  18325. style: {
  18326. /** @internal */
  18327. color: '#666666'
  18328. }
  18329. },
  18330. /**
  18331. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`
  18332. * or `category`. In a datetime axis, the numbers are given in
  18333. * milliseconds, and tick marks are placed on appropriate values like
  18334. * full hours or days. In a category axis, the
  18335. * [point names](#series.line.data.name) of the chart's series are used
  18336. * for categories, if not a [categories](#xAxis.categories) array is
  18337. * defined.
  18338. *
  18339. * @sample {highcharts} highcharts/xaxis/type-linear/
  18340. * Linear
  18341. * @sample {highcharts} highcharts/yaxis/type-log/
  18342. * Logarithmic
  18343. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  18344. * Logarithmic with minor grid lines
  18345. * @sample {highcharts} highcharts/xaxis/type-log-both/
  18346. * Logarithmic on two axes
  18347. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  18348. * Logarithmic with extension to emulate negative values
  18349. *
  18350. * @type {Highcharts.AxisTypeValue}
  18351. * @product highcharts gantt
  18352. */
  18353. type: 'linear',
  18354. /**
  18355. * If there are multiple axes on the same side of the chart, the pixel
  18356. * margin between the axes. Defaults to 0 on vertical axes, 15 on
  18357. * horizontal axes.
  18358. *
  18359. * @type {number}
  18360. * @since 7.0.3
  18361. * @apioption xAxis.margin
  18362. */
  18363. /**
  18364. * Applies only when the axis `type` is `category`. When `uniqueNames`
  18365. * is true, points are placed on the X axis according to their names.
  18366. * If the same point name is repeated in the same or another series,
  18367. * the point is placed on the same X position as other points of the
  18368. * same name. When `uniqueNames` is false, the points are laid out in
  18369. * increasing X positions regardless of their names, and the X axis
  18370. * category will take the name of the last point in each position.
  18371. *
  18372. * @sample {highcharts} highcharts/xaxis/uniquenames-true/
  18373. * True by default
  18374. * @sample {highcharts} highcharts/xaxis/uniquenames-false/
  18375. * False
  18376. *
  18377. * @type {boolean}
  18378. * @default true
  18379. * @since 4.2.7
  18380. * @product highcharts gantt
  18381. * @apioption xAxis.uniqueNames
  18382. */
  18383. /**
  18384. * Datetime axis only. An array determining what time intervals the
  18385. * ticks are allowed to fall on. Each array item is an array where the
  18386. * first value is the time unit and the second value another array of
  18387. * allowed multiples.
  18388. *
  18389. * Defaults to:
  18390. * ```js
  18391. * units: [[
  18392. * 'millisecond', // unit name
  18393. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  18394. * ], [
  18395. * 'second',
  18396. * [1, 2, 5, 10, 15, 30]
  18397. * ], [
  18398. * 'minute',
  18399. * [1, 2, 5, 10, 15, 30]
  18400. * ], [
  18401. * 'hour',
  18402. * [1, 2, 3, 4, 6, 8, 12]
  18403. * ], [
  18404. * 'day',
  18405. * [1]
  18406. * ], [
  18407. * 'week',
  18408. * [1]
  18409. * ], [
  18410. * 'month',
  18411. * [1, 3, 6]
  18412. * ], [
  18413. * 'year',
  18414. * null
  18415. * ]]
  18416. * ```
  18417. *
  18418. * @type {Array<Array<string,(Array<number>|null)>>}
  18419. * @product highcharts highstock gantt
  18420. * @apioption xAxis.units
  18421. */
  18422. /**
  18423. * Whether axis, including axis title, line, ticks and labels, should
  18424. * be visible.
  18425. *
  18426. * @type {boolean}
  18427. * @default true
  18428. * @since 4.1.9
  18429. * @product highcharts highstock gantt
  18430. * @apioption xAxis.visible
  18431. */
  18432. /**
  18433. * Color of the minor, secondary grid lines.
  18434. *
  18435. * In styled mode, the stroke width is given in the
  18436. * `.highcharts-minor-grid-line` class.
  18437. *
  18438. * @sample {highcharts} highcharts/yaxis/minorgridlinecolor/
  18439. * Bright grey lines from Y axis
  18440. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  18441. * Styled mode
  18442. * @sample {highstock} stock/xaxis/minorgridlinecolor/
  18443. * Bright grey lines from Y axis
  18444. *
  18445. * @type {Highcharts.ColorType}
  18446. * @default #f2f2f2
  18447. */
  18448. minorGridLineColor: '#f2f2f2',
  18449. /**
  18450. * Width of the minor, secondary grid lines.
  18451. *
  18452. * In styled mode, the stroke width is given in the
  18453. * `.highcharts-grid-line` class.
  18454. *
  18455. * @sample {highcharts} highcharts/yaxis/minorgridlinewidth/
  18456. * 2px lines from Y axis
  18457. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  18458. * Styled mode
  18459. * @sample {highstock} stock/xaxis/minorgridlinewidth/
  18460. * 2px lines from Y axis
  18461. */
  18462. minorGridLineWidth: 1,
  18463. /**
  18464. * Color for the minor tick marks.
  18465. *
  18466. * @sample {highcharts} highcharts/yaxis/minortickcolor/
  18467. * Black tick marks on Y axis
  18468. * @sample {highstock} stock/xaxis/minorticks/
  18469. * Black tick marks on Y axis
  18470. *
  18471. * @type {Highcharts.ColorType}
  18472. * @default #999999
  18473. */
  18474. minorTickColor: '#999999',
  18475. /**
  18476. * The color of the line marking the axis itself.
  18477. *
  18478. * In styled mode, the line stroke is given in the
  18479. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  18480. *
  18481. * @productdesc {highmaps}
  18482. * In Highmaps, the axis line is hidden by default, because the axis is
  18483. * not visible by default.
  18484. *
  18485. * @sample {highcharts} highcharts/yaxis/linecolor/
  18486. * A red line on Y axis
  18487. * @sample {highcharts|highstock} highcharts/css/axis/
  18488. * Axes in styled mode
  18489. * @sample {highstock} stock/xaxis/linecolor/
  18490. * A red line on X axis
  18491. *
  18492. * @type {Highcharts.ColorType}
  18493. * @default #ccd6eb
  18494. */
  18495. lineColor: '#ccd6eb',
  18496. /**
  18497. * The width of the line marking the axis itself.
  18498. *
  18499. * In styled mode, the stroke width is given in the
  18500. * `.highcharts-axis-line` or `.highcharts-xaxis-line` class.
  18501. *
  18502. * @sample {highcharts} highcharts/yaxis/linecolor/
  18503. * A 1px line on Y axis
  18504. * @sample {highcharts|highstock} highcharts/css/axis/
  18505. * Axes in styled mode
  18506. * @sample {highstock} stock/xaxis/linewidth/
  18507. * A 2px line on X axis
  18508. *
  18509. * @default {highcharts|highstock} 1
  18510. * @default {highmaps} 0
  18511. */
  18512. lineWidth: 1,
  18513. /**
  18514. * Color of the grid lines extending the ticks across the plot area.
  18515. *
  18516. * In styled mode, the stroke is given in the `.highcharts-grid-line`
  18517. * class.
  18518. *
  18519. * @productdesc {highmaps}
  18520. * In Highmaps, the grid lines are hidden by default.
  18521. *
  18522. * @sample {highcharts} highcharts/yaxis/gridlinecolor/
  18523. * Green lines
  18524. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  18525. * Styled mode
  18526. * @sample {highstock} stock/xaxis/gridlinecolor/
  18527. * Green lines
  18528. *
  18529. * @type {Highcharts.ColorType}
  18530. * @default #e6e6e6
  18531. */
  18532. gridLineColor: '#e6e6e6',
  18533. // gridLineDashStyle: 'solid',
  18534. /**
  18535. * The width of the grid lines extending the ticks across the plot area.
  18536. *
  18537. * In styled mode, the stroke width is given in the
  18538. * `.highcharts-grid-line` class.
  18539. *
  18540. * @sample {highcharts} highcharts/yaxis/gridlinewidth/
  18541. * 2px lines
  18542. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  18543. * Styled mode
  18544. * @sample {highstock} stock/xaxis/gridlinewidth/
  18545. * 2px lines
  18546. *
  18547. * @type {number}
  18548. * @default 0
  18549. * @apioption xAxis.gridLineWidth
  18550. */
  18551. // gridLineWidth: 0,
  18552. /**
  18553. * The height as the vertical axis. If it's a number, it is
  18554. * interpreted as pixels.
  18555. *
  18556. * Since Highcharts 2: If it's a percentage string, it is interpreted
  18557. * as percentages of the total plot height.
  18558. *
  18559. * @type {number|string}
  18560. * @product highcharts highstock
  18561. * @apioption xAxis.height
  18562. */
  18563. /**
  18564. * The width as the horizontal axis. If it's a number, it is interpreted
  18565. * as pixels.
  18566. *
  18567. * Since Highcharts v5.0.13: If it's a percentage string, it is
  18568. * interpreted as percentages of the total plot width.
  18569. *
  18570. * @type {number|string}
  18571. * @product highcharts highstock
  18572. * @apioption xAxis.width
  18573. */
  18574. /**
  18575. * Color for the main tick marks.
  18576. *
  18577. * In styled mode, the stroke is given in the `.highcharts-tick`
  18578. * class.
  18579. *
  18580. * @sample {highcharts} highcharts/xaxis/tickcolor/
  18581. * Red ticks on X axis
  18582. * @sample {highcharts|highstock} highcharts/css/axis-grid/
  18583. * Styled mode
  18584. * @sample {highstock} stock/xaxis/ticks/
  18585. * Formatted ticks on X axis
  18586. *
  18587. * @type {Highcharts.ColorType}
  18588. * @default #ccd6eb
  18589. */
  18590. tickColor: '#ccd6eb'
  18591. // tickWidth: 1
  18592. };
  18593. /**
  18594. * The Y axis or value axis. Normally this is the vertical axis,
  18595. * though if the chart is inverted this is the horizontal axis.
  18596. * In case of multiple axes, the yAxis node is an array of
  18597. * configuration objects.
  18598. *
  18599. * See [the Axis object](/class-reference/Highcharts.Axis) for programmatic
  18600. * access to the axis.
  18601. *
  18602. * @type {*|Array<*>}
  18603. * @extends xAxis
  18604. * @excluding currentDateIndicator,ordinal,overscroll
  18605. * @optionparent yAxis
  18606. *
  18607. * @private
  18608. */
  18609. Axis.defaultYAxisOptions = {
  18610. /**
  18611. * The type of axis. Can be one of `linear`, `logarithmic`, `datetime`,
  18612. * `category` or `treegrid`. Defaults to `treegrid` for Gantt charts,
  18613. * `linear` for other chart types.
  18614. *
  18615. * In a datetime axis, the numbers are given in milliseconds, and tick
  18616. * marks are placed on appropriate values, like full hours or days. In a
  18617. * category or treegrid axis, the [point names](#series.line.data.name)
  18618. * of the chart's series are used for categories, if a
  18619. * [categories](#xAxis.categories) array is not defined.
  18620. *
  18621. * @sample {highcharts} highcharts/yaxis/type-log-minorgrid/
  18622. * Logarithmic with minor grid lines
  18623. * @sample {highcharts} highcharts/yaxis/type-log-negative/
  18624. * Logarithmic with extension to emulate negative values
  18625. * @sample {gantt} gantt/treegrid-axis/demo
  18626. * Treegrid axis
  18627. *
  18628. * @type {Highcharts.AxisTypeValue}
  18629. * @default {highcharts} linear
  18630. * @default {gantt} treegrid
  18631. * @product highcharts gantt
  18632. * @apioption yAxis.type
  18633. */
  18634. /**
  18635. * The height of the Y axis. If it's a number, it is interpreted as
  18636. * pixels.
  18637. *
  18638. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  18639. * percentages of the total plot height.
  18640. *
  18641. * @see [yAxis.top](#yAxis.top)
  18642. *
  18643. * @sample {highstock} stock/demo/candlestick-and-volume/
  18644. * Percentage height panes
  18645. *
  18646. * @type {number|string}
  18647. * @product highcharts highstock
  18648. * @apioption yAxis.height
  18649. */
  18650. /**
  18651. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  18652. * to represent the maximum value of the Y axis.
  18653. *
  18654. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  18655. * Min and max colors
  18656. *
  18657. * @type {Highcharts.ColorType}
  18658. * @default #003399
  18659. * @since 4.0
  18660. * @product highcharts
  18661. * @apioption yAxis.maxColor
  18662. */
  18663. /**
  18664. * Solid gauge only. Unless [stops](#yAxis.stops) are set, the color
  18665. * to represent the minimum value of the Y axis.
  18666. *
  18667. * @sample {highcharts} highcharts/yaxis/mincolor-maxcolor/
  18668. * Min and max color
  18669. *
  18670. * @type {Highcharts.ColorType}
  18671. * @default #e6ebf5
  18672. * @since 4.0
  18673. * @product highcharts
  18674. * @apioption yAxis.minColor
  18675. */
  18676. /**
  18677. * Whether to reverse the axis so that the highest number is closest
  18678. * to the origin.
  18679. *
  18680. * @sample {highcharts} highcharts/yaxis/reversed/
  18681. * Reversed Y axis
  18682. * @sample {highstock} stock/xaxis/reversed/
  18683. * Reversed Y axis
  18684. *
  18685. * @type {boolean}
  18686. * @default {highcharts} false
  18687. * @default {highstock} false
  18688. * @default {highmaps} true
  18689. * @default {gantt} true
  18690. * @apioption yAxis.reversed
  18691. */
  18692. /**
  18693. * If `true`, the first series in a stack will be drawn on top in a
  18694. * positive, non-reversed Y axis. If `false`, the first series is in
  18695. * the base of the stack.
  18696. *
  18697. * @sample {highcharts} highcharts/yaxis/reversedstacks-false/
  18698. * Non-reversed stacks
  18699. * @sample {highstock} highcharts/yaxis/reversedstacks-false/
  18700. * Non-reversed stacks
  18701. *
  18702. * @type {boolean}
  18703. * @default true
  18704. * @since 3.0.10
  18705. * @product highcharts highstock
  18706. * @apioption yAxis.reversedStacks
  18707. */
  18708. /**
  18709. * Solid gauge series only. Color stops for the solid gauge. Use this
  18710. * in cases where a linear gradient between a `minColor` and `maxColor`
  18711. * is not sufficient. The stops is an array of tuples, where the first
  18712. * item is a float between 0 and 1 assigning the relative position in
  18713. * the gradient, and the second item is the color.
  18714. *
  18715. * For solid gauges, the Y axis also inherits the concept of
  18716. * [data classes](https://api.highcharts.com/highmaps#colorAxis.dataClasses)
  18717. * from the Highmaps color axis.
  18718. *
  18719. * @see [minColor](#yAxis.minColor)
  18720. * @see [maxColor](#yAxis.maxColor)
  18721. *
  18722. * @sample {highcharts} highcharts/demo/gauge-solid/
  18723. * True by default
  18724. *
  18725. * @type {Array<Highcharts.GradientColorStopObject>}
  18726. * @since 4.0
  18727. * @product highcharts
  18728. * @apioption yAxis.stops
  18729. */
  18730. /**
  18731. * The pixel width of the major tick marks.
  18732. *
  18733. * @sample {highcharts} highcharts/xaxis/tickwidth/ 10 px width
  18734. * @sample {highstock} stock/xaxis/ticks/ Formatted ticks on X axis
  18735. *
  18736. * @type {number}
  18737. * @default 0
  18738. * @product highcharts highstock gantt
  18739. * @apioption yAxis.tickWidth
  18740. */
  18741. /**
  18742. * Angular gauges and solid gauges only.
  18743. * The label's pixel distance from the perimeter of the plot area.
  18744. *
  18745. * Since v7.1.2: If it's a percentage string, it is interpreted the
  18746. * same as [series.radius](#plotOptions.gauge.radius), so label can be
  18747. * aligned under the gauge's shape.
  18748. *
  18749. * @sample {highcharts} highcharts/yaxis/labels-distance/
  18750. * Labels centered under the arc
  18751. *
  18752. * @type {number|string}
  18753. * @default -25
  18754. * @product highcharts
  18755. * @apioption yAxis.labels.distance
  18756. */
  18757. /**
  18758. * The y position offset of the label relative to the tick position
  18759. * on the axis.
  18760. *
  18761. * @sample {highcharts} highcharts/xaxis/labels-x/
  18762. * Y axis labels placed on grid lines
  18763. *
  18764. * @type {number}
  18765. * @default {highcharts} 3
  18766. * @default {highstock} -2
  18767. * @default {highmaps} 3
  18768. * @apioption yAxis.labels.y
  18769. */
  18770. /**
  18771. * Whether to force the axis to end on a tick. Use this option with
  18772. * the `maxPadding` option to control the axis end.
  18773. *
  18774. * This option is always disabled, when panning type is
  18775. * either `y` or `xy`.
  18776. *
  18777. * @see [type](#chart.panning.type)
  18778. *
  18779. *
  18780. * @sample {highcharts} highcharts/chart/reflow-true/
  18781. * True by default
  18782. * @sample {highcharts} highcharts/yaxis/endontick/
  18783. * False
  18784. * @sample {highstock} stock/demo/basic-line/
  18785. * True by default
  18786. * @sample {highstock} stock/xaxis/endontick/
  18787. * False for Y axis
  18788. *
  18789. * @since 1.2.0
  18790. */
  18791. endOnTick: true,
  18792. /**
  18793. * Padding of the max value relative to the length of the axis. A
  18794. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  18795. * when you don't want the highest data value to appear on the edge
  18796. * of the plot area. When the axis' `max` option is set or a max extreme
  18797. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  18798. *
  18799. * Also the `softThreshold` option takes precedence over `maxPadding`,
  18800. * so if the data is tangent to the threshold, `maxPadding` may not
  18801. * apply unless `softThreshold` is set to false.
  18802. *
  18803. * @sample {highcharts} highcharts/yaxis/maxpadding-02/
  18804. * Max padding of 0.2
  18805. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  18806. * Greater min- and maxPadding
  18807. *
  18808. * @since 1.2.0
  18809. * @product highcharts highstock gantt
  18810. */
  18811. maxPadding: 0.05,
  18812. /**
  18813. * Padding of the min value relative to the length of the axis. A
  18814. * padding of 0.05 will make a 100px axis 5px longer. This is useful
  18815. * when you don't want the lowest data value to appear on the edge
  18816. * of the plot area. When the axis' `min` option is set or a max extreme
  18817. * is set using `axis.setExtremes()`, the maxPadding will be ignored.
  18818. *
  18819. * Also the `softThreshold` option takes precedence over `minPadding`,
  18820. * so if the data is tangent to the threshold, `minPadding` may not
  18821. * apply unless `softThreshold` is set to false.
  18822. *
  18823. * @sample {highcharts} highcharts/yaxis/minpadding/
  18824. * Min padding of 0.2
  18825. * @sample {highstock} stock/xaxis/minpadding-maxpadding/
  18826. * Greater min- and maxPadding
  18827. *
  18828. * @since 1.2.0
  18829. * @product highcharts highstock gantt
  18830. */
  18831. minPadding: 0.05,
  18832. /**
  18833. * @productdesc {highstock}
  18834. * In Highstock 1.x, the Y axis was placed on the left side by default.
  18835. *
  18836. * @sample {highcharts} highcharts/yaxis/opposite/
  18837. * Secondary Y axis opposite
  18838. * @sample {highstock} stock/xaxis/opposite/
  18839. * Y axis on left side
  18840. *
  18841. * @type {boolean}
  18842. * @default {highstock} true
  18843. * @default {highcharts} false
  18844. * @product highstock highcharts gantt
  18845. * @apioption yAxis.opposite
  18846. */
  18847. /**
  18848. * @see [tickInterval](#xAxis.tickInterval)
  18849. * @see [tickPositioner](#xAxis.tickPositioner)
  18850. * @see [tickPositions](#xAxis.tickPositions)
  18851. */
  18852. tickPixelInterval: 72,
  18853. showLastLabel: true,
  18854. /**
  18855. * @extends xAxis.labels
  18856. */
  18857. labels: {
  18858. /**
  18859. * What part of the string the given position is anchored to. Can
  18860. * be one of `"left"`, `"center"` or `"right"`. The exact position
  18861. * also depends on the `labels.x` setting.
  18862. *
  18863. * Angular gauges and solid gauges defaults to `"center"`.
  18864. * Solid gauges with two labels have additional option `"auto"`
  18865. * for automatic horizontal and vertical alignment.
  18866. *
  18867. * @see [yAxis.labels.distance](#yAxis.labels.distance)
  18868. *
  18869. * @sample {highcharts} highcharts/yaxis/labels-align-left/
  18870. * Left
  18871. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  18872. * Solid gauge labels auto aligned
  18873. *
  18874. * @type {Highcharts.AlignValue}
  18875. * @default {highcharts|highmaps} right
  18876. * @default {highstock} left
  18877. * @apioption yAxis.labels.align
  18878. */
  18879. /**
  18880. * The x position offset of the label relative to the tick position
  18881. * on the axis. Defaults to -15 for left axis, 15 for right axis.
  18882. *
  18883. * @sample {highcharts} highcharts/xaxis/labels-x/
  18884. * Y axis labels placed on grid lines
  18885. */
  18886. x: -8
  18887. },
  18888. /**
  18889. * @productdesc {highmaps}
  18890. * In Highmaps, the axis line is hidden by default, because the axis is
  18891. * not visible by default.
  18892. *
  18893. * @type {Highcharts.ColorType}
  18894. * @apioption yAxis.lineColor
  18895. */
  18896. /**
  18897. * @sample {highcharts} highcharts/yaxis/max-200/
  18898. * Y axis max of 200
  18899. * @sample {highcharts} highcharts/yaxis/max-logarithmic/
  18900. * Y axis max on logarithmic axis
  18901. * @sample {highstock} stock/yaxis/min-max/
  18902. * Fixed min and max on Y axis
  18903. * @sample {highmaps} maps/axis/min-max/
  18904. * Pre-zoomed to a specific area
  18905. *
  18906. * @apioption yAxis.max
  18907. */
  18908. /**
  18909. * @sample {highcharts} highcharts/yaxis/min-startontick-false/
  18910. * -50 with startOnTick to false
  18911. * @sample {highcharts} highcharts/yaxis/min-startontick-true/
  18912. * -50 with startOnTick true by default
  18913. * @sample {highstock} stock/yaxis/min-max/
  18914. * Fixed min and max on Y axis
  18915. * @sample {highmaps} maps/axis/min-max/
  18916. * Pre-zoomed to a specific area
  18917. *
  18918. * @apioption yAxis.min
  18919. */
  18920. /**
  18921. * An optional scrollbar to display on the Y axis in response to
  18922. * limiting the minimum an maximum of the axis values.
  18923. *
  18924. * In styled mode, all the presentational options for the scrollbar
  18925. * are replaced by the classes `.highcharts-scrollbar-thumb`,
  18926. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  18927. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  18928. *
  18929. * @sample {highstock} stock/yaxis/scrollbar/
  18930. * Scrollbar on the Y axis
  18931. *
  18932. * @extends scrollbar
  18933. * @since 4.2.6
  18934. * @product highstock
  18935. * @excluding height
  18936. * @apioption yAxis.scrollbar
  18937. */
  18938. /**
  18939. * Enable the scrollbar on the Y axis.
  18940. *
  18941. * @sample {highstock} stock/yaxis/scrollbar/
  18942. * Enabled on Y axis
  18943. *
  18944. * @type {boolean}
  18945. * @default false
  18946. * @since 4.2.6
  18947. * @product highstock
  18948. * @apioption yAxis.scrollbar.enabled
  18949. */
  18950. /**
  18951. * Pixel margin between the scrollbar and the axis elements.
  18952. *
  18953. * @type {number}
  18954. * @default 10
  18955. * @since 4.2.6
  18956. * @product highstock
  18957. * @apioption yAxis.scrollbar.margin
  18958. */
  18959. /**
  18960. * Whether to show the scrollbar when it is fully zoomed out at max
  18961. * range. Setting it to `false` on the Y axis makes the scrollbar stay
  18962. * hidden until the user zooms in, like common in browsers.
  18963. *
  18964. * @type {boolean}
  18965. * @default true
  18966. * @since 4.2.6
  18967. * @product highstock
  18968. * @apioption yAxis.scrollbar.showFull
  18969. */
  18970. /**
  18971. * The width of a vertical scrollbar or height of a horizontal
  18972. * scrollbar. Defaults to 20 on touch devices.
  18973. *
  18974. * @type {number}
  18975. * @default 14
  18976. * @since 4.2.6
  18977. * @product highstock
  18978. * @apioption yAxis.scrollbar.size
  18979. */
  18980. /**
  18981. * Z index of the scrollbar elements.
  18982. *
  18983. * @type {number}
  18984. * @default 3
  18985. * @since 4.2.6
  18986. * @product highstock
  18987. * @apioption yAxis.scrollbar.zIndex
  18988. */
  18989. /**
  18990. * A soft maximum for the axis. If the series data maximum is less
  18991. * than this, the axis will stay at this maximum, but if the series
  18992. * data maximum is higher, the axis will flex to show all data.
  18993. *
  18994. * **Note**: The [series.softThreshold](
  18995. * #plotOptions.series.softThreshold) option takes precedence over this
  18996. * option.
  18997. *
  18998. * @sample highcharts/yaxis/softmin-softmax/
  18999. * Soft min and max
  19000. *
  19001. * @type {number}
  19002. * @since 5.0.1
  19003. * @product highcharts highstock gantt
  19004. * @apioption yAxis.softMax
  19005. */
  19006. /**
  19007. * A soft minimum for the axis. If the series data minimum is greater
  19008. * than this, the axis will stay at this minimum, but if the series
  19009. * data minimum is lower, the axis will flex to show all data.
  19010. *
  19011. * **Note**: The [series.softThreshold](
  19012. * #plotOptions.series.softThreshold) option takes precedence over this
  19013. * option.
  19014. *
  19015. * @sample highcharts/yaxis/softmin-softmax/
  19016. * Soft min and max
  19017. *
  19018. * @type {number}
  19019. * @since 5.0.1
  19020. * @product highcharts highstock gantt
  19021. * @apioption yAxis.softMin
  19022. */
  19023. /**
  19024. * Defines the horizontal alignment of the stack total label. Can be one
  19025. * of `"left"`, `"center"` or `"right"`. The default value is calculated
  19026. * at runtime and depends on orientation and whether the stack is
  19027. * positive or negative.
  19028. *
  19029. * @sample {highcharts} highcharts/yaxis/stacklabels-align-left/
  19030. * Aligned to the left
  19031. * @sample {highcharts} highcharts/yaxis/stacklabels-align-center/
  19032. * Aligned in center
  19033. * @sample {highcharts} highcharts/yaxis/stacklabels-align-right/
  19034. * Aligned to the right
  19035. *
  19036. * @type {Highcharts.AlignValue}
  19037. * @since 2.1.5
  19038. * @product highcharts
  19039. * @apioption yAxis.stackLabels.align
  19040. */
  19041. /**
  19042. * A format string for the data label. Available variables are the same
  19043. * as for `formatter`.
  19044. *
  19045. * @type {string}
  19046. * @default {total}
  19047. * @since 3.0.2
  19048. * @product highcharts highstock
  19049. * @apioption yAxis.stackLabels.format
  19050. */
  19051. /**
  19052. * Rotation of the labels in degrees.
  19053. *
  19054. * @sample {highcharts} highcharts/yaxis/stacklabels-rotation/
  19055. * Labels rotated 45°
  19056. *
  19057. * @type {number}
  19058. * @default 0
  19059. * @since 2.1.5
  19060. * @product highcharts
  19061. * @apioption yAxis.stackLabels.rotation
  19062. */
  19063. /**
  19064. * The text alignment for the label. While `align` determines where the
  19065. * texts anchor point is placed with regards to the stack, `textAlign`
  19066. * determines how the text is aligned against its anchor point. Possible
  19067. * values are `"left"`, `"center"` and `"right"`. The default value is
  19068. * calculated at runtime and depends on orientation and whether the
  19069. * stack is positive or negative.
  19070. *
  19071. * @sample {highcharts} highcharts/yaxis/stacklabels-textalign-left/
  19072. * Label in center position but text-aligned left
  19073. *
  19074. * @type {Highcharts.AlignValue}
  19075. * @since 2.1.5
  19076. * @product highcharts
  19077. * @apioption yAxis.stackLabels.textAlign
  19078. */
  19079. /**
  19080. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  19081. * to render the labels.
  19082. *
  19083. * @type {boolean}
  19084. * @default false
  19085. * @since 3.0
  19086. * @product highcharts highstock
  19087. * @apioption yAxis.stackLabels.useHTML
  19088. */
  19089. /**
  19090. * Defines the vertical alignment of the stack total label. Can be one
  19091. * of `"top"`, `"middle"` or `"bottom"`. The default value is calculated
  19092. * at runtime and depends on orientation and whether the stack is
  19093. * positive or negative.
  19094. *
  19095. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-top/
  19096. * Vertically aligned top
  19097. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-middle/
  19098. * Vertically aligned middle
  19099. * @sample {highcharts} highcharts/yaxis/stacklabels-verticalalign-bottom/
  19100. * Vertically aligned bottom
  19101. *
  19102. * @type {Highcharts.VerticalAlignValue}
  19103. * @since 2.1.5
  19104. * @product highcharts
  19105. * @apioption yAxis.stackLabels.verticalAlign
  19106. */
  19107. /**
  19108. * The x position offset of the label relative to the left of the
  19109. * stacked bar. The default value is calculated at runtime and depends
  19110. * on orientation and whether the stack is positive or negative.
  19111. *
  19112. * @sample {highcharts} highcharts/yaxis/stacklabels-x/
  19113. * Stack total labels with x offset
  19114. *
  19115. * @type {number}
  19116. * @since 2.1.5
  19117. * @product highcharts
  19118. * @apioption yAxis.stackLabels.x
  19119. */
  19120. /**
  19121. * The y position offset of the label relative to the tick position
  19122. * on the axis. The default value is calculated at runtime and depends
  19123. * on orientation and whether the stack is positive or negative.
  19124. *
  19125. * @sample {highcharts} highcharts/yaxis/stacklabels-y/
  19126. * Stack total labels with y offset
  19127. *
  19128. * @type {number}
  19129. * @since 2.1.5
  19130. * @product highcharts
  19131. * @apioption yAxis.stackLabels.y
  19132. */
  19133. /**
  19134. * Whether to force the axis to start on a tick. Use this option with
  19135. * the `maxPadding` option to control the axis start.
  19136. *
  19137. * This option is always disabled, when panning type is
  19138. * either `y` or `xy`.
  19139. *
  19140. * @see [type](#chart.panning.type)
  19141. *
  19142. * @sample {highcharts} highcharts/xaxis/startontick-false/
  19143. * False by default
  19144. * @sample {highcharts} highcharts/xaxis/startontick-true/
  19145. * True
  19146. * @sample {highstock} stock/xaxis/endontick/
  19147. * False for Y axis
  19148. *
  19149. * @since 1.2.0
  19150. * @product highcharts highstock gantt
  19151. */
  19152. startOnTick: true,
  19153. title: {
  19154. /**
  19155. * The pixel distance between the axis labels and the title.
  19156. * Positive values are outside the axis line, negative are inside.
  19157. *
  19158. * @sample {highcharts} highcharts/xaxis/title-margin/
  19159. * Y axis title margin of 60
  19160. *
  19161. * @type {number}
  19162. * @default 40
  19163. * @apioption yAxis.title.margin
  19164. */
  19165. /**
  19166. * The rotation of the text in degrees. 0 is horizontal, 270 is
  19167. * vertical reading from bottom to top.
  19168. *
  19169. * @sample {highcharts} highcharts/yaxis/title-offset/
  19170. * Horizontal
  19171. */
  19172. rotation: 270,
  19173. /**
  19174. * The actual text of the axis title. Horizontal texts can contain
  19175. * HTML, but rotated texts are painted using vector techniques and
  19176. * must be clean text. The Y axis title is disabled by setting the
  19177. * `text` option to `undefined`.
  19178. *
  19179. * @sample {highcharts} highcharts/xaxis/title-text/
  19180. * Custom HTML
  19181. *
  19182. * @type {string|null}
  19183. * @default {highcharts} Values
  19184. * @default {highstock} undefined
  19185. * @product highcharts highstock gantt
  19186. */
  19187. text: 'Values'
  19188. },
  19189. /**
  19190. * The top position of the Y axis. If it's a number, it is interpreted
  19191. * as pixel position relative to the chart.
  19192. *
  19193. * Since Highcharts 2: If it's a percentage string, it is interpreted as
  19194. * percentages of the plot height, offset from plot area top.
  19195. *
  19196. * @see [yAxis.height](#yAxis.height)
  19197. *
  19198. * @sample {highstock} stock/demo/candlestick-and-volume/
  19199. * Percentage height panes
  19200. *
  19201. * @type {number|string}
  19202. * @product highcharts highstock
  19203. * @apioption yAxis.top
  19204. */
  19205. /**
  19206. * The stack labels show the total value for each bar in a stacked
  19207. * column or bar chart. The label will be placed on top of positive
  19208. * columns and below negative columns. In case of an inverted column
  19209. * chart or a bar chart the label is placed to the right of positive
  19210. * bars and to the left of negative bars.
  19211. *
  19212. * @product highcharts
  19213. */
  19214. stackLabels: {
  19215. /**
  19216. * Allow the stack labels to overlap.
  19217. *
  19218. * @sample {highcharts} highcharts/yaxis/stacklabels-allowoverlap-false/
  19219. * Default false
  19220. *
  19221. * @since 5.0.13
  19222. * @product highcharts
  19223. */
  19224. allowOverlap: false,
  19225. /**
  19226. * The background color or gradient for the stack label.
  19227. *
  19228. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  19229. * Stack labels box options
  19230. * @type {Highcharts.ColorType}
  19231. * @since 8.1.0
  19232. * @apioption yAxis.stackLabels.backgroundColor
  19233. */
  19234. /**
  19235. * The border color for the stack label. Defaults to `undefined`.
  19236. *
  19237. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  19238. * Stack labels box options
  19239. * @type {Highcharts.ColorType}
  19240. * @since 8.1.0
  19241. * @apioption yAxis.stackLabels.borderColor
  19242. */
  19243. /**
  19244. * The border radius in pixels for the stack label.
  19245. *
  19246. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  19247. * Stack labels box options
  19248. * @type {number}
  19249. * @default 0
  19250. * @since 8.1.0
  19251. * @apioption yAxis.stackLabels.borderRadius
  19252. */
  19253. /**
  19254. * The border width in pixels for the stack label.
  19255. *
  19256. * @sample {highcharts} highcharts/yaxis/stacklabels-box/
  19257. * Stack labels box options
  19258. * @type {number}
  19259. * @default 0
  19260. * @since 8.1.0
  19261. * @apioption yAxis.stackLabels.borderWidth
  19262. */
  19263. /**
  19264. * Enable or disable the stack total labels.
  19265. *
  19266. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled/
  19267. * Enabled stack total labels
  19268. * @sample {highcharts} highcharts/yaxis/stacklabels-enabled-waterfall/
  19269. * Enabled stack labels in waterfall chart
  19270. *
  19271. * @since 2.1.5
  19272. * @product highcharts
  19273. */
  19274. enabled: false,
  19275. /**
  19276. * Whether to hide stack labels that are outside the plot area.
  19277. * By default, the stack label is moved
  19278. * inside the plot area according to the
  19279. * [overflow](/highcharts/#yAxis/stackLabels/overflow)
  19280. * option.
  19281. *
  19282. * @type {boolean}
  19283. * @since 7.1.3
  19284. */
  19285. crop: true,
  19286. /**
  19287. * How to handle stack total labels that flow outside the plot area.
  19288. * The default is set to `"justify"`,
  19289. * which aligns them inside the plot area.
  19290. * For columns and bars, this means it will be moved inside the bar.
  19291. * To display stack labels outside the plot area,
  19292. * set `crop` to `false` and `overflow` to `"allow"`.
  19293. *
  19294. * @sample highcharts/yaxis/stacklabels-overflow/
  19295. * Stack labels flows outside the plot area.
  19296. *
  19297. * @type {Highcharts.DataLabelsOverflowValue}
  19298. * @since 7.1.3
  19299. */
  19300. overflow: 'justify',
  19301. /* eslint-disable valid-jsdoc */
  19302. /**
  19303. * Callback JavaScript function to format the label. The value is
  19304. * given by `this.total`.
  19305. *
  19306. * @sample {highcharts} highcharts/yaxis/stacklabels-formatter/
  19307. * Added units to stack total value
  19308. *
  19309. * @type {Highcharts.FormatterCallbackFunction<Highcharts.StackItemObject>}
  19310. * @since 2.1.5
  19311. * @product highcharts
  19312. */
  19313. formatter: function () {
  19314. var numberFormatter = this.axis.chart.numberFormatter;
  19315. /* eslint-enable valid-jsdoc */
  19316. return numberFormatter(this.total, -1);
  19317. },
  19318. /**
  19319. * CSS styles for the label.
  19320. *
  19321. * In styled mode, the styles are set in the
  19322. * `.highcharts-stack-label` class.
  19323. *
  19324. * @sample {highcharts} highcharts/yaxis/stacklabels-style/
  19325. * Red stack total labels
  19326. *
  19327. * @type {Highcharts.CSSObject}
  19328. * @since 2.1.5
  19329. * @product highcharts
  19330. */
  19331. style: {
  19332. /** @internal */
  19333. color: '#000000',
  19334. /** @internal */
  19335. fontSize: '11px',
  19336. /** @internal */
  19337. fontWeight: 'bold',
  19338. /** @internal */
  19339. textOutline: '1px contrast'
  19340. }
  19341. },
  19342. gridLineWidth: 1,
  19343. lineWidth: 0
  19344. // tickWidth: 0
  19345. };
  19346. /**
  19347. * The Z axis or depth axis for 3D plots.
  19348. *
  19349. * See the [Axis class](/class-reference/Highcharts.Axis) for programmatic
  19350. * access to the axis.
  19351. *
  19352. * @sample {highcharts} highcharts/3d/scatter-zaxis-categories/
  19353. * Z-Axis with Categories
  19354. * @sample {highcharts} highcharts/3d/scatter-zaxis-grid/
  19355. * Z-Axis with styling
  19356. *
  19357. * @type {*|Array<*>}
  19358. * @extends xAxis
  19359. * @since 5.0.0
  19360. * @product highcharts
  19361. * @excluding breaks, crosshair, height, left, lineColor, lineWidth,
  19362. * nameToX, showEmpty, top, width
  19363. * @apioption zAxis
  19364. *
  19365. * @private
  19366. */
  19367. // This variable extends the defaultOptions for left axes.
  19368. Axis.defaultLeftAxisOptions = {
  19369. labels: {
  19370. x: -15
  19371. },
  19372. title: {
  19373. rotation: 270
  19374. }
  19375. };
  19376. // This variable extends the defaultOptions for right axes.
  19377. Axis.defaultRightAxisOptions = {
  19378. labels: {
  19379. x: 15
  19380. },
  19381. title: {
  19382. rotation: 90
  19383. }
  19384. };
  19385. // This variable extends the defaultOptions for bottom axes.
  19386. Axis.defaultBottomAxisOptions = {
  19387. labels: {
  19388. autoRotation: [-45],
  19389. x: 0
  19390. // overflow: undefined,
  19391. // staggerLines: null
  19392. },
  19393. margin: 15,
  19394. title: {
  19395. rotation: 0
  19396. }
  19397. };
  19398. // This variable extends the defaultOptions for top axes.
  19399. Axis.defaultTopAxisOptions = {
  19400. labels: {
  19401. autoRotation: [-45],
  19402. x: 0
  19403. // overflow: undefined
  19404. // staggerLines: null
  19405. },
  19406. margin: 15,
  19407. title: {
  19408. rotation: 0
  19409. }
  19410. };
  19411. // Properties to survive after destroy, needed for Axis.update (#4317,
  19412. // #5773, #5881).
  19413. Axis.keepProps = ['extKey', 'hcEvents', 'names', 'series', 'userMax', 'userMin'];
  19414. return Axis;
  19415. }());
  19416. H.Axis = Axis;
  19417. return H.Axis;
  19418. });
  19419. _registerModule(_modules, 'parts/DateTimeAxis.js', [_modules['parts/Axis.js'], _modules['parts/Utilities.js']], function (Axis, U) {
  19420. /* *
  19421. *
  19422. * (c) 2010-2020 Torstein Honsi
  19423. *
  19424. * License: www.highcharts.com/license
  19425. *
  19426. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  19427. *
  19428. * */
  19429. var addEvent = U.addEvent, getMagnitude = U.getMagnitude, normalizeTickInterval = U.normalizeTickInterval, timeUnits = U.timeUnits;
  19430. /* eslint-disable valid-jsdoc */
  19431. var DateTimeAxisAdditions = /** @class */ (function () {
  19432. /* *
  19433. *
  19434. * Constructors
  19435. *
  19436. * */
  19437. function DateTimeAxisAdditions(axis) {
  19438. this.axis = axis;
  19439. }
  19440. /* *
  19441. *
  19442. * Functions
  19443. *
  19444. * */
  19445. /**
  19446. * Get a normalized tick interval for dates. Returns a configuration object
  19447. * with unit range (interval), count and name. Used to prepare data for
  19448. * `getTimeTicks`. Previously this logic was part of getTimeTicks, but as
  19449. * `getTimeTicks` now runs of segments in stock charts, the normalizing
  19450. * logic was extracted in order to prevent it for running over again for
  19451. * each segment having the same interval. #662, #697.
  19452. * @private
  19453. */
  19454. /**
  19455. * Get a normalized tick interval for dates. Returns a configuration object
  19456. * with unit range (interval), count and name. Used to prepare data for
  19457. * `getTimeTicks`. Previously this logic was part of getTimeTicks, but as
  19458. * `getTimeTicks` now runs of segments in stock charts, the normalizing
  19459. * logic was extracted in order to prevent it for running over again for
  19460. * each segment having the same interval. #662, #697.
  19461. * @private
  19462. */
  19463. DateTimeAxisAdditions.prototype.normalizeTimeTickInterval = function (tickInterval, unitsOption) {
  19464. var units = unitsOption || [[
  19465. 'millisecond',
  19466. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  19467. ], [
  19468. 'second',
  19469. [1, 2, 5, 10, 15, 30]
  19470. ], [
  19471. 'minute',
  19472. [1, 2, 5, 10, 15, 30]
  19473. ], [
  19474. 'hour',
  19475. [1, 2, 3, 4, 6, 8, 12]
  19476. ], [
  19477. 'day',
  19478. [1, 2]
  19479. ], [
  19480. 'week',
  19481. [1, 2]
  19482. ], [
  19483. 'month',
  19484. [1, 2, 3, 4, 6]
  19485. ], [
  19486. 'year',
  19487. null
  19488. ]], unit = units[units.length - 1], // default unit is years
  19489. interval = timeUnits[unit[0]], multiples = unit[1], count, i;
  19490. // loop through the units to find the one that best fits the
  19491. // tickInterval
  19492. for (i = 0; i < units.length; i++) {
  19493. unit = units[i];
  19494. interval = timeUnits[unit[0]];
  19495. multiples = unit[1];
  19496. if (units[i + 1]) {
  19497. // lessThan is in the middle between the highest multiple and
  19498. // the next unit.
  19499. var lessThan = (interval *
  19500. multiples[multiples.length - 1] +
  19501. timeUnits[units[i + 1][0]]) / 2;
  19502. // break and keep the current unit
  19503. if (tickInterval <= lessThan) {
  19504. break;
  19505. }
  19506. }
  19507. }
  19508. // prevent 2.5 years intervals, though 25, 250 etc. are allowed
  19509. if (interval === timeUnits.year && tickInterval < 5 * interval) {
  19510. multiples = [1, 2, 5];
  19511. }
  19512. // get the count
  19513. count = normalizeTickInterval(tickInterval / interval, multiples, unit[0] === 'year' ? // #1913, #2360
  19514. Math.max(getMagnitude(tickInterval / interval), 1) :
  19515. 1);
  19516. return {
  19517. unitRange: interval,
  19518. count: count,
  19519. unitName: unit[0]
  19520. };
  19521. };
  19522. return DateTimeAxisAdditions;
  19523. }());
  19524. /**
  19525. * Date and time support for axes.
  19526. *
  19527. * @private
  19528. * @class
  19529. */
  19530. var DateTimeAxis = /** @class */ (function () {
  19531. function DateTimeAxis() {
  19532. }
  19533. /* *
  19534. *
  19535. * Static Functions
  19536. *
  19537. * */
  19538. /**
  19539. * Extends axis class with date and time support.
  19540. * @private
  19541. */
  19542. DateTimeAxis.compose = function (AxisClass) {
  19543. AxisClass.keepProps.push('dateTime');
  19544. var axisProto = AxisClass.prototype;
  19545. /**
  19546. * Set the tick positions to a time unit that makes sense, for example
  19547. * on the first of each month or on every Monday. Return an array with
  19548. * the time positions. Used in datetime axes as well as for grouping
  19549. * data on a datetime axis.
  19550. *
  19551. * @private
  19552. * @function Highcharts.Axis#getTimeTicks
  19553. *
  19554. * @param {Highcharts.TimeNormalizeObject} normalizedInterval
  19555. * The interval in axis values (ms) and thecount.
  19556. *
  19557. * @param {number} min
  19558. * The minimum in axis values.
  19559. *
  19560. * @param {number} max
  19561. * The maximum in axis values.
  19562. *
  19563. * @param {number} startOfWeek
  19564. *
  19565. * @return {Highcharts.AxisTickPositionsArray}
  19566. */
  19567. axisProto.getTimeTicks = function () {
  19568. return this.chart.time.getTimeTicks.apply(this.chart.time, arguments);
  19569. };
  19570. /* eslint-disable no-invalid-this */
  19571. addEvent(AxisClass, 'init', function (e) {
  19572. var axis = this;
  19573. var options = e.userOptions;
  19574. if (options.type !== 'datetime') {
  19575. axis.dateTime = void 0;
  19576. return;
  19577. }
  19578. if (!axis.dateTime) {
  19579. axis.dateTime = new DateTimeAxisAdditions(axis);
  19580. }
  19581. });
  19582. /* eslint-enable no-invalid-this */
  19583. };
  19584. /* *
  19585. *
  19586. * Static Properties
  19587. *
  19588. * */
  19589. DateTimeAxis.AdditionsClass = DateTimeAxisAdditions;
  19590. return DateTimeAxis;
  19591. }());
  19592. DateTimeAxis.compose(Axis);
  19593. return DateTimeAxis;
  19594. });
  19595. _registerModule(_modules, 'parts/LogarithmicAxis.js', [_modules['parts/Axis.js'], _modules['parts/Utilities.js']], function (Axis, U) {
  19596. /* *
  19597. *
  19598. * (c) 2010-2020 Torstein Honsi
  19599. *
  19600. * License: www.highcharts.com/license
  19601. *
  19602. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  19603. *
  19604. * */
  19605. var addEvent = U.addEvent, getMagnitude = U.getMagnitude, normalizeTickInterval = U.normalizeTickInterval, pick = U.pick;
  19606. /* eslint-disable valid-jsdoc */
  19607. /**
  19608. * Provides logarithmic support for axes.
  19609. *
  19610. * @private
  19611. * @class
  19612. */
  19613. var LogarithmicAxisAdditions = /** @class */ (function () {
  19614. /* *
  19615. *
  19616. * Constructors
  19617. *
  19618. * */
  19619. function LogarithmicAxisAdditions(axis) {
  19620. this.axis = axis;
  19621. }
  19622. /* *
  19623. *
  19624. * Functions
  19625. *
  19626. * */
  19627. /**
  19628. * Set the tick positions of a logarithmic axis.
  19629. */
  19630. LogarithmicAxisAdditions.prototype.getLogTickPositions = function (interval, min, max, minor) {
  19631. var log = this;
  19632. var axis = log.axis;
  19633. var axisLength = axis.len;
  19634. var options = axis.options;
  19635. // Since we use this method for both major and minor ticks,
  19636. // use a local variable and return the result
  19637. var positions = [];
  19638. // Reset
  19639. if (!minor) {
  19640. log.minorAutoInterval = void 0;
  19641. }
  19642. // First case: All ticks fall on whole logarithms: 1, 10, 100 etc.
  19643. if (interval >= 0.5) {
  19644. interval = Math.round(interval);
  19645. positions = axis.getLinearTickPositions(interval, min, max);
  19646. // Second case: We need intermediary ticks. For example
  19647. // 1, 2, 4, 6, 8, 10, 20, 40 etc.
  19648. }
  19649. else if (interval >= 0.08) {
  19650. var roundedMin = Math.floor(min), intermediate, i, j, len, pos, lastPos, break2;
  19651. if (interval > 0.3) {
  19652. intermediate = [1, 2, 4];
  19653. // 0.2 equals five minor ticks per 1, 10, 100 etc
  19654. }
  19655. else if (interval > 0.15) {
  19656. intermediate = [1, 2, 4, 6, 8];
  19657. }
  19658. else { // 0.1 equals ten minor ticks per 1, 10, 100 etc
  19659. intermediate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  19660. }
  19661. for (i = roundedMin; i < max + 1 && !break2; i++) {
  19662. len = intermediate.length;
  19663. for (j = 0; j < len && !break2; j++) {
  19664. pos = log.log2lin(log.lin2log(i) * intermediate[j]);
  19665. // #1670, lastPos is #3113
  19666. if (pos > min &&
  19667. (!minor || lastPos <= max) &&
  19668. typeof lastPos !== 'undefined') {
  19669. positions.push(lastPos);
  19670. }
  19671. if (lastPos > max) {
  19672. break2 = true;
  19673. }
  19674. lastPos = pos;
  19675. }
  19676. }
  19677. // Third case: We are so deep in between whole logarithmic values that
  19678. // we might as well handle the tick positions like a linear axis. For
  19679. // example 1.01, 1.02, 1.03, 1.04.
  19680. }
  19681. else {
  19682. var realMin = log.lin2log(min), realMax = log.lin2log(max), tickIntervalOption = minor ?
  19683. axis.getMinorTickInterval() :
  19684. options.tickInterval, filteredTickIntervalOption = tickIntervalOption === 'auto' ?
  19685. null :
  19686. tickIntervalOption, tickPixelIntervalOption = options.tickPixelInterval / (minor ? 5 : 1), totalPixelLength = minor ?
  19687. axisLength / axis.tickPositions.length :
  19688. axisLength;
  19689. interval = pick(filteredTickIntervalOption, log.minorAutoInterval, (realMax - realMin) *
  19690. tickPixelIntervalOption / (totalPixelLength || 1));
  19691. interval = normalizeTickInterval(interval, void 0, getMagnitude(interval));
  19692. positions = axis.getLinearTickPositions(interval, realMin, realMax).map(log.log2lin);
  19693. if (!minor) {
  19694. log.minorAutoInterval = interval / 5;
  19695. }
  19696. }
  19697. // Set the axis-level tickInterval variable
  19698. if (!minor) {
  19699. axis.tickInterval = interval;
  19700. }
  19701. return positions;
  19702. };
  19703. LogarithmicAxisAdditions.prototype.lin2log = function (num) {
  19704. return Math.pow(10, num);
  19705. };
  19706. LogarithmicAxisAdditions.prototype.log2lin = function (num) {
  19707. return Math.log(num) / Math.LN10;
  19708. };
  19709. return LogarithmicAxisAdditions;
  19710. }());
  19711. var LogarithmicAxis = /** @class */ (function () {
  19712. function LogarithmicAxis() {
  19713. }
  19714. /**
  19715. * Provides logarithmic support for axes.
  19716. *
  19717. * @private
  19718. */
  19719. LogarithmicAxis.compose = function (AxisClass) {
  19720. AxisClass.keepProps.push('logarithmic');
  19721. // HC <= 8 backwards compatibility, allow wrapping
  19722. // Axis.prototype.lin2log and log2lin
  19723. // @todo Remove this in next major
  19724. var axisProto = AxisClass.prototype;
  19725. var logAxisProto = LogarithmicAxisAdditions.prototype;
  19726. axisProto.log2lin = logAxisProto.log2lin;
  19727. axisProto.lin2log = logAxisProto.lin2log;
  19728. /* eslint-disable no-invalid-this */
  19729. addEvent(AxisClass, 'init', function (e) {
  19730. var axis = this;
  19731. var options = e.userOptions;
  19732. var logarithmic = axis.logarithmic;
  19733. if (options.type !== 'logarithmic') {
  19734. axis.logarithmic = void 0;
  19735. }
  19736. else {
  19737. if (!logarithmic) {
  19738. logarithmic = axis.logarithmic = new LogarithmicAxisAdditions(axis);
  19739. }
  19740. // HC <= 8 backwards compatibility, allow wrapping
  19741. // Axis.prototype.lin2log and log2lin
  19742. // @todo Remove this in next major
  19743. if (axis.log2lin !== logarithmic.log2lin) {
  19744. logarithmic.log2lin = axis.log2lin.bind(axis);
  19745. }
  19746. if (axis.lin2log !== logarithmic.lin2log) {
  19747. logarithmic.lin2log = axis.lin2log.bind(axis);
  19748. }
  19749. }
  19750. });
  19751. addEvent(AxisClass, 'afterInit', function () {
  19752. var axis = this;
  19753. var log = axis.logarithmic;
  19754. // extend logarithmic axis
  19755. if (log) {
  19756. axis.lin2val = function (num) {
  19757. return log.lin2log(num);
  19758. };
  19759. axis.val2lin = function (num) {
  19760. return log.log2lin(num);
  19761. };
  19762. }
  19763. });
  19764. };
  19765. return LogarithmicAxis;
  19766. }());
  19767. LogarithmicAxis.compose(Axis); // @todo move to factory functions
  19768. return LogarithmicAxis;
  19769. });
  19770. _registerModule(_modules, 'parts/PlotLineOrBand.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Axis, H, U) {
  19771. /* *
  19772. *
  19773. * (c) 2010-2020 Torstein Honsi
  19774. *
  19775. * License: www.highcharts.com/license
  19776. *
  19777. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  19778. *
  19779. * */
  19780. /**
  19781. * Options for plot bands on axes.
  19782. *
  19783. * @typedef {Highcharts.XAxisPlotBandsOptions|Highcharts.YAxisPlotBandsOptions|Highcharts.ZAxisPlotBandsOptions} Highcharts.AxisPlotBandsOptions
  19784. */
  19785. /**
  19786. * Options for plot band labels on axes.
  19787. *
  19788. * @typedef {Highcharts.XAxisPlotBandsLabelOptions|Highcharts.YAxisPlotBandsLabelOptions|Highcharts.ZAxisPlotBandsLabelOptions} Highcharts.AxisPlotBandsLabelOptions
  19789. */
  19790. /**
  19791. * Options for plot lines on axes.
  19792. *
  19793. * @typedef {Highcharts.XAxisPlotLinesOptions|Highcharts.YAxisPlotLinesOptions|Highcharts.ZAxisPlotLinesOptions} Highcharts.AxisPlotLinesOptions
  19794. */
  19795. /**
  19796. * Options for plot line labels on axes.
  19797. *
  19798. * @typedef {Highcharts.XAxisPlotLinesLabelOptions|Highcharts.YAxisPlotLinesLabelOptions|Highcharts.ZAxisPlotLinesLabelOptions} Highcharts.AxisPlotLinesLabelOptions
  19799. */
  19800. var arrayMax = U.arrayMax, arrayMin = U.arrayMin, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, fireEvent = U.fireEvent, merge = U.merge, objectEach = U.objectEach, pick = U.pick;
  19801. /* eslint-disable no-invalid-this, valid-jsdoc */
  19802. /**
  19803. * The object wrapper for plot lines and plot bands
  19804. *
  19805. * @class
  19806. * @name Highcharts.PlotLineOrBand
  19807. *
  19808. * @param {Highcharts.Axis} axis
  19809. *
  19810. * @param {Highcharts.AxisPlotLinesOptions|Highcharts.AxisPlotBandsOptions} [options]
  19811. */
  19812. var PlotLineOrBand = /** @class */ (function () {
  19813. function PlotLineOrBand(axis, options) {
  19814. this.axis = axis;
  19815. if (options) {
  19816. this.options = options;
  19817. this.id = options.id;
  19818. }
  19819. }
  19820. /**
  19821. * Render the plot line or plot band. If it is already existing,
  19822. * move it.
  19823. *
  19824. * @private
  19825. * @function Highcharts.PlotLineOrBand#render
  19826. * @return {Highcharts.PlotLineOrBand|undefined}
  19827. */
  19828. PlotLineOrBand.prototype.render = function () {
  19829. H.fireEvent(this, 'render');
  19830. var plotLine = this, axis = plotLine.axis, horiz = axis.horiz, log = axis.logarithmic, options = plotLine.options, optionsLabel = options.label, label = plotLine.label, to = options.to, from = options.from, value = options.value, isBand = defined(from) && defined(to), isLine = defined(value), svgElem = plotLine.svgElem, isNew = !svgElem, path = [], color = options.color, zIndex = pick(options.zIndex, 0), events = options.events, attribs = {
  19831. 'class': 'highcharts-plot-' + (isBand ? 'band ' : 'line ') +
  19832. (options.className || '')
  19833. }, groupAttribs = {}, renderer = axis.chart.renderer, groupName = isBand ? 'bands' : 'lines', group;
  19834. // logarithmic conversion
  19835. if (log) {
  19836. from = log.log2lin(from);
  19837. to = log.log2lin(to);
  19838. value = log.log2lin(value);
  19839. }
  19840. // Set the presentational attributes
  19841. if (!axis.chart.styledMode) {
  19842. if (isLine) {
  19843. attribs.stroke = color || '#999999';
  19844. attribs['stroke-width'] = pick(options.width, 1);
  19845. if (options.dashStyle) {
  19846. attribs.dashstyle =
  19847. options.dashStyle;
  19848. }
  19849. }
  19850. else if (isBand) { // plot band
  19851. attribs.fill = color || '#e6ebf5';
  19852. if (options.borderWidth) {
  19853. attribs.stroke = options.borderColor;
  19854. attribs['stroke-width'] = options.borderWidth;
  19855. }
  19856. }
  19857. }
  19858. // Grouping and zIndex
  19859. groupAttribs.zIndex = zIndex;
  19860. groupName += '-' + zIndex;
  19861. group = axis.plotLinesAndBandsGroups[groupName];
  19862. if (!group) {
  19863. axis.plotLinesAndBandsGroups[groupName] = group =
  19864. renderer.g('plot-' + groupName)
  19865. .attr(groupAttribs).add();
  19866. }
  19867. // Create the path
  19868. if (isNew) {
  19869. /**
  19870. * SVG element of the plot line or band.
  19871. *
  19872. * @name Highcharts.PlotLineOrBand#svgElement
  19873. * @type {Highcharts.SVGElement}
  19874. */
  19875. plotLine.svgElem = svgElem = renderer
  19876. .path()
  19877. .attr(attribs)
  19878. .add(group);
  19879. }
  19880. // Set the path or return
  19881. if (isLine) {
  19882. path = axis.getPlotLinePath({
  19883. value: value,
  19884. lineWidth: svgElem.strokeWidth(),
  19885. acrossPanes: options.acrossPanes
  19886. });
  19887. }
  19888. else if (isBand) { // plot band
  19889. path = axis.getPlotBandPath(from, to, options);
  19890. }
  19891. else {
  19892. return;
  19893. }
  19894. // common for lines and bands
  19895. // Add events only if they were not added before.
  19896. if (!plotLine.eventsAdded && events) {
  19897. objectEach(events, function (event, eventType) {
  19898. svgElem.on(eventType, function (e) {
  19899. events[eventType].apply(plotLine, [e]);
  19900. });
  19901. });
  19902. plotLine.eventsAdded = true;
  19903. }
  19904. if ((isNew || !svgElem.d) && path && path.length) {
  19905. svgElem.attr({ d: path });
  19906. }
  19907. else if (svgElem) {
  19908. if (path) {
  19909. svgElem.show(true);
  19910. svgElem.animate({ d: path });
  19911. }
  19912. else if (svgElem.d) {
  19913. svgElem.hide();
  19914. if (label) {
  19915. plotLine.label = label = label.destroy();
  19916. }
  19917. }
  19918. }
  19919. // the plot band/line label
  19920. if (optionsLabel &&
  19921. (defined(optionsLabel.text) || defined(optionsLabel.formatter)) &&
  19922. path &&
  19923. path.length &&
  19924. axis.width > 0 &&
  19925. axis.height > 0 &&
  19926. !path.isFlat) {
  19927. // apply defaults
  19928. optionsLabel = merge({
  19929. align: horiz && isBand && 'center',
  19930. x: horiz ? !isBand && 4 : 10,
  19931. verticalAlign: !horiz && isBand && 'middle',
  19932. y: horiz ? isBand ? 16 : 10 : isBand ? 6 : -4,
  19933. rotation: horiz && !isBand && 90
  19934. }, optionsLabel);
  19935. this.renderLabel(optionsLabel, path, isBand, zIndex);
  19936. }
  19937. else if (label) { // move out of sight
  19938. label.hide();
  19939. }
  19940. // chainable
  19941. return plotLine;
  19942. };
  19943. /**
  19944. * Render and align label for plot line or band.
  19945. *
  19946. * @private
  19947. * @function Highcharts.PlotLineOrBand#renderLabel
  19948. * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
  19949. * @param {Highcharts.SVGPathArray} path
  19950. * @param {boolean} [isBand]
  19951. * @param {number} [zIndex]
  19952. * @return {void}
  19953. */
  19954. PlotLineOrBand.prototype.renderLabel = function (optionsLabel, path, isBand, zIndex) {
  19955. var plotLine = this, label = plotLine.label, renderer = plotLine.axis.chart.renderer, attribs, xBounds, yBounds, x, y, labelText;
  19956. // add the SVG element
  19957. if (!label) {
  19958. attribs = {
  19959. align: optionsLabel.textAlign || optionsLabel.align,
  19960. rotation: optionsLabel.rotation,
  19961. 'class': 'highcharts-plot-' + (isBand ? 'band' : 'line') +
  19962. '-label ' + (optionsLabel.className || '')
  19963. };
  19964. attribs.zIndex = zIndex;
  19965. labelText = this.getLabelText(optionsLabel);
  19966. /**
  19967. * SVG element of the label.
  19968. *
  19969. * @name Highcharts.PlotLineOrBand#label
  19970. * @type {Highcharts.SVGElement}
  19971. */
  19972. plotLine.label = label = renderer
  19973. .text(labelText, 0, 0, optionsLabel.useHTML)
  19974. .attr(attribs)
  19975. .add();
  19976. if (!this.axis.chart.styledMode) {
  19977. label.css(optionsLabel.style);
  19978. }
  19979. }
  19980. // get the bounding box and align the label
  19981. // #3000 changed to better handle choice between plotband or plotline
  19982. xBounds = path.xBounds ||
  19983. [path[0][1], path[1][1], (isBand ? path[2][1] : path[0][1])];
  19984. yBounds = path.yBounds ||
  19985. [path[0][2], path[1][2], (isBand ? path[2][2] : path[0][2])];
  19986. x = arrayMin(xBounds);
  19987. y = arrayMin(yBounds);
  19988. label.align(optionsLabel, false, {
  19989. x: x,
  19990. y: y,
  19991. width: arrayMax(xBounds) - x,
  19992. height: arrayMax(yBounds) - y
  19993. });
  19994. label.show(true);
  19995. };
  19996. /**
  19997. * Get label's text content.
  19998. *
  19999. * @private
  20000. * @function Highcharts.PlotLineOrBand#getLabelText
  20001. * @param {Highcharts.AxisPlotLinesLabelOptions|Highcharts.AxisPlotBandsLabelOptions} optionsLabel
  20002. * @return {string}
  20003. */
  20004. PlotLineOrBand.prototype.getLabelText = function (optionsLabel) {
  20005. return defined(optionsLabel.formatter) ?
  20006. optionsLabel.formatter
  20007. .call(this) :
  20008. optionsLabel.text;
  20009. };
  20010. /**
  20011. * Remove the plot line or band.
  20012. *
  20013. * @function Highcharts.PlotLineOrBand#destroy
  20014. * @return {void}
  20015. */
  20016. PlotLineOrBand.prototype.destroy = function () {
  20017. // remove it from the lookup
  20018. erase(this.axis.plotLinesAndBands, this);
  20019. delete this.axis;
  20020. destroyObjectProperties(this);
  20021. };
  20022. return PlotLineOrBand;
  20023. }());
  20024. /* eslint-enable no-invalid-this, valid-jsdoc */
  20025. // Object with members for extending the Axis prototype
  20026. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  20027. /**
  20028. * An array of colored bands stretching across the plot area marking an
  20029. * interval on the axis.
  20030. *
  20031. * In styled mode, the plot bands are styled by the `.highcharts-plot-band`
  20032. * class in addition to the `className` option.
  20033. *
  20034. * @productdesc {highcharts}
  20035. * In a gauge, a plot band on the Y axis (value axis) will stretch along the
  20036. * perimeter of the gauge.
  20037. *
  20038. * @type {Array<*>}
  20039. * @product highcharts highstock gantt
  20040. * @apioption xAxis.plotBands
  20041. */
  20042. /**
  20043. * Flag to decide if plotBand should be rendered across all panes.
  20044. *
  20045. * @since 7.1.2
  20046. * @product highstock
  20047. * @type {boolean}
  20048. * @default true
  20049. * @apioption xAxis.plotBands.acrossPanes
  20050. */
  20051. /**
  20052. * Border color for the plot band. Also requires `borderWidth` to be set.
  20053. *
  20054. * @type {Highcharts.ColorString}
  20055. * @apioption xAxis.plotBands.borderColor
  20056. */
  20057. /**
  20058. * Border width for the plot band. Also requires `borderColor` to be set.
  20059. *
  20060. * @type {number}
  20061. * @default 0
  20062. * @apioption xAxis.plotBands.borderWidth
  20063. */
  20064. /**
  20065. * A custom class name, in addition to the default `highcharts-plot-band`,
  20066. * to apply to each individual band.
  20067. *
  20068. * @type {string}
  20069. * @since 5.0.0
  20070. * @apioption xAxis.plotBands.className
  20071. */
  20072. /**
  20073. * The color of the plot band.
  20074. *
  20075. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  20076. * Color band
  20077. * @sample {highstock} stock/xaxis/plotbands/
  20078. * Plot band on Y axis
  20079. *
  20080. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  20081. * @default #e6ebf5
  20082. * @apioption xAxis.plotBands.color
  20083. */
  20084. /**
  20085. * An object defining mouse events for the plot band. Supported properties
  20086. * are `click`, `mouseover`, `mouseout`, `mousemove`.
  20087. *
  20088. * @sample {highcharts} highcharts/xaxis/plotbands-events/
  20089. * Mouse events demonstrated
  20090. *
  20091. * @since 1.2
  20092. * @apioption xAxis.plotBands.events
  20093. */
  20094. /**
  20095. * Click event on a plot band.
  20096. *
  20097. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20098. * @apioption xAxis.plotBands.events.click
  20099. */
  20100. /**
  20101. * Mouse move event on a plot band.
  20102. *
  20103. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20104. * @apioption xAxis.plotBands.events.mousemove
  20105. */
  20106. /**
  20107. * Mouse out event on the corner of a plot band.
  20108. *
  20109. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20110. * @apioption xAxis.plotBands.events.mouseout
  20111. */
  20112. /**
  20113. * Mouse over event on a plot band.
  20114. *
  20115. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20116. * @apioption xAxis.plotBands.events.mouseover
  20117. */
  20118. /**
  20119. * The start position of the plot band in axis units.
  20120. *
  20121. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  20122. * Datetime axis
  20123. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  20124. * Categorized axis
  20125. * @sample {highstock} stock/xaxis/plotbands/
  20126. * Plot band on Y axis
  20127. *
  20128. * @type {number}
  20129. * @apioption xAxis.plotBands.from
  20130. */
  20131. /**
  20132. * An id used for identifying the plot band in Axis.removePlotBand.
  20133. *
  20134. * @sample {highcharts} highcharts/xaxis/plotbands-id/
  20135. * Remove plot band by id
  20136. * @sample {highstock} highcharts/xaxis/plotbands-id/
  20137. * Remove plot band by id
  20138. *
  20139. * @type {string}
  20140. * @apioption xAxis.plotBands.id
  20141. */
  20142. /**
  20143. * The end position of the plot band in axis units.
  20144. *
  20145. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  20146. * Datetime axis
  20147. * @sample {highcharts} highcharts/xaxis/plotbands-from/
  20148. * Categorized axis
  20149. * @sample {highstock} stock/xaxis/plotbands/
  20150. * Plot band on Y axis
  20151. *
  20152. * @type {number}
  20153. * @apioption xAxis.plotBands.to
  20154. */
  20155. /**
  20156. * The z index of the plot band within the chart, relative to other
  20157. * elements. Using the same z index as another element may give
  20158. * unpredictable results, as the last rendered element will be on top.
  20159. * Values from 0 to 20 make sense.
  20160. *
  20161. * @sample {highcharts} highcharts/xaxis/plotbands-color/
  20162. * Behind plot lines by default
  20163. * @sample {highcharts} highcharts/xaxis/plotbands-zindex/
  20164. * Above plot lines
  20165. * @sample {highcharts} highcharts/xaxis/plotbands-zindex-above-series/
  20166. * Above plot lines and series
  20167. *
  20168. * @type {number}
  20169. * @since 1.2
  20170. * @apioption xAxis.plotBands.zIndex
  20171. */
  20172. /**
  20173. * Text labels for the plot bands
  20174. *
  20175. * @product highcharts highstock gantt
  20176. * @apioption xAxis.plotBands.label
  20177. */
  20178. /**
  20179. * Horizontal alignment of the label. Can be one of "left", "center" or
  20180. * "right".
  20181. *
  20182. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  20183. * Aligned to the right
  20184. * @sample {highstock} stock/xaxis/plotbands-label/
  20185. * Plot band with labels
  20186. *
  20187. * @type {Highcharts.AlignValue}
  20188. * @default center
  20189. * @since 2.1
  20190. * @apioption xAxis.plotBands.label.align
  20191. */
  20192. /**
  20193. * Rotation of the text label in degrees .
  20194. *
  20195. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  20196. * Vertical text
  20197. *
  20198. * @type {number}
  20199. * @default 0
  20200. * @since 2.1
  20201. * @apioption xAxis.plotBands.label.rotation
  20202. */
  20203. /**
  20204. * CSS styles for the text label.
  20205. *
  20206. * In styled mode, the labels are styled by the
  20207. * `.highcharts-plot-band-label` class.
  20208. *
  20209. * @sample {highcharts} highcharts/xaxis/plotbands-label-style/
  20210. * Blue and bold label
  20211. *
  20212. * @type {Highcharts.CSSObject}
  20213. * @since 2.1
  20214. * @apioption xAxis.plotBands.label.style
  20215. */
  20216. /**
  20217. * The string text itself. A subset of HTML is supported.
  20218. *
  20219. * @type {string}
  20220. * @since 2.1
  20221. * @apioption xAxis.plotBands.label.text
  20222. */
  20223. /**
  20224. * The text alignment for the label. While `align` determines where the
  20225. * texts anchor point is placed within the plot band, `textAlign` determines
  20226. * how the text is aligned against its anchor point. Possible values are
  20227. * "left", "center" and "right". Defaults to the same as the `align` option.
  20228. *
  20229. * @sample {highcharts} highcharts/xaxis/plotbands-label-rotation/
  20230. * Vertical text in center position but text-aligned left
  20231. *
  20232. * @type {Highcharts.AlignValue}
  20233. * @since 2.1
  20234. * @apioption xAxis.plotBands.label.textAlign
  20235. */
  20236. /**
  20237. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  20238. * to render the labels.
  20239. *
  20240. * @type {boolean}
  20241. * @default false
  20242. * @since 3.0.3
  20243. * @apioption xAxis.plotBands.label.useHTML
  20244. */
  20245. /**
  20246. * Vertical alignment of the label relative to the plot band. Can be one of
  20247. * "top", "middle" or "bottom".
  20248. *
  20249. * @sample {highcharts} highcharts/xaxis/plotbands-label-verticalalign/
  20250. * Vertically centered label
  20251. * @sample {highstock} stock/xaxis/plotbands-label/
  20252. * Plot band with labels
  20253. *
  20254. * @type {Highcharts.VerticalAlignValue}
  20255. * @default top
  20256. * @since 2.1
  20257. * @apioption xAxis.plotBands.label.verticalAlign
  20258. */
  20259. /**
  20260. * Horizontal position relative the alignment. Default varies by
  20261. * orientation.
  20262. *
  20263. * @sample {highcharts} highcharts/xaxis/plotbands-label-align/
  20264. * Aligned 10px from the right edge
  20265. * @sample {highstock} stock/xaxis/plotbands-label/
  20266. * Plot band with labels
  20267. *
  20268. * @type {number}
  20269. * @since 2.1
  20270. * @apioption xAxis.plotBands.label.x
  20271. */
  20272. /**
  20273. * Vertical position of the text baseline relative to the alignment. Default
  20274. * varies by orientation.
  20275. *
  20276. * @sample {highcharts} highcharts/xaxis/plotbands-label-y/
  20277. * Label on x axis
  20278. * @sample {highstock} stock/xaxis/plotbands-label/
  20279. * Plot band with labels
  20280. *
  20281. * @type {number}
  20282. * @since 2.1
  20283. * @apioption xAxis.plotBands.label.y
  20284. */
  20285. /**
  20286. * An array of lines stretching across the plot area, marking a specific
  20287. * value on one of the axes.
  20288. *
  20289. * In styled mode, the plot lines are styled by the
  20290. * `.highcharts-plot-line` class in addition to the `className` option.
  20291. *
  20292. * @type {Array<*>}
  20293. * @product highcharts highstock gantt
  20294. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  20295. * Basic plot line
  20296. * @sample {highcharts} highcharts/series-solidgauge/labels-auto-aligned/
  20297. * Solid gauge plot line
  20298. * @apioption xAxis.plotLines
  20299. */
  20300. /**
  20301. * Flag to decide if plotLine should be rendered across all panes.
  20302. *
  20303. * @sample {highstock} stock/xaxis/plotlines-acrosspanes/
  20304. * Plot lines on different panes
  20305. *
  20306. * @since 7.1.2
  20307. * @product highstock
  20308. * @type {boolean}
  20309. * @default true
  20310. * @apioption xAxis.plotLines.acrossPanes
  20311. */
  20312. /**
  20313. * A custom class name, in addition to the default `highcharts-plot-line`,
  20314. * to apply to each individual line.
  20315. *
  20316. * @type {string}
  20317. * @since 5.0.0
  20318. * @apioption xAxis.plotLines.className
  20319. */
  20320. /**
  20321. * The color of the line.
  20322. *
  20323. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  20324. * A red line from X axis
  20325. * @sample {highstock} stock/xaxis/plotlines/
  20326. * Plot line on Y axis
  20327. *
  20328. * @type {Highcharts.ColorString}
  20329. * @default #999999
  20330. * @apioption xAxis.plotLines.color
  20331. */
  20332. /**
  20333. * The dashing or dot style for the plot line. For possible values see
  20334. * [this overview](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
  20335. *
  20336. * @sample {highcharts} highcharts/xaxis/plotlines-dashstyle/
  20337. * Dash and dot pattern
  20338. * @sample {highstock} stock/xaxis/plotlines/
  20339. * Plot line on Y axis
  20340. *
  20341. * @type {Highcharts.DashStyleValue}
  20342. * @default Solid
  20343. * @since 1.2
  20344. * @apioption xAxis.plotLines.dashStyle
  20345. */
  20346. /**
  20347. * An object defining mouse events for the plot line. Supported
  20348. * properties are `click`, `mouseover`, `mouseout`, `mousemove`.
  20349. *
  20350. * @sample {highcharts} highcharts/xaxis/plotlines-events/
  20351. * Mouse events demonstrated
  20352. *
  20353. * @since 1.2
  20354. * @apioption xAxis.plotLines.events
  20355. */
  20356. /**
  20357. * Click event on a plot band.
  20358. *
  20359. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20360. * @apioption xAxis.plotLines.events.click
  20361. */
  20362. /**
  20363. * Mouse move event on a plot band.
  20364. *
  20365. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20366. * @apioption xAxis.plotLines.events.mousemove
  20367. */
  20368. /**
  20369. * Mouse out event on the corner of a plot band.
  20370. *
  20371. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20372. * @apioption xAxis.plotLines.events.mouseout
  20373. */
  20374. /**
  20375. * Mouse over event on a plot band.
  20376. *
  20377. * @type {Highcharts.EventCallbackFunction<Highcharts.PlotLineOrBand>}
  20378. * @apioption xAxis.plotLines.events.mouseover
  20379. */
  20380. /**
  20381. * An id used for identifying the plot line in Axis.removePlotLine.
  20382. *
  20383. * @sample {highcharts} highcharts/xaxis/plotlines-id/
  20384. * Remove plot line by id
  20385. *
  20386. * @type {string}
  20387. * @apioption xAxis.plotLines.id
  20388. */
  20389. /**
  20390. * The position of the line in axis units.
  20391. *
  20392. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  20393. * Between two categories on X axis
  20394. * @sample {highstock} stock/xaxis/plotlines/
  20395. * Plot line on Y axis
  20396. *
  20397. * @type {number}
  20398. * @apioption xAxis.plotLines.value
  20399. */
  20400. /**
  20401. * The width or thickness of the plot line.
  20402. *
  20403. * @sample {highcharts} highcharts/xaxis/plotlines-color/
  20404. * 2px wide line from X axis
  20405. * @sample {highstock} stock/xaxis/plotlines/
  20406. * Plot line on Y axis
  20407. *
  20408. * @type {number}
  20409. * @default 2
  20410. * @apioption xAxis.plotLines.width
  20411. */
  20412. /**
  20413. * The z index of the plot line within the chart.
  20414. *
  20415. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-behind/
  20416. * Behind plot lines by default
  20417. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above/
  20418. * Above plot lines
  20419. * @sample {highcharts} highcharts/xaxis/plotlines-zindex-above-all/
  20420. * Above plot lines and series
  20421. *
  20422. * @type {number}
  20423. * @since 1.2
  20424. * @apioption xAxis.plotLines.zIndex
  20425. */
  20426. /**
  20427. * Text labels for the plot bands
  20428. *
  20429. * @apioption xAxis.plotLines.label
  20430. */
  20431. /**
  20432. * Horizontal alignment of the label. Can be one of "left", "center" or
  20433. * "right".
  20434. *
  20435. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  20436. * Aligned to the right
  20437. * @sample {highstock} stock/xaxis/plotlines/
  20438. * Plot line on Y axis
  20439. *
  20440. * @type {Highcharts.AlignValue}
  20441. * @default left
  20442. * @since 2.1
  20443. * @apioption xAxis.plotLines.label.align
  20444. */
  20445. /**
  20446. * Callback JavaScript function to format the label. Useful properties like
  20447. * the value of plot line or the range of plot band (`from` & `to`
  20448. * properties) can be found in `this.options` object.
  20449. *
  20450. * @sample {highcharts} highcharts/xaxis/plotlines-plotbands-label-formatter
  20451. * Label formatters for plot line and plot band.
  20452. * @type {Highcharts.FormatterCallbackFunction<Highcharts.PlotLineOrBand>}
  20453. * @apioption xAxis.plotLines.label.formatter
  20454. */
  20455. /**
  20456. * Rotation of the text label in degrees. Defaults to 0 for horizontal plot
  20457. * lines and 90 for vertical lines.
  20458. *
  20459. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  20460. * Slanted text
  20461. *
  20462. * @type {number}
  20463. * @since 2.1
  20464. * @apioption xAxis.plotLines.label.rotation
  20465. */
  20466. /**
  20467. * CSS styles for the text label.
  20468. *
  20469. * In styled mode, the labels are styled by the
  20470. * `.highcharts-plot-line-label` class.
  20471. *
  20472. * @sample {highcharts} highcharts/xaxis/plotlines-label-style/
  20473. * Blue and bold label
  20474. *
  20475. * @type {Highcharts.CSSObject}
  20476. * @since 2.1
  20477. * @apioption xAxis.plotLines.label.style
  20478. */
  20479. /**
  20480. * The text itself. A subset of HTML is supported.
  20481. *
  20482. * @type {string}
  20483. * @since 2.1
  20484. * @apioption xAxis.plotLines.label.text
  20485. */
  20486. /**
  20487. * The text alignment for the label. While `align` determines where the
  20488. * texts anchor point is placed within the plot band, `textAlign` determines
  20489. * how the text is aligned against its anchor point. Possible values are
  20490. * "left", "center" and "right". Defaults to the same as the `align` option.
  20491. *
  20492. * @sample {highcharts} highcharts/xaxis/plotlines-label-textalign/
  20493. * Text label in bottom position
  20494. *
  20495. * @type {Highcharts.AlignValue}
  20496. * @since 2.1
  20497. * @apioption xAxis.plotLines.label.textAlign
  20498. */
  20499. /**
  20500. * Whether to [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  20501. * to render the labels.
  20502. *
  20503. * @type {boolean}
  20504. * @default false
  20505. * @since 3.0.3
  20506. * @apioption xAxis.plotLines.label.useHTML
  20507. */
  20508. /**
  20509. * Vertical alignment of the label relative to the plot line. Can be
  20510. * one of "top", "middle" or "bottom".
  20511. *
  20512. * @sample {highcharts} highcharts/xaxis/plotlines-label-verticalalign-middle/
  20513. * Vertically centered label
  20514. *
  20515. * @type {Highcharts.VerticalAlignValue}
  20516. * @default {highcharts} top
  20517. * @default {highstock} top
  20518. * @since 2.1
  20519. * @apioption xAxis.plotLines.label.verticalAlign
  20520. */
  20521. /**
  20522. * Horizontal position relative the alignment. Default varies by
  20523. * orientation.
  20524. *
  20525. * @sample {highcharts} highcharts/xaxis/plotlines-label-align-right/
  20526. * Aligned 10px from the right edge
  20527. * @sample {highstock} stock/xaxis/plotlines/
  20528. * Plot line on Y axis
  20529. *
  20530. * @type {number}
  20531. * @since 2.1
  20532. * @apioption xAxis.plotLines.label.x
  20533. */
  20534. /**
  20535. * Vertical position of the text baseline relative to the alignment. Default
  20536. * varies by orientation.
  20537. *
  20538. * @sample {highcharts} highcharts/xaxis/plotlines-label-y/
  20539. * Label below the plot line
  20540. * @sample {highstock} stock/xaxis/plotlines/
  20541. * Plot line on Y axis
  20542. *
  20543. * @type {number}
  20544. * @since 2.1
  20545. * @apioption xAxis.plotLines.label.y
  20546. */
  20547. /**
  20548. *
  20549. * @type {Array<*>}
  20550. * @extends xAxis.plotBands
  20551. * @apioption yAxis.plotBands
  20552. */
  20553. /**
  20554. * In a gauge chart, this option determines the inner radius of the
  20555. * plot band that stretches along the perimeter. It can be given as
  20556. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  20557. * By default, the inner radius is controlled by the [thickness](
  20558. * #yAxis.plotBands.thickness) option.
  20559. *
  20560. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  20561. * Gauge plot band
  20562. *
  20563. * @type {number|string}
  20564. * @since 2.3
  20565. * @product highcharts
  20566. * @apioption yAxis.plotBands.innerRadius
  20567. */
  20568. /**
  20569. * In a gauge chart, this option determines the outer radius of the
  20570. * plot band that stretches along the perimeter. It can be given as
  20571. * a percentage string, like `"100%"`, or as a pixel number, like `100`.
  20572. *
  20573. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  20574. * Gauge plot band
  20575. *
  20576. * @type {number|string}
  20577. * @default 100%
  20578. * @since 2.3
  20579. * @product highcharts
  20580. * @apioption yAxis.plotBands.outerRadius
  20581. */
  20582. /**
  20583. * In a gauge chart, this option sets the width of the plot band
  20584. * stretching along the perimeter. It can be given as a percentage
  20585. * string, like `"10%"`, or as a pixel number, like `10`. The default
  20586. * value 10 is the same as the default [tickLength](#yAxis.tickLength),
  20587. * thus making the plot band act as a background for the tick markers.
  20588. *
  20589. * @sample {highcharts} highcharts/xaxis/plotbands-gauge
  20590. * Gauge plot band
  20591. *
  20592. * @type {number|string}
  20593. * @default 10
  20594. * @since 2.3
  20595. * @product highcharts
  20596. * @apioption yAxis.plotBands.thickness
  20597. */
  20598. /**
  20599. * @type {Array<*>}
  20600. * @extends xAxis.plotLines
  20601. * @apioption yAxis.plotLines
  20602. */
  20603. /* eslint-disable no-invalid-this, valid-jsdoc */
  20604. /**
  20605. * Internal function to create the SVG path definition for a plot band.
  20606. *
  20607. * @function Highcharts.Axis#getPlotBandPath
  20608. *
  20609. * @param {number} from
  20610. * The axis value to start from.
  20611. *
  20612. * @param {number} to
  20613. * The axis value to end on.
  20614. *
  20615. * @return {Highcharts.SVGPathArray}
  20616. * The SVG path definition in array form.
  20617. */
  20618. getPlotBandPath: function (from, to) {
  20619. var toPath = this.getPlotLinePath({
  20620. value: to,
  20621. force: true,
  20622. acrossPanes: this.options.acrossPanes
  20623. }), path = this.getPlotLinePath({
  20624. value: from,
  20625. force: true,
  20626. acrossPanes: this.options.acrossPanes
  20627. }), result = [], i,
  20628. // #4964 check if chart is inverted or plotband is on yAxis
  20629. horiz = this.horiz, plus = 1, isFlat, outside = (from < this.min && to < this.min) ||
  20630. (from > this.max && to > this.max);
  20631. if (path && toPath) {
  20632. // Flat paths don't need labels (#3836)
  20633. if (outside) {
  20634. isFlat = path.toString() === toPath.toString();
  20635. plus = 0;
  20636. }
  20637. // Go over each subpath - for panes in Highstock
  20638. for (i = 0; i < path.length; i += 2) {
  20639. var pathStart = path[i], pathEnd = path[i + 1], toPathStart = toPath[i], toPathEnd = toPath[i + 1];
  20640. // Type checking all affected path segments. Consider something
  20641. // smarter.
  20642. if ((pathStart[0] === 'M' || pathStart[0] === 'L') &&
  20643. (pathEnd[0] === 'M' || pathEnd[0] === 'L') &&
  20644. (toPathStart[0] === 'M' || toPathStart[0] === 'L') &&
  20645. (toPathEnd[0] === 'M' || toPathEnd[0] === 'L')) {
  20646. // Add 1 pixel when coordinates are the same
  20647. if (horiz && toPathStart[1] === pathStart[1]) {
  20648. toPathStart[1] += plus;
  20649. toPathEnd[1] += plus;
  20650. }
  20651. else if (!horiz && toPathStart[2] === pathStart[2]) {
  20652. toPathStart[2] += plus;
  20653. toPathEnd[2] += plus;
  20654. }
  20655. result.push(['M', pathStart[1], pathStart[2]], ['L', pathEnd[1], pathEnd[2]], ['L', toPathEnd[1], toPathEnd[2]], ['L', toPathStart[1], toPathStart[2]], ['Z']);
  20656. }
  20657. result.isFlat = isFlat;
  20658. }
  20659. }
  20660. else { // outside the axis area
  20661. path = null;
  20662. }
  20663. return result;
  20664. },
  20665. /**
  20666. * Add a plot band after render time.
  20667. *
  20668. * @sample highcharts/members/axis-addplotband/
  20669. * Toggle the plot band from a button
  20670. *
  20671. * @function Highcharts.Axis#addPlotBand
  20672. *
  20673. * @param {Highcharts.AxisPlotBandsOptions} options
  20674. * A configuration object for the plot band, as defined in
  20675. * [xAxis.plotBands](https://api.highcharts.com/highcharts/xAxis.plotBands).
  20676. *
  20677. * @return {Highcharts.PlotLineOrBand|undefined}
  20678. * The added plot band.
  20679. */
  20680. addPlotBand: function (options) {
  20681. return this.addPlotBandOrLine(options, 'plotBands');
  20682. },
  20683. /**
  20684. * Add a plot line after render time.
  20685. *
  20686. * @sample highcharts/members/axis-addplotline/
  20687. * Toggle the plot line from a button
  20688. *
  20689. * @function Highcharts.Axis#addPlotLine
  20690. *
  20691. * @param {Highcharts.AxisPlotLinesOptions} options
  20692. * A configuration object for the plot line, as defined in
  20693. * [xAxis.plotLines](https://api.highcharts.com/highcharts/xAxis.plotLines).
  20694. *
  20695. * @return {Highcharts.PlotLineOrBand|undefined}
  20696. * The added plot line.
  20697. */
  20698. addPlotLine: function (options) {
  20699. return this.addPlotBandOrLine(options, 'plotLines');
  20700. },
  20701. /**
  20702. * Add a plot band or plot line after render time. Called from addPlotBand
  20703. * and addPlotLine internally.
  20704. *
  20705. * @private
  20706. * @function Highcharts.Axis#addPlotBandOrLine
  20707. *
  20708. * @param {Highcharts.AxisPlotBandsOptions|Highcharts.AxisPlotLinesOptions} options
  20709. * The plotBand or plotLine configuration object.
  20710. *
  20711. * @param {"plotBands"|"plotLines"} [coll]
  20712. *
  20713. * @return {Highcharts.PlotLineOrBand|undefined}
  20714. */
  20715. addPlotBandOrLine: function (options, coll) {
  20716. var obj = new PlotLineOrBand(this, options).render(), userOptions = this.userOptions;
  20717. if (obj) { // #2189
  20718. // Add it to the user options for exporting and Axis.update
  20719. if (coll) {
  20720. // Workaround Microsoft/TypeScript issue #32693
  20721. var updatedOptions = (userOptions[coll] || []);
  20722. updatedOptions.push(options);
  20723. userOptions[coll] = updatedOptions;
  20724. }
  20725. this.plotLinesAndBands.push(obj);
  20726. this._addedPlotLB = true;
  20727. }
  20728. return obj;
  20729. },
  20730. /**
  20731. * Remove a plot band or plot line from the chart by id. Called internally
  20732. * from `removePlotBand` and `removePlotLine`.
  20733. *
  20734. * @private
  20735. * @function Highcharts.Axis#removePlotBandOrLine
  20736. * @param {string} id
  20737. * @return {void}
  20738. */
  20739. removePlotBandOrLine: function (id) {
  20740. var plotLinesAndBands = this.plotLinesAndBands, options = this.options, userOptions = this.userOptions, i = plotLinesAndBands.length;
  20741. while (i--) {
  20742. if (plotLinesAndBands[i].id === id) {
  20743. plotLinesAndBands[i].destroy();
  20744. }
  20745. }
  20746. ([
  20747. options.plotLines || [],
  20748. userOptions.plotLines || [],
  20749. options.plotBands || [],
  20750. userOptions.plotBands || []
  20751. ]).forEach(function (arr) {
  20752. i = arr.length;
  20753. while (i--) {
  20754. if ((arr[i] || {}).id === id) {
  20755. erase(arr, arr[i]);
  20756. }
  20757. }
  20758. });
  20759. },
  20760. /**
  20761. * Remove a plot band by its id.
  20762. *
  20763. * @sample highcharts/members/axis-removeplotband/
  20764. * Remove plot band by id
  20765. * @sample highcharts/members/axis-addplotband/
  20766. * Toggle the plot band from a button
  20767. *
  20768. * @function Highcharts.Axis#removePlotBand
  20769. *
  20770. * @param {string} id
  20771. * The plot band's `id` as given in the original configuration
  20772. * object or in the `addPlotBand` option.
  20773. *
  20774. * @return {void}
  20775. */
  20776. removePlotBand: function (id) {
  20777. this.removePlotBandOrLine(id);
  20778. },
  20779. /**
  20780. * Remove a plot line by its id.
  20781. *
  20782. * @sample highcharts/xaxis/plotlines-id/
  20783. * Remove plot line by id
  20784. * @sample highcharts/members/axis-addplotline/
  20785. * Toggle the plot line from a button
  20786. *
  20787. * @function Highcharts.Axis#removePlotLine
  20788. *
  20789. * @param {string} id
  20790. * The plot line's `id` as given in the original configuration
  20791. * object or in the `addPlotLine` option.
  20792. */
  20793. removePlotLine: function (id) {
  20794. this.removePlotBandOrLine(id);
  20795. }
  20796. });
  20797. H.PlotLineOrBand = PlotLineOrBand;
  20798. return H.PlotLineOrBand;
  20799. });
  20800. _registerModule(_modules, 'parts/Tooltip.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  20801. /* *
  20802. *
  20803. * (c) 2010-2020 Torstein Honsi
  20804. *
  20805. * License: www.highcharts.com/license
  20806. *
  20807. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  20808. *
  20809. * */
  20810. var doc = H.doc;
  20811. var clamp = U.clamp, css = U.css, defined = U.defined, discardElement = U.discardElement, extend = U.extend, fireEvent = U.fireEvent, format = U.format, isNumber = U.isNumber, isString = U.isString, merge = U.merge, pick = U.pick, splat = U.splat, syncTimeout = U.syncTimeout, timeUnits = U.timeUnits;
  20812. /**
  20813. * Callback function to format the text of the tooltip from scratch.
  20814. *
  20815. * In case of single or shared tooltips, a string should be be returned. In case
  20816. * of splitted tooltips, it should return an array where the first item is the
  20817. * header, and subsequent items are mapped to the points. Return `false` to
  20818. * disable tooltip for a specific point on series.
  20819. *
  20820. * @callback Highcharts.TooltipFormatterCallbackFunction
  20821. *
  20822. * @param {Highcharts.TooltipFormatterContextObject} this
  20823. * Context to format
  20824. *
  20825. * @param {Highcharts.Tooltip} tooltip
  20826. * The tooltip instance
  20827. *
  20828. * @return {false|string|Array<(string|null|undefined)>|null|undefined}
  20829. * Formatted text or false
  20830. */
  20831. /**
  20832. * @interface Highcharts.TooltipFormatterContextObject
  20833. */ /**
  20834. * @name Highcharts.TooltipFormatterContextObject#color
  20835. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  20836. */ /**
  20837. * @name Highcharts.TooltipFormatterContextObject#colorIndex
  20838. * @type {number|undefined}
  20839. */ /**
  20840. * @name Highcharts.TooltipFormatterContextObject#key
  20841. * @type {number}
  20842. */ /**
  20843. * @name Highcharts.TooltipFormatterContextObject#percentage
  20844. * @type {number|undefined}
  20845. */ /**
  20846. * @name Highcharts.TooltipFormatterContextObject#point
  20847. * @type {Highcharts.Point}
  20848. */ /**
  20849. * @name Highcharts.TooltipFormatterContextObject#points
  20850. * @type {Array<Highcharts.TooltipFormatterContextObject>|undefined}
  20851. */ /**
  20852. * @name Highcharts.TooltipFormatterContextObject#series
  20853. * @type {Highcharts.Series}
  20854. */ /**
  20855. * @name Highcharts.TooltipFormatterContextObject#total
  20856. * @type {number|undefined}
  20857. */ /**
  20858. * @name Highcharts.TooltipFormatterContextObject#x
  20859. * @type {number}
  20860. */ /**
  20861. * @name Highcharts.TooltipFormatterContextObject#y
  20862. * @type {number}
  20863. */
  20864. /**
  20865. * A callback function to place the tooltip in a specific position.
  20866. *
  20867. * @callback Highcharts.TooltipPositionerCallbackFunction
  20868. *
  20869. * @param {Highcharts.Tooltip} this
  20870. * Tooltip context of the callback.
  20871. *
  20872. * @param {number} labelWidth
  20873. * Width of the tooltip.
  20874. *
  20875. * @param {number} labelHeight
  20876. * Height of the tooltip.
  20877. *
  20878. * @param {Highcharts.Point|Highcharts.TooltipPositionerPointObject} point
  20879. * Point information for positioning a tooltip.
  20880. *
  20881. * @return {Highcharts.PositionObject}
  20882. * New position for the tooltip.
  20883. */
  20884. /**
  20885. * Point information for positioning a tooltip.
  20886. *
  20887. * @interface Highcharts.TooltipPositionerPointObject
  20888. */ /**
  20889. * If `tooltip.split` option is enabled and positioner is called for each of the
  20890. * boxes separately, this property indicates the call on the xAxis header, which
  20891. * is not a point itself.
  20892. * @name Highcharts.TooltipPositionerPointObject#isHeader
  20893. * @type {boolean}
  20894. */ /**
  20895. * The reference point relative to the plot area. Add chart.plotLeft to get the
  20896. * full coordinates.
  20897. * @name Highcharts.TooltipPositionerPointObject#plotX
  20898. * @type {number}
  20899. */ /**
  20900. * The reference point relative to the plot area. Add chart.plotTop to get the
  20901. * full coordinates.
  20902. * @name Highcharts.TooltipPositionerPointObject#plotY
  20903. * @type {number}
  20904. */
  20905. /**
  20906. * @typedef {"callout"|"circle"|"square"} Highcharts.TooltipShapeValue
  20907. */
  20908. ''; // separates doclets above from variables below
  20909. /* eslint-disable no-invalid-this, valid-jsdoc */
  20910. /**
  20911. * Tooltip of a chart.
  20912. *
  20913. * @class
  20914. * @name Highcharts.Tooltip
  20915. *
  20916. * @param {Highcharts.Chart} chart
  20917. * The chart instance.
  20918. *
  20919. * @param {Highcharts.TooltipOptions} options
  20920. * Tooltip options.
  20921. */
  20922. var Tooltip = /** @class */ (function () {
  20923. /* *
  20924. *
  20925. * Constructors
  20926. *
  20927. * */
  20928. function Tooltip(chart, options) {
  20929. this.container = void 0;
  20930. this.crosshairs = [];
  20931. this.distance = 0;
  20932. this.isHidden = true;
  20933. this.isSticky = false;
  20934. this.now = {};
  20935. this.options = {};
  20936. this.outside = false;
  20937. this.chart = chart;
  20938. this.init(chart, options);
  20939. }
  20940. /* *
  20941. *
  20942. * Functions
  20943. *
  20944. * */
  20945. /**
  20946. * In styled mode, apply the default filter for the tooltip drop-shadow. It
  20947. * needs to have an id specific to the chart, otherwise there will be issues
  20948. * when one tooltip adopts the filter of a different chart, specifically one
  20949. * where the container is hidden.
  20950. *
  20951. * @private
  20952. * @function Highcharts.Tooltip#applyFilter
  20953. */
  20954. Tooltip.prototype.applyFilter = function () {
  20955. var chart = this.chart;
  20956. chart.renderer.definition({
  20957. tagName: 'filter',
  20958. id: 'drop-shadow-' + chart.index,
  20959. opacity: 0.5,
  20960. children: [{
  20961. tagName: 'feGaussianBlur',
  20962. 'in': 'SourceAlpha',
  20963. stdDeviation: 1
  20964. }, {
  20965. tagName: 'feOffset',
  20966. dx: 1,
  20967. dy: 1
  20968. }, {
  20969. tagName: 'feComponentTransfer',
  20970. children: [{
  20971. tagName: 'feFuncA',
  20972. type: 'linear',
  20973. slope: 0.3
  20974. }]
  20975. }, {
  20976. tagName: 'feMerge',
  20977. children: [{
  20978. tagName: 'feMergeNode'
  20979. }, {
  20980. tagName: 'feMergeNode',
  20981. 'in': 'SourceGraphic'
  20982. }]
  20983. }]
  20984. });
  20985. chart.renderer.definition({
  20986. tagName: 'style',
  20987. textContent: '.highcharts-tooltip-' + chart.index + '{' +
  20988. 'filter:url(#drop-shadow-' + chart.index + ')' +
  20989. '}'
  20990. });
  20991. };
  20992. /**
  20993. * Build the body (lines) of the tooltip by iterating over the items and
  20994. * returning one entry for each item, abstracting this functionality allows
  20995. * to easily overwrite and extend it.
  20996. *
  20997. * @private
  20998. * @function Highcharts.Tooltip#bodyFormatter
  20999. * @param {Array<(Highcharts.Point|Highcharts.Series)>} items
  21000. * @return {Array<string>}
  21001. */
  21002. Tooltip.prototype.bodyFormatter = function (items) {
  21003. return items.map(function (item) {
  21004. var tooltipOptions = item.series.tooltipOptions;
  21005. return (tooltipOptions[(item.point.formatPrefix || 'point') + 'Formatter'] ||
  21006. item.point.tooltipFormatter).call(item.point, tooltipOptions[(item.point.formatPrefix || 'point') + 'Format'] || '');
  21007. });
  21008. };
  21009. /**
  21010. * Destroy the single tooltips in a split tooltip.
  21011. * If the tooltip is active then it is not destroyed, unless forced to.
  21012. *
  21013. * @private
  21014. * @function Highcharts.Tooltip#cleanSplit
  21015. *
  21016. * @param {boolean} [force]
  21017. * Force destroy all tooltips.
  21018. */
  21019. Tooltip.prototype.cleanSplit = function (force) {
  21020. this.chart.series.forEach(function (series) {
  21021. var tt = series && series.tt;
  21022. if (tt) {
  21023. if (!tt.isActive || force) {
  21024. series.tt = tt.destroy();
  21025. }
  21026. else {
  21027. tt.isActive = false;
  21028. }
  21029. }
  21030. });
  21031. };
  21032. /**
  21033. * In case no user defined formatter is given, this will be used. Note that
  21034. * the context here is an object holding point, series, x, y etc.
  21035. *
  21036. * @function Highcharts.Tooltip#defaultFormatter
  21037. *
  21038. * @param {Highcharts.Tooltip} tooltip
  21039. *
  21040. * @return {Array<string>}
  21041. */
  21042. Tooltip.prototype.defaultFormatter = function (tooltip) {
  21043. var items = this.points || splat(this), s;
  21044. // Build the header
  21045. s = [tooltip.tooltipFooterHeaderFormatter(items[0])];
  21046. // build the values
  21047. s = s.concat(tooltip.bodyFormatter(items));
  21048. // footer
  21049. s.push(tooltip.tooltipFooterHeaderFormatter(items[0], true));
  21050. return s;
  21051. };
  21052. /**
  21053. * Removes and destroys the tooltip and its elements.
  21054. *
  21055. * @function Highcharts.Tooltip#destroy
  21056. */
  21057. Tooltip.prototype.destroy = function () {
  21058. // Destroy and clear local variables
  21059. if (this.label) {
  21060. this.label = this.label.destroy();
  21061. }
  21062. if (this.split && this.tt) {
  21063. this.cleanSplit(this.chart, true);
  21064. this.tt = this.tt.destroy();
  21065. }
  21066. if (this.renderer) {
  21067. this.renderer = this.renderer.destroy();
  21068. discardElement(this.container);
  21069. }
  21070. U.clearTimeout(this.hideTimer);
  21071. U.clearTimeout(this.tooltipTimeout);
  21072. };
  21073. /**
  21074. * Extendable method to get the anchor position of the tooltip
  21075. * from a point or set of points
  21076. *
  21077. * @private
  21078. * @function Highcharts.Tooltip#getAnchor
  21079. *
  21080. * @param {Highcharts.Point|Array<Highcharts.Point>} points
  21081. *
  21082. * @param {Highcharts.PointerEventObject} [mouseEvent]
  21083. *
  21084. * @return {Array<number>}
  21085. */
  21086. Tooltip.prototype.getAnchor = function (points, mouseEvent) {
  21087. var ret, chart = this.chart, pointer = chart.pointer, inverted = chart.inverted, plotTop = chart.plotTop, plotLeft = chart.plotLeft, plotX = 0, plotY = 0, yAxis, xAxis;
  21088. points = splat(points);
  21089. // When tooltip follows mouse, relate the position to the mouse
  21090. if (this.followPointer && mouseEvent) {
  21091. if (typeof mouseEvent.chartX === 'undefined') {
  21092. mouseEvent = pointer.normalize(mouseEvent);
  21093. }
  21094. ret = [
  21095. mouseEvent.chartX - plotLeft,
  21096. mouseEvent.chartY - plotTop
  21097. ];
  21098. // Some series types use a specificly calculated tooltip position for
  21099. // each point
  21100. }
  21101. else if (points[0].tooltipPos) {
  21102. ret = points[0].tooltipPos;
  21103. // When shared, use the average position
  21104. }
  21105. else {
  21106. points.forEach(function (point) {
  21107. yAxis = point.series.yAxis;
  21108. xAxis = point.series.xAxis;
  21109. plotX += point.plotX +
  21110. (!inverted && xAxis ? xAxis.left - plotLeft : 0);
  21111. plotY += (point.plotLow ?
  21112. (point.plotLow + point.plotHigh) / 2 :
  21113. point.plotY) + (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151
  21114. });
  21115. plotX /= points.length;
  21116. plotY /= points.length;
  21117. ret = [
  21118. inverted ? chart.plotWidth - plotY : plotX,
  21119. this.shared && !inverted && points.length > 1 && mouseEvent ?
  21120. // place shared tooltip next to the mouse (#424)
  21121. mouseEvent.chartY - plotTop :
  21122. inverted ? chart.plotHeight - plotX : plotY
  21123. ];
  21124. }
  21125. return ret.map(Math.round);
  21126. };
  21127. /**
  21128. * Get the optimal date format for a point, based on a range.
  21129. *
  21130. * @private
  21131. * @function Highcharts.Tooltip#getDateFormat
  21132. *
  21133. * @param {number} range
  21134. * The time range
  21135. *
  21136. * @param {number} date
  21137. * The date of the point in question
  21138. *
  21139. * @param {number} startOfWeek
  21140. * An integer representing the first day of the week, where 0 is
  21141. * Sunday.
  21142. *
  21143. * @param {Highcharts.Dictionary<string>} dateTimeLabelFormats
  21144. * A map of time units to formats.
  21145. *
  21146. * @return {string}
  21147. * The optimal date format for a point.
  21148. */
  21149. Tooltip.prototype.getDateFormat = function (range, date, startOfWeek, dateTimeLabelFormats) {
  21150. var time = this.chart.time, dateStr = time.dateFormat('%m-%d %H:%M:%S.%L', date), format, n, blank = '01-01 00:00:00.000', strpos = {
  21151. millisecond: 15,
  21152. second: 12,
  21153. minute: 9,
  21154. hour: 6,
  21155. day: 3
  21156. }, lastN = 'millisecond'; // for sub-millisecond data, #4223
  21157. for (n in timeUnits) { // eslint-disable-line guard-for-in
  21158. // If the range is exactly one week and we're looking at a
  21159. // Sunday/Monday, go for the week format
  21160. if (range === timeUnits.week &&
  21161. +time.dateFormat('%w', date) === startOfWeek &&
  21162. dateStr.substr(6) === blank.substr(6)) {
  21163. n = 'week';
  21164. break;
  21165. }
  21166. // The first format that is too great for the range
  21167. if (timeUnits[n] > range) {
  21168. n = lastN;
  21169. break;
  21170. }
  21171. // If the point is placed every day at 23:59, we need to show
  21172. // the minutes as well. #2637.
  21173. if (strpos[n] &&
  21174. dateStr.substr(strpos[n]) !== blank.substr(strpos[n])) {
  21175. break;
  21176. }
  21177. // Weeks are outside the hierarchy, only apply them on
  21178. // Mondays/Sundays like in the first condition
  21179. if (n !== 'week') {
  21180. lastN = n;
  21181. }
  21182. }
  21183. if (n) {
  21184. format = time.resolveDTLFormat(dateTimeLabelFormats[n]).main;
  21185. }
  21186. return format;
  21187. };
  21188. /**
  21189. * Creates the Tooltip label element if it does not exist, then returns it.
  21190. *
  21191. * @function Highcharts.Tooltip#getLabel
  21192. * @return {Highcharts.SVGElement}
  21193. */
  21194. Tooltip.prototype.getLabel = function () {
  21195. var _a, _b;
  21196. var tooltip = this, renderer = this.chart.renderer, styledMode = this.chart.styledMode, options = this.options, className = ('tooltip' + (defined(options.className) ?
  21197. ' ' + options.className :
  21198. '')), pointerEvents = (((_a = options.style) === null || _a === void 0 ? void 0 : _a.pointerEvents) ||
  21199. (!this.followPointer && options.stickOnContact ? 'auto' : 'none')), container, set, onMouseEnter = function () {
  21200. tooltip.inContact = true;
  21201. }, onMouseLeave = function () {
  21202. var series = tooltip.chart.hoverSeries;
  21203. tooltip.inContact = false;
  21204. if (series &&
  21205. series.onMouseOut) {
  21206. series.onMouseOut();
  21207. }
  21208. };
  21209. if (!this.label) {
  21210. if (this.outside) {
  21211. /**
  21212. * Reference to the tooltip's container, when
  21213. * [Highcharts.Tooltip#outside] is set to true, otherwise
  21214. * it's undefined.
  21215. *
  21216. * @name Highcharts.Tooltip#container
  21217. * @type {Highcharts.HTMLDOMElement|undefined}
  21218. */
  21219. this.container = container = H.doc.createElement('div');
  21220. container.className = 'highcharts-tooltip-container';
  21221. css(container, {
  21222. position: 'absolute',
  21223. top: '1px',
  21224. pointerEvents: pointerEvents,
  21225. zIndex: 3
  21226. });
  21227. H.doc.body.appendChild(container);
  21228. /**
  21229. * Reference to the tooltip's renderer, when
  21230. * [Highcharts.Tooltip#outside] is set to true, otherwise
  21231. * it's undefined.
  21232. *
  21233. * @name Highcharts.Tooltip#renderer
  21234. * @type {Highcharts.SVGRenderer|undefined}
  21235. */
  21236. this.renderer = renderer = new H.Renderer(container, 0, 0, (_b = this.chart.options.chart) === null || _b === void 0 ? void 0 : _b.style, void 0, void 0, renderer.styledMode);
  21237. }
  21238. // Create the label
  21239. if (this.split) {
  21240. this.label = renderer.g(className);
  21241. }
  21242. else {
  21243. this.label = renderer
  21244. .label('', 0, 0, options.shape || 'callout', null, null, options.useHTML, null, className)
  21245. .attr({
  21246. padding: options.padding,
  21247. r: options.borderRadius
  21248. });
  21249. if (!styledMode) {
  21250. this.label
  21251. .attr({
  21252. fill: options.backgroundColor,
  21253. 'stroke-width': options.borderWidth
  21254. })
  21255. // #2301, #2657
  21256. .css(options.style)
  21257. .css({ pointerEvents: pointerEvents })
  21258. .shadow(options.shadow);
  21259. }
  21260. }
  21261. if (styledMode) {
  21262. // Apply the drop-shadow filter
  21263. this.applyFilter();
  21264. this.label.addClass('highcharts-tooltip-' + this.chart.index);
  21265. }
  21266. // Split tooltip use updateTooltipContainer to position the tooltip
  21267. // container.
  21268. if (tooltip.outside && !tooltip.split) {
  21269. var label_1 = this.label;
  21270. var xSetter_1 = label_1.xSetter, ySetter_1 = label_1.ySetter;
  21271. label_1.xSetter = function (value) {
  21272. xSetter_1.call(label_1, tooltip.distance);
  21273. container.style.left = value + 'px';
  21274. };
  21275. label_1.ySetter = function (value) {
  21276. ySetter_1.call(label_1, tooltip.distance);
  21277. container.style.top = value + 'px';
  21278. };
  21279. }
  21280. this.label
  21281. .on('mouseenter', onMouseEnter)
  21282. .on('mouseleave', onMouseLeave)
  21283. .attr({ zIndex: 8 })
  21284. .add();
  21285. }
  21286. return this.label;
  21287. };
  21288. /**
  21289. * Place the tooltip in a chart without spilling over
  21290. * and not covering the point it self.
  21291. *
  21292. * @private
  21293. * @function Highcharts.Tooltip#getPosition
  21294. *
  21295. * @param {number} boxWidth
  21296. *
  21297. * @param {number} boxHeight
  21298. *
  21299. * @param {Highcharts.Point} point
  21300. *
  21301. * @return {Highcharts.PositionObject}
  21302. */
  21303. Tooltip.prototype.getPosition = function (boxWidth, boxHeight, point) {
  21304. var chart = this.chart, distance = this.distance, ret = {},
  21305. // Don't use h if chart isn't inverted (#7242) ???
  21306. h = (chart.inverted && point.h) || 0, // #4117 ???
  21307. swapped, outside = this.outside, outerWidth = outside ?
  21308. // substract distance to prevent scrollbars
  21309. doc.documentElement.clientWidth - 2 * distance :
  21310. chart.chartWidth, outerHeight = outside ?
  21311. Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight, doc.body.offsetHeight, doc.documentElement.offsetHeight, doc.documentElement.clientHeight) :
  21312. chart.chartHeight, chartPosition = chart.pointer.getChartPosition(), containerScaling = chart.containerScaling, scaleX = function (val) { return ( // eslint-disable-line no-confusing-arrow
  21313. containerScaling ? val * containerScaling.scaleX : val); }, scaleY = function (val) { return ( // eslint-disable-line no-confusing-arrow
  21314. containerScaling ? val * containerScaling.scaleY : val); },
  21315. // Build parameter arrays for firstDimension()/secondDimension()
  21316. buildDimensionArray = function (dim) {
  21317. var isX = dim === 'x';
  21318. return [
  21319. dim,
  21320. isX ? outerWidth : outerHeight,
  21321. isX ? boxWidth : boxHeight
  21322. ].concat(outside ? [
  21323. // If we are using tooltip.outside, we need to scale the
  21324. // position to match scaling of the container in case there
  21325. // is a transform/zoom on the container. #11329
  21326. isX ? scaleX(boxWidth) : scaleY(boxHeight),
  21327. isX ? chartPosition.left - distance +
  21328. scaleX(point.plotX + chart.plotLeft) :
  21329. chartPosition.top - distance +
  21330. scaleY(point.plotY + chart.plotTop),
  21331. 0,
  21332. isX ? outerWidth : outerHeight
  21333. ] : [
  21334. // Not outside, no scaling is needed
  21335. isX ? boxWidth : boxHeight,
  21336. isX ? point.plotX + chart.plotLeft :
  21337. point.plotY + chart.plotTop,
  21338. isX ? chart.plotLeft : chart.plotTop,
  21339. isX ? chart.plotLeft + chart.plotWidth :
  21340. chart.plotTop + chart.plotHeight
  21341. ]);
  21342. }, first = buildDimensionArray('y'), second = buildDimensionArray('x'),
  21343. // The far side is right or bottom
  21344. preferFarSide = !this.followPointer && pick(point.ttBelow, !chart.inverted === !!point.negative), // #4984
  21345. /*
  21346. * Handle the preferred dimension. When the preferred dimension is
  21347. * tooltip on top or bottom of the point, it will look for space
  21348. * there.
  21349. *
  21350. * @private
  21351. */
  21352. firstDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  21353. point, min, max) {
  21354. var scaledDist = dim === 'y' ?
  21355. scaleY(distance) : scaleX(distance), scaleDiff = (innerSize - scaledInnerSize) / 2, roomLeft = scaledInnerSize < point - distance, roomRight = point + distance + scaledInnerSize < outerSize, alignedLeft = point - scaledDist - innerSize + scaleDiff, alignedRight = point + scaledDist - scaleDiff;
  21356. if (preferFarSide && roomRight) {
  21357. ret[dim] = alignedRight;
  21358. }
  21359. else if (!preferFarSide && roomLeft) {
  21360. ret[dim] = alignedLeft;
  21361. }
  21362. else if (roomLeft) {
  21363. ret[dim] = Math.min(max - scaledInnerSize, alignedLeft - h < 0 ? alignedLeft : alignedLeft - h);
  21364. }
  21365. else if (roomRight) {
  21366. ret[dim] = Math.max(min, alignedRight + h + innerSize > outerSize ?
  21367. alignedRight :
  21368. alignedRight + h);
  21369. }
  21370. else {
  21371. return false;
  21372. }
  21373. },
  21374. /*
  21375. * Handle the secondary dimension. If the preferred dimension is
  21376. * tooltip on top or bottom of the point, the second dimension is to
  21377. * align the tooltip above the point, trying to align center but
  21378. * allowing left or right align within the chart box.
  21379. *
  21380. * @private
  21381. */
  21382. secondDimension = function (dim, outerSize, innerSize, scaledInnerSize, // #11329
  21383. point) {
  21384. var retVal;
  21385. // Too close to the edge, return false and swap dimensions
  21386. if (point < distance || point > outerSize - distance) {
  21387. retVal = false;
  21388. // Align left/top
  21389. }
  21390. else if (point < innerSize / 2) {
  21391. ret[dim] = 1;
  21392. // Align right/bottom
  21393. }
  21394. else if (point > outerSize - scaledInnerSize / 2) {
  21395. ret[dim] = outerSize - scaledInnerSize - 2;
  21396. // Align center
  21397. }
  21398. else {
  21399. ret[dim] = point - innerSize / 2;
  21400. }
  21401. return retVal;
  21402. },
  21403. /*
  21404. * Swap the dimensions
  21405. */
  21406. swap = function (count) {
  21407. var temp = first;
  21408. first = second;
  21409. second = temp;
  21410. swapped = count;
  21411. }, run = function () {
  21412. if (firstDimension.apply(0, first) !== false) {
  21413. if (secondDimension.apply(0, second) === false &&
  21414. !swapped) {
  21415. swap(true);
  21416. run();
  21417. }
  21418. }
  21419. else if (!swapped) {
  21420. swap(true);
  21421. run();
  21422. }
  21423. else {
  21424. ret.x = ret.y = 0;
  21425. }
  21426. };
  21427. // Under these conditions, prefer the tooltip on the side of the point
  21428. if (chart.inverted || this.len > 1) {
  21429. swap();
  21430. }
  21431. run();
  21432. return ret;
  21433. };
  21434. /**
  21435. * Get the best X date format based on the closest point range on the axis.
  21436. *
  21437. * @private
  21438. * @function Highcharts.Tooltip#getXDateFormat
  21439. *
  21440. * @param {Highcharts.Point} point
  21441. *
  21442. * @param {Highcharts.TooltipOptions} options
  21443. *
  21444. * @param {Highcharts.Axis} xAxis
  21445. *
  21446. * @return {string}
  21447. */
  21448. Tooltip.prototype.getXDateFormat = function (point, options, xAxis) {
  21449. var xDateFormat, dateTimeLabelFormats = options.dateTimeLabelFormats, closestPointRange = xAxis && xAxis.closestPointRange;
  21450. if (closestPointRange) {
  21451. xDateFormat = this.getDateFormat(closestPointRange, point.x, xAxis.options.startOfWeek, dateTimeLabelFormats);
  21452. }
  21453. else {
  21454. xDateFormat = dateTimeLabelFormats.day;
  21455. }
  21456. return xDateFormat || dateTimeLabelFormats.year; // #2546, 2581
  21457. };
  21458. /**
  21459. * Hides the tooltip with a fade out animation.
  21460. *
  21461. * @function Highcharts.Tooltip#hide
  21462. *
  21463. * @param {number} [delay]
  21464. * The fade out in milliseconds. If no value is provided the value
  21465. * of the tooltip.hideDelay option is used. A value of 0 disables
  21466. * the fade out animation.
  21467. */
  21468. Tooltip.prototype.hide = function (delay) {
  21469. var tooltip = this;
  21470. // disallow duplicate timers (#1728, #1766)
  21471. U.clearTimeout(this.hideTimer);
  21472. delay = pick(delay, this.options.hideDelay, 500);
  21473. if (!this.isHidden) {
  21474. this.hideTimer = syncTimeout(function () {
  21475. // If there is a delay, do fadeOut with the default duration. If
  21476. // the hideDelay is 0, we assume no animation is wanted, so we
  21477. // pass 0 duration. #12994.
  21478. tooltip.getLabel().fadeOut(delay ? void 0 : delay);
  21479. tooltip.isHidden = true;
  21480. }, delay);
  21481. }
  21482. };
  21483. /**
  21484. * @private
  21485. * @function Highcharts.Tooltip#init
  21486. *
  21487. * @param {Highcharts.Chart} chart
  21488. * The chart instance.
  21489. *
  21490. * @param {Highcharts.TooltipOptions} options
  21491. * Tooltip options.
  21492. */
  21493. Tooltip.prototype.init = function (chart, options) {
  21494. /**
  21495. * Chart of the tooltip.
  21496. *
  21497. * @readonly
  21498. * @name Highcharts.Tooltip#chart
  21499. * @type {Highcharts.Chart}
  21500. */
  21501. this.chart = chart;
  21502. /**
  21503. * Used tooltip options.
  21504. *
  21505. * @readonly
  21506. * @name Highcharts.Tooltip#options
  21507. * @type {Highcharts.TooltipOptions}
  21508. */
  21509. this.options = options;
  21510. /**
  21511. * List of crosshairs.
  21512. *
  21513. * @private
  21514. * @readonly
  21515. * @name Highcharts.Tooltip#crosshairs
  21516. * @type {Array<null>}
  21517. */
  21518. this.crosshairs = [];
  21519. /**
  21520. * Current values of x and y when animating.
  21521. *
  21522. * @private
  21523. * @readonly
  21524. * @name Highcharts.Tooltip#now
  21525. * @type {Highcharts.PositionObject}
  21526. */
  21527. this.now = { x: 0, y: 0 };
  21528. /**
  21529. * Tooltips are initially hidden.
  21530. *
  21531. * @private
  21532. * @readonly
  21533. * @name Highcharts.Tooltip#isHidden
  21534. * @type {boolean}
  21535. */
  21536. this.isHidden = true;
  21537. /**
  21538. * True, if the tooltip is split into one label per series, with the
  21539. * header close to the axis.
  21540. *
  21541. * @readonly
  21542. * @name Highcharts.Tooltip#split
  21543. * @type {boolean|undefined}
  21544. */
  21545. this.split = options.split && !chart.inverted && !chart.polar;
  21546. /**
  21547. * When the tooltip is shared, the entire plot area will capture mouse
  21548. * movement or touch events.
  21549. *
  21550. * @readonly
  21551. * @name Highcharts.Tooltip#shared
  21552. * @type {boolean|undefined}
  21553. */
  21554. this.shared = options.shared || this.split;
  21555. /**
  21556. * Whether to allow the tooltip to render outside the chart's SVG
  21557. * element box. By default (false), the tooltip is rendered within the
  21558. * chart's SVG element, which results in the tooltip being aligned
  21559. * inside the chart area.
  21560. *
  21561. * @readonly
  21562. * @name Highcharts.Tooltip#outside
  21563. * @type {boolean}
  21564. *
  21565. * @todo
  21566. * Split tooltip does not support outside in the first iteration. Should
  21567. * not be too complicated to implement.
  21568. */
  21569. this.outside = pick(options.outside, Boolean(chart.scrollablePixelsX || chart.scrollablePixelsY));
  21570. };
  21571. /**
  21572. * Returns true, if the pointer is in contact with the tooltip tracker.
  21573. */
  21574. Tooltip.prototype.isStickyOnContact = function () {
  21575. return !!(!this.followPointer &&
  21576. this.options.stickOnContact &&
  21577. this.inContact);
  21578. };
  21579. /**
  21580. * Moves the tooltip with a soft animation to a new position.
  21581. *
  21582. * @private
  21583. * @function Highcharts.Tooltip#move
  21584. *
  21585. * @param {number} x
  21586. *
  21587. * @param {number} y
  21588. *
  21589. * @param {number} anchorX
  21590. *
  21591. * @param {number} anchorY
  21592. */
  21593. Tooltip.prototype.move = function (x, y, anchorX, anchorY) {
  21594. var tooltip = this, now = tooltip.now, animate = tooltip.options.animation !== false &&
  21595. !tooltip.isHidden &&
  21596. // When we get close to the target position, abort animation and
  21597. // land on the right place (#3056)
  21598. (Math.abs(x - now.x) > 1 || Math.abs(y - now.y) > 1), skipAnchor = tooltip.followPointer || tooltip.len > 1;
  21599. // Get intermediate values for animation
  21600. extend(now, {
  21601. x: animate ? (2 * now.x + x) / 3 : x,
  21602. y: animate ? (now.y + y) / 2 : y,
  21603. anchorX: skipAnchor ?
  21604. void 0 :
  21605. animate ? (2 * now.anchorX + anchorX) / 3 : anchorX,
  21606. anchorY: skipAnchor ?
  21607. void 0 :
  21608. animate ? (now.anchorY + anchorY) / 2 : anchorY
  21609. });
  21610. // Move to the intermediate value
  21611. tooltip.getLabel().attr(now);
  21612. tooltip.drawTracker();
  21613. // Run on next tick of the mouse tracker
  21614. if (animate) {
  21615. // Never allow two timeouts
  21616. U.clearTimeout(this.tooltipTimeout);
  21617. // Set the fixed interval ticking for the smooth tooltip
  21618. this.tooltipTimeout = setTimeout(function () {
  21619. // The interval function may still be running during destroy,
  21620. // so check that the chart is really there before calling.
  21621. if (tooltip) {
  21622. tooltip.move(x, y, anchorX, anchorY);
  21623. }
  21624. }, 32);
  21625. }
  21626. };
  21627. /**
  21628. * Refresh the tooltip's text and position.
  21629. *
  21630. * @function Highcharts.Tooltip#refresh
  21631. *
  21632. * @param {Highcharts.Point|Array<Highcharts.Point>} pointOrPoints
  21633. * Either a point or an array of points.
  21634. *
  21635. * @param {Highcharts.PointerEventObject} [mouseEvent]
  21636. * Mouse event, that is responsible for the refresh and should be
  21637. * used for the tooltip update.
  21638. */
  21639. Tooltip.prototype.refresh = function (pointOrPoints, mouseEvent) {
  21640. var tooltip = this, chart = this.chart, options = tooltip.options, x, y, point = pointOrPoints, anchor, textConfig = {}, text, pointConfig = [], formatter = options.formatter || tooltip.defaultFormatter, shared = tooltip.shared, currentSeries, styledMode = chart.styledMode;
  21641. if (!options.enabled) {
  21642. return;
  21643. }
  21644. U.clearTimeout(this.hideTimer);
  21645. // get the reference point coordinates (pie charts use tooltipPos)
  21646. tooltip.followPointer = splat(point)[0].series.tooltipOptions
  21647. .followPointer;
  21648. anchor = tooltip.getAnchor(point, mouseEvent);
  21649. x = anchor[0];
  21650. y = anchor[1];
  21651. // shared tooltip, array is sent over
  21652. if (shared &&
  21653. !(point.series &&
  21654. point.series.noSharedTooltip)) {
  21655. chart.pointer.applyInactiveState(point);
  21656. // Now set hover state for the choosen ones:
  21657. point.forEach(function (item) {
  21658. item.setState('hover');
  21659. pointConfig.push(item.getLabelConfig());
  21660. });
  21661. textConfig = {
  21662. x: point[0].category,
  21663. y: point[0].y
  21664. };
  21665. textConfig.points = pointConfig;
  21666. point = point[0];
  21667. // single point tooltip
  21668. }
  21669. else {
  21670. textConfig = point.getLabelConfig();
  21671. }
  21672. this.len = pointConfig.length; // #6128
  21673. text = formatter.call(textConfig, tooltip);
  21674. // register the current series
  21675. currentSeries = point.series;
  21676. this.distance = pick(currentSeries.tooltipOptions.distance, 16);
  21677. // update the inner HTML
  21678. if (text === false) {
  21679. this.hide();
  21680. }
  21681. else {
  21682. // update text
  21683. if (tooltip.split) {
  21684. this.renderSplit(text, splat(pointOrPoints));
  21685. }
  21686. else {
  21687. var label = tooltip.getLabel();
  21688. // Prevent the tooltip from flowing over the chart box (#6659)
  21689. if (!options.style.width || styledMode) {
  21690. label.css({
  21691. width: this.chart.spacingBox.width + 'px'
  21692. });
  21693. }
  21694. label.attr({
  21695. text: text && text.join ?
  21696. text.join('') :
  21697. text
  21698. });
  21699. // Set the stroke color of the box to reflect the point
  21700. label.removeClass(/highcharts-color-[\d]+/g)
  21701. .addClass('highcharts-color-' +
  21702. pick(point.colorIndex, currentSeries.colorIndex));
  21703. if (!styledMode) {
  21704. label.attr({
  21705. stroke: (options.borderColor ||
  21706. point.color ||
  21707. currentSeries.color ||
  21708. '#666666')
  21709. });
  21710. }
  21711. tooltip.updatePosition({
  21712. plotX: x,
  21713. plotY: y,
  21714. negative: point.negative,
  21715. ttBelow: point.ttBelow,
  21716. h: anchor[2] || 0
  21717. });
  21718. }
  21719. // show it
  21720. if (tooltip.isHidden && tooltip.label) {
  21721. tooltip.label.attr({
  21722. opacity: 1
  21723. }).show();
  21724. }
  21725. tooltip.isHidden = false;
  21726. }
  21727. fireEvent(this, 'refresh');
  21728. };
  21729. /**
  21730. * Render the split tooltip. Loops over each point's text and adds
  21731. * a label next to the point, then uses the distribute function to
  21732. * find best non-overlapping positions.
  21733. *
  21734. * @private
  21735. * @function Highcharts.Tooltip#renderSplit
  21736. *
  21737. * @param {string|Array<(boolean|string)>} labels
  21738. *
  21739. * @param {Array<Highcharts.Point>} points
  21740. */
  21741. Tooltip.prototype.renderSplit = function (labels, points) {
  21742. var tooltip = this;
  21743. var chart = tooltip.chart, _a = tooltip.chart, chartWidth = _a.chartWidth, chartHeight = _a.chartHeight, plotHeight = _a.plotHeight, plotLeft = _a.plotLeft, plotTop = _a.plotTop, pointer = _a.pointer, ren = _a.renderer, _b = _a.scrollablePixelsY, scrollablePixelsY = _b === void 0 ? 0 : _b, _c = _a.scrollingContainer, _d = _c === void 0 ? { scrollLeft: 0, scrollTop: 0 } : _c, scrollLeft = _d.scrollLeft, scrollTop = _d.scrollTop, styledMode = _a.styledMode, distance = tooltip.distance, options = tooltip.options, positioner = tooltip.options.positioner;
  21744. // The area which the tooltip should be limited to. Limit to scrollable
  21745. // plot area if enabled, otherwise limit to the chart container.
  21746. var bounds = {
  21747. left: scrollLeft,
  21748. right: scrollLeft + chartWidth,
  21749. top: scrollTop,
  21750. bottom: scrollTop + chartHeight
  21751. };
  21752. var tooltipLabel = tooltip.getLabel();
  21753. var headerTop = Boolean(chart.xAxis[0] && chart.xAxis[0].opposite);
  21754. var distributionBoxTop = plotTop + scrollTop;
  21755. var headerHeight = 0;
  21756. var adjustedPlotHeight = plotHeight - scrollablePixelsY;
  21757. /**
  21758. * Calculates the anchor position for the partial tooltip
  21759. *
  21760. * @private
  21761. * @param {Highcharts.Point} point The point related to the tooltip
  21762. * @return {object} Returns an object with anchorX and anchorY
  21763. */
  21764. function getAnchor(point) {
  21765. var isHeader = point.isHeader, _a = point.plotX, plotX = _a === void 0 ? 0 : _a, _b = point.plotY, plotY = _b === void 0 ? 0 : _b, series = point.series;
  21766. var anchorX;
  21767. var anchorY;
  21768. if (isHeader) {
  21769. // Set anchorX to plotX
  21770. anchorX = plotLeft + plotX;
  21771. // Set anchorY to center of visible plot area.
  21772. anchorY = plotTop + plotHeight / 2;
  21773. }
  21774. else {
  21775. var xAxis = series.xAxis, yAxis = series.yAxis;
  21776. // Set anchorX to plotX. Limit to within xAxis.
  21777. anchorX = xAxis.pos + clamp(plotX, -distance, xAxis.len + distance);
  21778. // Set anchorY, limit to the scrollable plot area
  21779. if (yAxis.pos + plotY >= scrollTop + plotTop &&
  21780. yAxis.pos + plotY <= scrollTop + plotTop + plotHeight - scrollablePixelsY) {
  21781. anchorY = yAxis.pos + plotY;
  21782. }
  21783. }
  21784. // Limit values to plot area
  21785. anchorX = clamp(anchorX, bounds.left - distance, bounds.right + distance);
  21786. return { anchorX: anchorX, anchorY: anchorY };
  21787. }
  21788. /**
  21789. * Calculates the position of the partial tooltip
  21790. *
  21791. * @private
  21792. * @param {number} anchorX The partial tooltip anchor x position
  21793. * @param {number} anchorY The partial tooltip anchor y position
  21794. * @param {boolean} isHeader Whether the partial tooltip is a header
  21795. * @param {number} boxWidth Width of the partial tooltip
  21796. * @return {Highcharts.PositionObject} Returns the partial tooltip x and
  21797. * y position
  21798. */
  21799. function defaultPositioner(anchorX, anchorY, isHeader, boxWidth, alignedLeft) {
  21800. if (alignedLeft === void 0) { alignedLeft = true; }
  21801. var y;
  21802. var x;
  21803. if (isHeader) {
  21804. y = headerTop ? 0 : adjustedPlotHeight;
  21805. x = clamp(anchorX - (boxWidth / 2), bounds.left, bounds.right - boxWidth);
  21806. }
  21807. else {
  21808. y = anchorY - distributionBoxTop;
  21809. x = alignedLeft ?
  21810. anchorX - boxWidth - distance :
  21811. anchorX + distance;
  21812. x = clamp(x, alignedLeft ? x : bounds.left, bounds.right);
  21813. }
  21814. // NOTE: y is relative to distributionBoxTop
  21815. return { x: x, y: y };
  21816. }
  21817. /**
  21818. * Updates the attributes and styling of the partial tooltip. Creates a
  21819. * new partial tooltip if it does not exists.
  21820. *
  21821. * @private
  21822. * @param {Highcharts.SVGElement|undefined} partialTooltip
  21823. * The partial tooltip to update
  21824. * @param {Highcharts.Point} point
  21825. * The point related to the partial tooltip
  21826. * @param {boolean|string} str The text for the partial tooltip
  21827. * @return {Highcharts.SVGElement} Returns the updated partial tooltip
  21828. */
  21829. function updatePartialTooltip(partialTooltip, point, str) {
  21830. var tt = partialTooltip;
  21831. var isHeader = point.isHeader, series = point.series;
  21832. var colorClass = 'highcharts-color-' + pick(point.colorIndex, series.colorIndex, 'none');
  21833. if (!tt) {
  21834. var attribs = {
  21835. padding: options.padding,
  21836. r: options.borderRadius
  21837. };
  21838. if (!styledMode) {
  21839. attribs.fill = options.backgroundColor;
  21840. attribs['stroke-width'] = options.borderWidth;
  21841. }
  21842. tt = ren
  21843. .label('', 0, 0, (options[isHeader ? 'headerShape' : 'shape']) ||
  21844. 'callout', void 0, void 0, options.useHTML)
  21845. .addClass((isHeader ? 'highcharts-tooltip-header ' : '') +
  21846. 'highcharts-tooltip-box ' +
  21847. colorClass)
  21848. .attr(attribs)
  21849. .add(tooltipLabel);
  21850. }
  21851. tt.isActive = true;
  21852. tt.attr({
  21853. text: str
  21854. });
  21855. if (!styledMode) {
  21856. tt.css(options.style)
  21857. .shadow(options.shadow)
  21858. .attr({
  21859. stroke: (options.borderColor ||
  21860. point.color ||
  21861. series.color ||
  21862. '#333333')
  21863. });
  21864. }
  21865. return tt;
  21866. }
  21867. // Graceful degradation for legacy formatters
  21868. if (isString(labels)) {
  21869. labels = [false, labels];
  21870. }
  21871. // Create the individual labels for header and points, ignore footer
  21872. var boxes = labels.slice(0, points.length + 1).reduce(function (boxes, str, i) {
  21873. if (str !== false && str !== '') {
  21874. var point = (points[i - 1] ||
  21875. {
  21876. // Item 0 is the header. Instead of this, we could also
  21877. // use the crosshair label
  21878. isHeader: true,
  21879. plotX: points[0].plotX,
  21880. plotY: plotHeight,
  21881. series: {}
  21882. });
  21883. var isHeader = point.isHeader;
  21884. // Store the tooltip label referance on the series
  21885. var owner = isHeader ? tooltip : point.series;
  21886. var tt = owner.tt = updatePartialTooltip(owner.tt, point, str);
  21887. // Get X position now, so we can move all to the other side in
  21888. // case of overflow
  21889. var bBox = tt.getBBox();
  21890. var boxWidth = bBox.width + tt.strokeWidth();
  21891. if (isHeader) {
  21892. headerHeight = bBox.height;
  21893. adjustedPlotHeight += headerHeight;
  21894. if (headerTop) {
  21895. distributionBoxTop -= headerHeight;
  21896. }
  21897. }
  21898. var _a = getAnchor(point), anchorX = _a.anchorX, anchorY = _a.anchorY;
  21899. if (typeof anchorY === 'number') {
  21900. var size = bBox.height + 1;
  21901. var boxPosition = (positioner ?
  21902. positioner.call(tooltip, boxWidth, size, point) :
  21903. defaultPositioner(anchorX, anchorY, isHeader, boxWidth));
  21904. boxes.push({
  21905. // 0-align to the top, 1-align to the bottom
  21906. align: positioner ? 0 : void 0,
  21907. anchorX: anchorX,
  21908. anchorY: anchorY,
  21909. boxWidth: boxWidth,
  21910. point: point,
  21911. rank: pick(boxPosition.rank, isHeader ? 1 : 0),
  21912. size: size,
  21913. target: boxPosition.y,
  21914. tt: tt,
  21915. x: boxPosition.x
  21916. });
  21917. }
  21918. else {
  21919. // Hide tooltips which anchorY is outside the visible plot
  21920. // area
  21921. tt.isActive = false;
  21922. }
  21923. }
  21924. return boxes;
  21925. }, []);
  21926. // If overflow left then align all labels to the right
  21927. if (!positioner && boxes.some(function (box) { return box.x < bounds.left; })) {
  21928. boxes = boxes.map(function (box) {
  21929. var _a = defaultPositioner(box.anchorX, box.anchorY, box.point.isHeader, box.boxWidth, false), x = _a.x, y = _a.y;
  21930. return extend(box, {
  21931. target: y,
  21932. x: x
  21933. });
  21934. });
  21935. }
  21936. // Clean previous run (for missing points)
  21937. tooltip.cleanSplit();
  21938. // Distribute and put in place
  21939. H.distribute(boxes, adjustedPlotHeight);
  21940. boxes.forEach(function (box) {
  21941. var anchorX = box.anchorX, anchorY = box.anchorY, pos = box.pos, x = box.x;
  21942. // Put the label in place
  21943. box.tt.attr({
  21944. visibility: typeof pos === 'undefined' ? 'hidden' : 'inherit',
  21945. x: x,
  21946. /* NOTE: y should equal pos to be consistent with !split
  21947. * tooltip, but is currently relative to plotTop. Is left as is
  21948. * to avoid breaking change. Remove distributionBoxTop to make
  21949. * it consistent.
  21950. */
  21951. y: pos + distributionBoxTop,
  21952. anchorX: anchorX,
  21953. anchorY: anchorY
  21954. });
  21955. });
  21956. /* If we have a seperate tooltip container, then update the necessary
  21957. * container properties.
  21958. * Test that tooltip has its own container and renderer before executing
  21959. * the operation.
  21960. */
  21961. var container = tooltip.container, outside = tooltip.outside, renderer = tooltip.renderer;
  21962. if (outside && container && renderer) {
  21963. // Set container size to fit the tooltip
  21964. var _e = tooltipLabel.getBBox(), width = _e.width, height = _e.height, x = _e.x, y = _e.y;
  21965. renderer.setSize(width + x, height + y, false);
  21966. // Position the tooltip container to the chart container
  21967. var chartPosition = pointer.getChartPosition();
  21968. container.style.left = chartPosition.left + 'px';
  21969. container.style.top = chartPosition.top + 'px';
  21970. }
  21971. };
  21972. /**
  21973. * If the `stickOnContact` option is active, this will add a tracker shape.
  21974. *
  21975. * @private
  21976. * @function Highcharts.Tooltip#drawTracker
  21977. */
  21978. Tooltip.prototype.drawTracker = function () {
  21979. var tooltip = this;
  21980. if (tooltip.followPointer ||
  21981. !tooltip.options.stickOnContact) {
  21982. if (tooltip.tracker) {
  21983. tooltip.tracker.destroy();
  21984. }
  21985. return;
  21986. }
  21987. var chart = tooltip.chart;
  21988. var label = tooltip.label;
  21989. var point = chart.hoverPoint;
  21990. if (!label || !point) {
  21991. return;
  21992. }
  21993. var box = {
  21994. x: 0,
  21995. y: 0,
  21996. width: 0,
  21997. height: 0
  21998. };
  21999. // Combine anchor and tooltip
  22000. var anchorPos = this.getAnchor(point);
  22001. var labelBBox = label.getBBox();
  22002. anchorPos[0] += chart.plotLeft - label.translateX;
  22003. anchorPos[1] += chart.plotTop - label.translateY;
  22004. // When the mouse pointer is between the anchor point and the label,
  22005. // the label should stick.
  22006. box.x = Math.min(0, anchorPos[0]);
  22007. box.y = Math.min(0, anchorPos[1]);
  22008. box.width = (anchorPos[0] < 0 ?
  22009. Math.max(Math.abs(anchorPos[0]), (labelBBox.width - anchorPos[0])) :
  22010. Math.max(Math.abs(anchorPos[0]), labelBBox.width));
  22011. box.height = (anchorPos[1] < 0 ?
  22012. Math.max(Math.abs(anchorPos[1]), (labelBBox.height - Math.abs(anchorPos[1]))) :
  22013. Math.max(Math.abs(anchorPos[1]), labelBBox.height));
  22014. if (tooltip.tracker) {
  22015. tooltip.tracker.attr(box);
  22016. }
  22017. else {
  22018. tooltip.tracker = label.renderer
  22019. .rect(box)
  22020. .addClass('highcharts-tracker')
  22021. .add(label);
  22022. if (!chart.styledMode) {
  22023. tooltip.tracker.attr({
  22024. fill: 'rgba(0,0,0,0)'
  22025. });
  22026. }
  22027. }
  22028. };
  22029. /**
  22030. * @private
  22031. */
  22032. Tooltip.prototype.styledModeFormat = function (formatString) {
  22033. return formatString
  22034. .replace('style="font-size: 10px"', 'class="highcharts-header"')
  22035. .replace(/style="color:{(point|series)\.color}"/g, 'class="highcharts-color-{$1.colorIndex}"');
  22036. };
  22037. /**
  22038. * Format the footer/header of the tooltip
  22039. * #3397: abstraction to enable formatting of footer and header
  22040. *
  22041. * @private
  22042. * @function Highcharts.Tooltip#tooltipFooterHeaderFormatter
  22043. * @param {Highcharts.PointLabelObject} labelConfig
  22044. * @param {boolean} [isFooter]
  22045. * @return {string}
  22046. */
  22047. Tooltip.prototype.tooltipFooterHeaderFormatter = function (labelConfig, isFooter) {
  22048. var footOrHead = isFooter ? 'footer' : 'header', series = labelConfig.series, tooltipOptions = series.tooltipOptions, xDateFormat = tooltipOptions.xDateFormat, xAxis = series.xAxis, isDateTime = (xAxis &&
  22049. xAxis.options.type === 'datetime' &&
  22050. isNumber(labelConfig.key)), formatString = tooltipOptions[footOrHead + 'Format'], e = {
  22051. isFooter: isFooter,
  22052. labelConfig: labelConfig
  22053. };
  22054. fireEvent(this, 'headerFormatter', e, function (e) {
  22055. // Guess the best date format based on the closest point distance
  22056. // (#568, #3418)
  22057. if (isDateTime && !xDateFormat) {
  22058. xDateFormat = this.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  22059. }
  22060. // Insert the footer date format if any
  22061. if (isDateTime && xDateFormat) {
  22062. ((labelConfig.point && labelConfig.point.tooltipDateKeys) ||
  22063. ['key']).forEach(function (key) {
  22064. formatString = formatString.replace('{point.' + key + '}', '{point.' + key + ':' + xDateFormat + '}');
  22065. });
  22066. }
  22067. // Replace default header style with class name
  22068. if (series.chart.styledMode) {
  22069. formatString = this.styledModeFormat(formatString);
  22070. }
  22071. e.text = format(formatString, {
  22072. point: labelConfig,
  22073. series: series
  22074. }, this.chart);
  22075. });
  22076. return e.text;
  22077. };
  22078. /**
  22079. * Updates the tooltip with the provided tooltip options.
  22080. *
  22081. * @function Highcharts.Tooltip#update
  22082. *
  22083. * @param {Highcharts.TooltipOptions} options
  22084. * The tooltip options to update.
  22085. */
  22086. Tooltip.prototype.update = function (options) {
  22087. this.destroy();
  22088. // Update user options (#6218)
  22089. merge(true, this.chart.options.tooltip.userOptions, options);
  22090. this.init(this.chart, merge(true, this.options, options));
  22091. };
  22092. /**
  22093. * Find the new position and perform the move
  22094. *
  22095. * @private
  22096. * @function Highcharts.Tooltip#updatePosition
  22097. *
  22098. * @param {Highcharts.Point} point
  22099. */
  22100. Tooltip.prototype.updatePosition = function (point) {
  22101. var chart = this.chart, pointer = chart.pointer, label = this.getLabel(), pos, anchorX = point.plotX + chart.plotLeft, anchorY = point.plotY + chart.plotTop, pad;
  22102. // Needed for outside: true (#11688)
  22103. var chartPosition = pointer.getChartPosition();
  22104. pos = (this.options.positioner || this.getPosition).call(this, label.width, label.height, point);
  22105. // Set the renderer size dynamically to prevent document size to change
  22106. if (this.outside) {
  22107. pad = (this.options.borderWidth || 0) + 2 * this.distance;
  22108. this.renderer.setSize(label.width + pad, label.height + pad, false);
  22109. // Anchor and tooltip container need scaling if chart container has
  22110. // scale transform/css zoom. #11329.
  22111. var containerScaling = chart.containerScaling;
  22112. if (containerScaling) {
  22113. css(this.container, {
  22114. transform: "scale(" + containerScaling.scaleX + ", " + containerScaling.scaleY + ")"
  22115. });
  22116. anchorX *= containerScaling.scaleX;
  22117. anchorY *= containerScaling.scaleY;
  22118. }
  22119. anchorX += chartPosition.left - pos.x;
  22120. anchorY += chartPosition.top - pos.y;
  22121. }
  22122. // do the move
  22123. this.move(Math.round(pos.x), Math.round(pos.y || 0), // can be undefined (#3977)
  22124. anchorX, anchorY);
  22125. };
  22126. return Tooltip;
  22127. }());
  22128. H.Tooltip = Tooltip;
  22129. return H.Tooltip;
  22130. });
  22131. _registerModule(_modules, 'parts/Pointer.js', [_modules['parts/Color.js'], _modules['parts/Globals.js'], _modules['parts/Tooltip.js'], _modules['parts/Utilities.js']], function (Color, H, Tooltip, U) {
  22132. /* *
  22133. *
  22134. * (c) 2010-2020 Torstein Honsi
  22135. *
  22136. * License: www.highcharts.com/license
  22137. *
  22138. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  22139. *
  22140. * */
  22141. var color = Color.parse;
  22142. var charts = H.charts, noop = H.noop;
  22143. var addEvent = U.addEvent, attr = U.attr, css = U.css, defined = U.defined, extend = U.extend, find = U.find, fireEvent = U.fireEvent, isNumber = U.isNumber, isObject = U.isObject, objectEach = U.objectEach, offset = U.offset, pick = U.pick, splat = U.splat;
  22144. /**
  22145. * One position in relation to an axis.
  22146. *
  22147. * @interface Highcharts.PointerAxisCoordinateObject
  22148. */ /**
  22149. * Related axis.
  22150. *
  22151. * @name Highcharts.PointerAxisCoordinateObject#axis
  22152. * @type {Highcharts.Axis}
  22153. */ /**
  22154. * Axis value.
  22155. *
  22156. * @name Highcharts.PointerAxisCoordinateObject#value
  22157. * @type {number}
  22158. */
  22159. /**
  22160. * Positions in terms of axis values.
  22161. *
  22162. * @interface Highcharts.PointerAxisCoordinatesObject
  22163. */ /**
  22164. * Positions on the x-axis.
  22165. * @name Highcharts.PointerAxisCoordinatesObject#xAxis
  22166. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  22167. */ /**
  22168. * Positions on the y-axis.
  22169. * @name Highcharts.PointerAxisCoordinatesObject#yAxis
  22170. * @type {Array<Highcharts.PointerAxisCoordinateObject>}
  22171. */
  22172. /**
  22173. * Pointer coordinates.
  22174. *
  22175. * @interface Highcharts.PointerCoordinatesObject
  22176. */ /**
  22177. * @name Highcharts.PointerCoordinatesObject#chartX
  22178. * @type {number}
  22179. */ /**
  22180. * @name Highcharts.PointerCoordinatesObject#chartY
  22181. * @type {number}
  22182. */
  22183. /**
  22184. * A native browser mouse or touch event, extended with position information
  22185. * relative to the {@link Chart.container}.
  22186. *
  22187. * @interface Highcharts.PointerEventObject
  22188. * @extends global.PointerEvent
  22189. */ /**
  22190. * The X coordinate of the pointer interaction relative to the chart.
  22191. *
  22192. * @name Highcharts.PointerEventObject#chartX
  22193. * @type {number}
  22194. */ /**
  22195. * The Y coordinate of the pointer interaction relative to the chart.
  22196. *
  22197. * @name Highcharts.PointerEventObject#chartY
  22198. * @type {number}
  22199. */
  22200. /**
  22201. * Axis-specific data of a selection.
  22202. *
  22203. * @interface Highcharts.SelectDataObject
  22204. */ /**
  22205. * @name Highcharts.SelectDataObject#axis
  22206. * @type {Highcharts.Axis}
  22207. */ /**
  22208. * @name Highcharts.SelectDataObject#max
  22209. * @type {number}
  22210. */ /**
  22211. * @name Highcharts.SelectDataObject#min
  22212. * @type {number}
  22213. */
  22214. /**
  22215. * Object for select events.
  22216. *
  22217. * @interface Highcharts.SelectEventObject
  22218. */ /**
  22219. * @name Highcharts.SelectEventObject#originalEvent
  22220. * @type {global.Event}
  22221. */ /**
  22222. * @name Highcharts.SelectEventObject#xAxis
  22223. * @type {Array<Highcharts.SelectDataObject>}
  22224. */ /**
  22225. * @name Highcharts.SelectEventObject#yAxis
  22226. * @type {Array<Highcharts.SelectDataObject>}
  22227. */
  22228. ''; // detach doclets above
  22229. /* eslint-disable no-invalid-this, valid-jsdoc */
  22230. /**
  22231. * The mouse and touch tracker object. Each {@link Chart} item has one
  22232. * assosiated Pointer item that can be accessed from the {@link Chart.pointer}
  22233. * property.
  22234. *
  22235. * @class
  22236. * @name Highcharts.Pointer
  22237. *
  22238. * @param {Highcharts.Chart} chart
  22239. * The chart instance.
  22240. *
  22241. * @param {Highcharts.Options} options
  22242. * The root options object. The pointer uses options from the chart and
  22243. * tooltip structures.
  22244. */
  22245. var Pointer = /** @class */ (function () {
  22246. /* *
  22247. *
  22248. * Constructors
  22249. *
  22250. * */
  22251. function Pointer(chart, options) {
  22252. this.lastValidTouch = {};
  22253. this.pinchDown = [];
  22254. this.runChartClick = false;
  22255. this.chart = chart;
  22256. this.hasDragged = false;
  22257. this.options = options;
  22258. this.unbindContainerMouseLeave = function () { };
  22259. this.init(chart, options);
  22260. }
  22261. /* *
  22262. *
  22263. * Functions
  22264. *
  22265. * */
  22266. /**
  22267. * Set inactive state to all series that are not currently hovered,
  22268. * or, if `inactiveOtherPoints` is set to true, set inactive state to
  22269. * all points within that series.
  22270. *
  22271. * @private
  22272. * @function Highcharts.Pointer#applyInactiveState
  22273. * @param {Array<Highcharts.Point>} points
  22274. * Currently hovered points
  22275. */
  22276. Pointer.prototype.applyInactiveState = function (points) {
  22277. var activeSeries = [], series;
  22278. // Get all active series from the hovered points
  22279. (points || []).forEach(function (item) {
  22280. series = item.series;
  22281. // Include itself
  22282. activeSeries.push(series);
  22283. // Include parent series
  22284. if (series.linkedParent) {
  22285. activeSeries.push(series.linkedParent);
  22286. }
  22287. // Include all child series
  22288. if (series.linkedSeries) {
  22289. activeSeries = activeSeries.concat(series.linkedSeries);
  22290. }
  22291. // Include navigator series
  22292. if (series.navigatorSeries) {
  22293. activeSeries.push(series.navigatorSeries);
  22294. }
  22295. });
  22296. // Now loop over all series, filtering out active series
  22297. this.chart.series.forEach(function (inactiveSeries) {
  22298. if (activeSeries.indexOf(inactiveSeries) === -1) {
  22299. // Inactive series
  22300. inactiveSeries.setState('inactive', true);
  22301. }
  22302. else if (inactiveSeries.options.inactiveOtherPoints) {
  22303. // Active series, but other points should be inactivated
  22304. inactiveSeries.setAllPointsToState('inactive');
  22305. }
  22306. });
  22307. };
  22308. /**
  22309. * Destroys the Pointer object and disconnects DOM events.
  22310. *
  22311. * @function Highcharts.Pointer#destroy
  22312. */
  22313. Pointer.prototype.destroy = function () {
  22314. var pointer = this;
  22315. if (typeof pointer.unDocMouseMove !== 'undefined') {
  22316. pointer.unDocMouseMove();
  22317. }
  22318. this.unbindContainerMouseLeave();
  22319. if (!H.chartCount) {
  22320. if (H.unbindDocumentMouseUp) {
  22321. H.unbindDocumentMouseUp = H.unbindDocumentMouseUp();
  22322. }
  22323. if (H.unbindDocumentTouchEnd) {
  22324. H.unbindDocumentTouchEnd = H.unbindDocumentTouchEnd();
  22325. }
  22326. }
  22327. // memory and CPU leak
  22328. clearInterval(pointer.tooltipTimeout);
  22329. objectEach(pointer, function (_val, prop) {
  22330. pointer[prop] = void 0;
  22331. });
  22332. };
  22333. /**
  22334. * Perform a drag operation in response to a mousemove event while the mouse
  22335. * is down.
  22336. *
  22337. * @private
  22338. * @function Highcharts.Pointer#drag
  22339. *
  22340. * @param {Highcharts.PointerEventObject} e
  22341. *
  22342. * @return {void}
  22343. */
  22344. Pointer.prototype.drag = function (e) {
  22345. var chart = this.chart, chartOptions = chart.options.chart, chartX = e.chartX, chartY = e.chartY, zoomHor = this.zoomHor, zoomVert = this.zoomVert, plotLeft = chart.plotLeft, plotTop = chart.plotTop, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, clickedInside, size, selectionMarker = this.selectionMarker, mouseDownX = (this.mouseDownX || 0), mouseDownY = (this.mouseDownY || 0), panningEnabled = isObject(chartOptions.panning) ?
  22346. chartOptions.panning && chartOptions.panning.enabled :
  22347. chartOptions.panning, panKey = (chartOptions.panKey && e[chartOptions.panKey + 'Key']);
  22348. // If the device supports both touch and mouse (like IE11), and we are
  22349. // touch-dragging inside the plot area, don't handle the mouse event.
  22350. // #4339.
  22351. if (selectionMarker && selectionMarker.touch) {
  22352. return;
  22353. }
  22354. // If the mouse is outside the plot area, adjust to cooordinates
  22355. // inside to prevent the selection marker from going outside
  22356. if (chartX < plotLeft) {
  22357. chartX = plotLeft;
  22358. }
  22359. else if (chartX > plotLeft + plotWidth) {
  22360. chartX = plotLeft + plotWidth;
  22361. }
  22362. if (chartY < plotTop) {
  22363. chartY = plotTop;
  22364. }
  22365. else if (chartY > plotTop + plotHeight) {
  22366. chartY = plotTop + plotHeight;
  22367. }
  22368. // determine if the mouse has moved more than 10px
  22369. this.hasDragged = Math.sqrt(Math.pow(mouseDownX - chartX, 2) +
  22370. Math.pow(mouseDownY - chartY, 2));
  22371. if (this.hasDragged > 10) {
  22372. clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop);
  22373. // make a selection
  22374. if (chart.hasCartesianSeries &&
  22375. (this.zoomX || this.zoomY) &&
  22376. clickedInside &&
  22377. !panKey) {
  22378. if (!selectionMarker) {
  22379. this.selectionMarker = selectionMarker =
  22380. chart.renderer.rect(plotLeft, plotTop, zoomHor ? 1 : plotWidth, zoomVert ? 1 : plotHeight, 0)
  22381. .attr({
  22382. 'class': 'highcharts-selection-marker',
  22383. zIndex: 7
  22384. })
  22385. .add();
  22386. if (!chart.styledMode) {
  22387. selectionMarker.attr({
  22388. fill: (chartOptions.selectionMarkerFill ||
  22389. color('#335cad')
  22390. .setOpacity(0.25).get())
  22391. });
  22392. }
  22393. }
  22394. }
  22395. // adjust the width of the selection marker
  22396. if (selectionMarker && zoomHor) {
  22397. size = chartX - mouseDownX;
  22398. selectionMarker.attr({
  22399. width: Math.abs(size),
  22400. x: (size > 0 ? 0 : size) + mouseDownX
  22401. });
  22402. }
  22403. // adjust the height of the selection marker
  22404. if (selectionMarker && zoomVert) {
  22405. size = chartY - mouseDownY;
  22406. selectionMarker.attr({
  22407. height: Math.abs(size),
  22408. y: (size > 0 ? 0 : size) + mouseDownY
  22409. });
  22410. }
  22411. // panning
  22412. if (clickedInside &&
  22413. !selectionMarker &&
  22414. panningEnabled) {
  22415. chart.pan(e, chartOptions.panning);
  22416. }
  22417. }
  22418. };
  22419. /**
  22420. * Start a drag operation.
  22421. *
  22422. * @private
  22423. * @function Highcharts.Pointer#dragStart
  22424. *
  22425. * @param {Highcharts.PointerEventObject} e
  22426. *
  22427. * @return {void}
  22428. */
  22429. Pointer.prototype.dragStart = function (e) {
  22430. var chart = this.chart;
  22431. // Record the start position
  22432. chart.mouseIsDown = e.type;
  22433. chart.cancelClick = false;
  22434. chart.mouseDownX = this.mouseDownX = e.chartX;
  22435. chart.mouseDownY = this.mouseDownY = e.chartY;
  22436. };
  22437. /**
  22438. * On mouse up or touch end across the entire document, drop the selection.
  22439. *
  22440. * @private
  22441. * @function Highcharts.Pointer#drop
  22442. *
  22443. * @param {global.Event} e
  22444. */
  22445. Pointer.prototype.drop = function (e) {
  22446. var pointer = this, chart = this.chart, hasPinched = this.hasPinched;
  22447. if (this.selectionMarker) {
  22448. var selectionData = {
  22449. originalEvent: e,
  22450. xAxis: [],
  22451. yAxis: []
  22452. }, selectionBox = this.selectionMarker, selectionLeft = selectionBox.attr ?
  22453. selectionBox.attr('x') :
  22454. selectionBox.x, selectionTop = selectionBox.attr ?
  22455. selectionBox.attr('y') :
  22456. selectionBox.y, selectionWidth = selectionBox.attr ?
  22457. selectionBox.attr('width') :
  22458. selectionBox.width, selectionHeight = selectionBox.attr ?
  22459. selectionBox.attr('height') :
  22460. selectionBox.height, runZoom;
  22461. // a selection has been made
  22462. if (this.hasDragged || hasPinched) {
  22463. // record each axis' min and max
  22464. chart.axes.forEach(function (axis) {
  22465. if (axis.zoomEnabled &&
  22466. defined(axis.min) &&
  22467. (hasPinched ||
  22468. pointer[{
  22469. xAxis: 'zoomX',
  22470. yAxis: 'zoomY'
  22471. }[axis.coll]]) &&
  22472. isNumber(selectionLeft) &&
  22473. isNumber(selectionTop)) { // #859, #3569
  22474. var horiz = axis.horiz, minPixelPadding = e.type === 'touchend' ?
  22475. axis.minPixelPadding :
  22476. 0, // #1207, #3075
  22477. selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop) +
  22478. minPixelPadding), selectionMax = axis.toValue((horiz ?
  22479. selectionLeft + selectionWidth :
  22480. selectionTop + selectionHeight) - minPixelPadding);
  22481. selectionData[axis.coll].push({
  22482. axis: axis,
  22483. // Min/max for reversed axes
  22484. min: Math.min(selectionMin, selectionMax),
  22485. max: Math.max(selectionMin, selectionMax)
  22486. });
  22487. runZoom = true;
  22488. }
  22489. });
  22490. if (runZoom) {
  22491. fireEvent(chart, 'selection', selectionData, function (args) {
  22492. chart.zoom(extend(args, hasPinched ?
  22493. { animation: false } :
  22494. null));
  22495. });
  22496. }
  22497. }
  22498. if (isNumber(chart.index)) {
  22499. this.selectionMarker = this.selectionMarker.destroy();
  22500. }
  22501. // Reset scaling preview
  22502. if (hasPinched) {
  22503. this.scaleGroups();
  22504. }
  22505. }
  22506. // Reset all. Check isNumber because it may be destroyed on mouse up
  22507. // (#877)
  22508. if (chart && isNumber(chart.index)) {
  22509. css(chart.container, { cursor: chart._cursor });
  22510. chart.cancelClick = this.hasDragged > 10; // #370
  22511. chart.mouseIsDown = this.hasDragged = this.hasPinched = false;
  22512. this.pinchDown = [];
  22513. }
  22514. };
  22515. /**
  22516. * Finds the closest point to a set of coordinates, using the k-d-tree
  22517. * algorithm.
  22518. *
  22519. * @function Highcharts.Pointer#findNearestKDPoint
  22520. *
  22521. * @param {Array<Highcharts.Series>} series
  22522. * All the series to search in.
  22523. *
  22524. * @param {boolean|undefined} shared
  22525. * Whether it is a shared tooltip or not.
  22526. *
  22527. * @param {Highcharts.PointerEventObject} e
  22528. * The pointer event object, containing chart coordinates of the
  22529. * pointer.
  22530. *
  22531. * @return {Highcharts.Point|undefined}
  22532. * The point closest to given coordinates.
  22533. */
  22534. Pointer.prototype.findNearestKDPoint = function (series, shared, e) {
  22535. var chart = this.chart;
  22536. var hoverPoint = chart.hoverPoint;
  22537. var tooltip = chart.tooltip;
  22538. if (hoverPoint &&
  22539. tooltip &&
  22540. tooltip.isStickyOnContact()) {
  22541. return hoverPoint;
  22542. }
  22543. var closest;
  22544. /** @private */
  22545. function sort(p1, p2) {
  22546. var isCloserX = p1.distX - p2.distX, isCloser = p1.dist - p2.dist, isAbove = (p2.series.group && p2.series.group.zIndex) -
  22547. (p1.series.group && p1.series.group.zIndex), result;
  22548. // We have two points which are not in the same place on xAxis
  22549. // and shared tooltip:
  22550. if (isCloserX !== 0 && shared) { // #5721
  22551. result = isCloserX;
  22552. // Points are not exactly in the same place on x/yAxis:
  22553. }
  22554. else if (isCloser !== 0) {
  22555. result = isCloser;
  22556. // The same xAxis and yAxis position, sort by z-index:
  22557. }
  22558. else if (isAbove !== 0) {
  22559. result = isAbove;
  22560. // The same zIndex, sort by array index:
  22561. }
  22562. else {
  22563. result =
  22564. p1.series.index > p2.series.index ?
  22565. -1 :
  22566. 1;
  22567. }
  22568. return result;
  22569. }
  22570. series.forEach(function (s) {
  22571. var noSharedTooltip = s.noSharedTooltip && shared, compareX = (!noSharedTooltip &&
  22572. s.options.findNearestPointBy.indexOf('y') < 0), point = s.searchPoint(e, compareX);
  22573. if ( // Check that we actually found a point on the series.
  22574. isObject(point, true) &&
  22575. // Use the new point if it is closer.
  22576. (!isObject(closest, true) ||
  22577. (sort(closest, point) > 0))) {
  22578. closest = point;
  22579. }
  22580. });
  22581. return closest;
  22582. };
  22583. /**
  22584. * @private
  22585. * @function Highcharts.Pointer#getChartCoordinatesFromPoint
  22586. * @param {Highcharts.Point} point
  22587. * @param {boolean} [inverted]
  22588. * @return {Highcharts.PointerCoordinatesObject|undefined}
  22589. */
  22590. Pointer.prototype.getChartCoordinatesFromPoint = function (point, inverted) {
  22591. var series = point.series, xAxis = series.xAxis, yAxis = series.yAxis, plotX = pick(point.clientX, point.plotX), shapeArgs = point.shapeArgs;
  22592. if (xAxis && yAxis) {
  22593. return inverted ? {
  22594. chartX: xAxis.len + xAxis.pos - plotX,
  22595. chartY: yAxis.len + yAxis.pos - point.plotY
  22596. } : {
  22597. chartX: plotX + xAxis.pos,
  22598. chartY: point.plotY + yAxis.pos
  22599. };
  22600. }
  22601. if (shapeArgs && shapeArgs.x && shapeArgs.y) {
  22602. // E.g. pies do not have axes
  22603. return {
  22604. chartX: shapeArgs.x,
  22605. chartY: shapeArgs.y
  22606. };
  22607. }
  22608. };
  22609. /**
  22610. * Return the cached chartPosition if it is available on the Pointer,
  22611. * otherwise find it. Running offset is quite expensive, so it should be
  22612. * avoided when we know the chart hasn't moved.
  22613. *
  22614. * @function Highcharts.Pointer#getChartPosition
  22615. *
  22616. * @return {Highcharts.OffsetObject}
  22617. * The offset of the chart container within the page
  22618. */
  22619. Pointer.prototype.getChartPosition = function () {
  22620. return (this.chartPosition ||
  22621. (this.chartPosition = offset(this.chart.container)));
  22622. };
  22623. /**
  22624. * Get the click position in terms of axis values.
  22625. *
  22626. * @function Highcharts.Pointer#getCoordinates
  22627. *
  22628. * @param {Highcharts.PointerEventObject} e
  22629. * Pointer event, extended with `chartX` and `chartY` properties.
  22630. *
  22631. * @return {Highcharts.PointerAxisCoordinatesObject}
  22632. */
  22633. Pointer.prototype.getCoordinates = function (e) {
  22634. var coordinates = {
  22635. xAxis: [],
  22636. yAxis: []
  22637. };
  22638. this.chart.axes.forEach(function (axis) {
  22639. coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({
  22640. axis: axis,
  22641. value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
  22642. });
  22643. });
  22644. return coordinates;
  22645. };
  22646. /**
  22647. * Calculates what is the current hovered point/points and series.
  22648. *
  22649. * @private
  22650. * @function Highcharts.Pointer#getHoverData
  22651. *
  22652. * @param {Highcharts.Point|undefined} existingHoverPoint
  22653. * The point currrently beeing hovered.
  22654. *
  22655. * @param {Highcharts.Series|undefined} existingHoverSeries
  22656. * The series currently beeing hovered.
  22657. *
  22658. * @param {Array<Highcharts.Series>} series
  22659. * All the series in the chart.
  22660. *
  22661. * @param {boolean} isDirectTouch
  22662. * Is the pointer directly hovering the point.
  22663. *
  22664. * @param {boolean|undefined} shared
  22665. * Whether it is a shared tooltip or not.
  22666. *
  22667. * @param {Highcharts.PointerEventObject} [e]
  22668. * The triggering event, containing chart coordinates of the pointer.
  22669. *
  22670. * @return {object}
  22671. * Object containing resulting hover data: hoverPoint, hoverSeries,
  22672. * and hoverPoints.
  22673. */
  22674. Pointer.prototype.getHoverData = function (existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
  22675. var hoverPoint, hoverPoints = [], hoverSeries = existingHoverSeries, useExisting = !!(isDirectTouch && existingHoverPoint), notSticky = hoverSeries && !hoverSeries.stickyTracking,
  22676. // Which series to look in for the hover point
  22677. searchSeries,
  22678. // Parameters needed for beforeGetHoverData event.
  22679. eventArgs = {
  22680. chartX: e ? e.chartX : void 0,
  22681. chartY: e ? e.chartY : void 0,
  22682. shared: shared
  22683. }, filter = function (s) {
  22684. return (s.visible &&
  22685. !(!shared && s.directTouch) && // #3821
  22686. pick(s.options.enableMouseTracking, true));
  22687. };
  22688. // Find chart.hoverPane and update filter method in polar.
  22689. fireEvent(this, 'beforeGetHoverData', eventArgs);
  22690. searchSeries = notSticky ?
  22691. // Only search on hovered series if it has stickyTracking false
  22692. [hoverSeries] :
  22693. // Filter what series to look in.
  22694. series.filter(function (s) {
  22695. return eventArgs.filter ? eventArgs.filter(s) : filter(s) &&
  22696. s.stickyTracking;
  22697. });
  22698. // Use existing hovered point or find the one closest to coordinates.
  22699. hoverPoint = useExisting || !e ?
  22700. existingHoverPoint :
  22701. this.findNearestKDPoint(searchSeries, shared, e);
  22702. // Assign hover series
  22703. hoverSeries = hoverPoint && hoverPoint.series;
  22704. // If we have a hoverPoint, assign hoverPoints.
  22705. if (hoverPoint) {
  22706. // When tooltip is shared, it displays more than one point
  22707. if (shared && !hoverSeries.noSharedTooltip) {
  22708. searchSeries = series.filter(function (s) {
  22709. return eventArgs.filter ?
  22710. eventArgs.filter(s) : filter(s) && !s.noSharedTooltip;
  22711. });
  22712. // Get all points with the same x value as the hoverPoint
  22713. searchSeries.forEach(function (s) {
  22714. var point = find(s.points, function (p) {
  22715. return p.x === hoverPoint.x && !p.isNull;
  22716. });
  22717. if (isObject(point)) {
  22718. /*
  22719. * Boost returns a minimal point. Convert it to a usable
  22720. * point for tooltip and states.
  22721. */
  22722. if (s.chart.isBoosting) {
  22723. point = s.getPoint(point);
  22724. }
  22725. hoverPoints.push(point);
  22726. }
  22727. });
  22728. }
  22729. else {
  22730. hoverPoints.push(hoverPoint);
  22731. }
  22732. }
  22733. // Check whether the hoverPoint is inside pane we are hovering over.
  22734. eventArgs = { hoverPoint: hoverPoint };
  22735. fireEvent(this, 'afterGetHoverData', eventArgs);
  22736. return {
  22737. hoverPoint: eventArgs.hoverPoint,
  22738. hoverSeries: hoverSeries,
  22739. hoverPoints: hoverPoints
  22740. };
  22741. };
  22742. /**
  22743. * @private
  22744. * @function Highcharts.Pointer#getPointFromEvent
  22745. *
  22746. * @param {global.Event} e
  22747. *
  22748. * @return {Highcharts.Point|undefined}
  22749. */
  22750. Pointer.prototype.getPointFromEvent = function (e) {
  22751. var target = e.target, point;
  22752. while (target && !point) {
  22753. point = target.point;
  22754. target = target.parentNode;
  22755. }
  22756. return point;
  22757. };
  22758. /**
  22759. * @private
  22760. * @function Highcharts.Pointer#onTrackerMouseOut
  22761. *
  22762. * @param {Highcharts.PointerEventObject} e
  22763. *
  22764. * @return {void}
  22765. */
  22766. Pointer.prototype.onTrackerMouseOut = function (e) {
  22767. var chart = this.chart;
  22768. var relatedTarget = e.relatedTarget || e.toElement;
  22769. var series = chart.hoverSeries;
  22770. this.isDirectTouch = false;
  22771. if (series &&
  22772. relatedTarget &&
  22773. !series.stickyTracking &&
  22774. !this.inClass(relatedTarget, 'highcharts-tooltip') &&
  22775. (!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465, #5553
  22776. !this.inClass(relatedTarget, 'highcharts-tracker'))) {
  22777. series.onMouseOut();
  22778. }
  22779. };
  22780. /**
  22781. * Utility to detect whether an element has, or has a parent with, a
  22782. * specificclass name. Used on detection of tracker objects and on deciding
  22783. * whether hovering the tooltip should cause the active series to mouse out.
  22784. *
  22785. * @function Highcharts.Pointer#inClass
  22786. *
  22787. * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
  22788. * The element to investigate.
  22789. *
  22790. * @param {string} className
  22791. * The class name to look for.
  22792. *
  22793. * @return {boolean|undefined}
  22794. * True if either the element or one of its parents has the given
  22795. * class name.
  22796. */
  22797. Pointer.prototype.inClass = function (element, className) {
  22798. var elemClassName;
  22799. while (element) {
  22800. elemClassName = attr(element, 'class');
  22801. if (elemClassName) {
  22802. if (elemClassName.indexOf(className) !== -1) {
  22803. return true;
  22804. }
  22805. if (elemClassName.indexOf('highcharts-container') !== -1) {
  22806. return false;
  22807. }
  22808. }
  22809. element = element.parentNode;
  22810. }
  22811. };
  22812. /**
  22813. * Initialize the Pointer.
  22814. *
  22815. * @private
  22816. * @function Highcharts.Pointer#init
  22817. *
  22818. * @param {Highcharts.Chart} chart
  22819. * The Chart instance.
  22820. *
  22821. * @param {Highcharts.Options} options
  22822. * The root options object. The pointer uses options from the chart
  22823. * and tooltip structures.
  22824. *
  22825. * @return {void}
  22826. */
  22827. Pointer.prototype.init = function (chart, options) {
  22828. // Store references
  22829. this.options = options;
  22830. this.chart = chart;
  22831. // Do we need to handle click on a touch device?
  22832. this.runChartClick =
  22833. options.chart.events &&
  22834. !!options.chart.events.click;
  22835. this.pinchDown = [];
  22836. this.lastValidTouch = {};
  22837. if (Tooltip) {
  22838. /**
  22839. * Tooltip object for points of series.
  22840. *
  22841. * @name Highcharts.Chart#tooltip
  22842. * @type {Highcharts.Tooltip}
  22843. */
  22844. chart.tooltip = new Tooltip(chart, options.tooltip);
  22845. this.followTouchMove = pick(options.tooltip.followTouchMove, true);
  22846. }
  22847. this.setDOMEvents();
  22848. };
  22849. /**
  22850. * Takes a browser event object and extends it with custom Highcharts
  22851. * properties `chartX` and `chartY` in order to work on the internal
  22852. * coordinate system.
  22853. *
  22854. * @function Highcharts.Pointer#normalize
  22855. *
  22856. * @param {global.MouseEvent|global.PointerEvent|global.TouchEvent} e
  22857. * Event object in standard browsers.
  22858. *
  22859. * @param {Highcharts.OffsetObject} [chartPosition]
  22860. * Additional chart offset.
  22861. *
  22862. * @return {Highcharts.PointerEventObject}
  22863. * A browser event with extended properties `chartX` and `chartY`.
  22864. */
  22865. Pointer.prototype.normalize = function (e, chartPosition) {
  22866. var touches = e.touches;
  22867. // iOS (#2757)
  22868. var ePos = (touches ?
  22869. touches.length ?
  22870. touches.item(0) :
  22871. (pick(// #13534
  22872. touches.changedTouches, e.changedTouches))[0] :
  22873. e);
  22874. // Get mouse position
  22875. if (!chartPosition) {
  22876. chartPosition = this.getChartPosition();
  22877. }
  22878. var chartX = ePos.pageX - chartPosition.left, chartY = ePos.pageY - chartPosition.top;
  22879. // #11329 - when there is scaling on a parent element, we need to take
  22880. // this into account
  22881. var containerScaling = this.chart.containerScaling;
  22882. if (containerScaling) {
  22883. chartX /= containerScaling.scaleX;
  22884. chartY /= containerScaling.scaleY;
  22885. }
  22886. return extend(e, {
  22887. chartX: Math.round(chartX),
  22888. chartY: Math.round(chartY)
  22889. });
  22890. };
  22891. /**
  22892. * @private
  22893. * @function Highcharts.Pointer#onContainerClick
  22894. */
  22895. Pointer.prototype.onContainerClick = function (e) {
  22896. var chart = this.chart;
  22897. var hoverPoint = chart.hoverPoint;
  22898. var pEvt = this.normalize(e);
  22899. var plotLeft = chart.plotLeft;
  22900. var plotTop = chart.plotTop;
  22901. if (!chart.cancelClick) {
  22902. // On tracker click, fire the series and point events. #783, #1583
  22903. if (hoverPoint &&
  22904. this.inClass(pEvt.target, 'highcharts-tracker')) {
  22905. // the series click event
  22906. fireEvent(hoverPoint.series, 'click', extend(pEvt, {
  22907. point: hoverPoint
  22908. }));
  22909. // the point click event
  22910. if (chart.hoverPoint) { // it may be destroyed (#1844)
  22911. hoverPoint.firePointEvent('click', pEvt);
  22912. }
  22913. // When clicking outside a tracker, fire a chart event
  22914. }
  22915. else {
  22916. extend(pEvt, this.getCoordinates(pEvt));
  22917. // fire a click event in the chart
  22918. if (chart.isInsidePlot((pEvt.chartX - plotLeft), (pEvt.chartY - plotTop))) {
  22919. fireEvent(chart, 'click', pEvt);
  22920. }
  22921. }
  22922. }
  22923. };
  22924. /**
  22925. * @private
  22926. * @function Highcharts.Pointer#onContainerMouseDown
  22927. *
  22928. * @param {global.MouseEvent} e
  22929. */
  22930. Pointer.prototype.onContainerMouseDown = function (e) {
  22931. // Normalize before the 'if' for the legacy IE (#7850)
  22932. e = this.normalize(e);
  22933. // #11635, Firefox does not reliable fire move event after click scroll
  22934. if (H.isFirefox &&
  22935. e.button !== 0) {
  22936. this.onContainerMouseMove(e);
  22937. }
  22938. // #11635, limiting to primary button (incl. IE 8 support)
  22939. if (typeof e.button === 'undefined' ||
  22940. ((e.buttons || e.button) & 1) === 1) {
  22941. this.zoomOption(e);
  22942. this.dragStart(e);
  22943. }
  22944. };
  22945. /**
  22946. * When mouse leaves the container, hide the tooltip.
  22947. *
  22948. * @private
  22949. * @function Highcharts.Pointer#onContainerMouseLeave
  22950. *
  22951. * @param {global.MouseEvent} e
  22952. *
  22953. * @return {void}
  22954. */
  22955. Pointer.prototype.onContainerMouseLeave = function (e) {
  22956. var chart = charts[pick(H.hoverChartIndex, -1)];
  22957. var tooltip = this.chart.tooltip;
  22958. e = this.normalize(e);
  22959. // #4886, MS Touch end fires mouseleave but with no related target
  22960. if (chart &&
  22961. (e.relatedTarget || e.toElement)) {
  22962. chart.pointer.reset();
  22963. // Also reset the chart position, used in #149 fix
  22964. chart.pointer.chartPosition = void 0;
  22965. }
  22966. if ( // #11635, Firefox wheel scroll does not fire out events consistently
  22967. tooltip &&
  22968. !tooltip.isHidden) {
  22969. this.reset();
  22970. }
  22971. };
  22972. /**
  22973. * The mousemove, touchmove and touchstart event handler
  22974. *
  22975. * @private
  22976. * @function Highcharts.Pointer#onContainerMouseMove
  22977. *
  22978. * @param {global.MouseEvent} e
  22979. *
  22980. * @return {void}
  22981. */
  22982. Pointer.prototype.onContainerMouseMove = function (e) {
  22983. var chart = this.chart;
  22984. var pEvt = this.normalize(e);
  22985. this.setHoverChartIndex();
  22986. // In IE8 we apparently need this returnValue set to false in order to
  22987. // avoid text being selected. But in Chrome, e.returnValue is prevented,
  22988. // plus we don't need to run e.preventDefault to prevent selected text
  22989. // in modern browsers. So we set it conditionally. Remove it when IE8 is
  22990. // no longer needed. #2251, #3224.
  22991. if (!pEvt.preventDefault) {
  22992. pEvt.returnValue = false;
  22993. }
  22994. if (chart.mouseIsDown === 'mousedown') {
  22995. this.drag(pEvt);
  22996. }
  22997. // Show the tooltip and run mouse over events (#977)
  22998. if (!chart.openMenu &&
  22999. (this.inClass(pEvt.target, 'highcharts-tracker') ||
  23000. chart.isInsidePlot((pEvt.chartX - chart.plotLeft), (pEvt.chartY - chart.plotTop)))) {
  23001. this.runPointActions(pEvt);
  23002. }
  23003. };
  23004. /**
  23005. * @private
  23006. * @function Highcharts.Pointer#onDocumentTouchEnd
  23007. *
  23008. * @param {Highcharts.PointerEventObject} e
  23009. *
  23010. * @return {void}
  23011. */
  23012. Pointer.prototype.onDocumentTouchEnd = function (e) {
  23013. if (charts[H.hoverChartIndex]) {
  23014. charts[H.hoverChartIndex].pointer.drop(e);
  23015. }
  23016. };
  23017. /**
  23018. * @private
  23019. * @function Highcharts.Pointer#onContainerTouchMove
  23020. *
  23021. * @param {Highcharts.PointerEventObject} e
  23022. *
  23023. * @return {void}
  23024. */
  23025. Pointer.prototype.onContainerTouchMove = function (e) {
  23026. this.touch(e);
  23027. };
  23028. /**
  23029. * @private
  23030. * @function Highcharts.Pointer#onContainerTouchStart
  23031. *
  23032. * @param {Highcharts.PointerEventObject} e
  23033. *
  23034. * @return {void}
  23035. */
  23036. Pointer.prototype.onContainerTouchStart = function (e) {
  23037. this.zoomOption(e);
  23038. this.touch(e, true);
  23039. };
  23040. /**
  23041. * Special handler for mouse move that will hide the tooltip when the mouse
  23042. * leaves the plotarea. Issue #149 workaround. The mouseleave event does not
  23043. * always fire.
  23044. *
  23045. * @private
  23046. * @function Highcharts.Pointer#onDocumentMouseMove
  23047. *
  23048. * @param {global.MouseEvent} e
  23049. *
  23050. * @return {void}
  23051. */
  23052. Pointer.prototype.onDocumentMouseMove = function (e) {
  23053. var chart = this.chart;
  23054. var chartPosition = this.chartPosition;
  23055. var pEvt = this.normalize(e, chartPosition);
  23056. var tooltip = chart.tooltip;
  23057. // If we're outside, hide the tooltip
  23058. if (chartPosition &&
  23059. (!tooltip ||
  23060. !tooltip.isStickyOnContact()) &&
  23061. !chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop) &&
  23062. !this.inClass(pEvt.target, 'highcharts-tracker')) {
  23063. this.reset();
  23064. }
  23065. };
  23066. /**
  23067. * @private
  23068. * @function Highcharts.Pointer#onDocumentMouseUp
  23069. *
  23070. * @param {global.MouseEvent} e
  23071. *
  23072. * @return {void}
  23073. */
  23074. Pointer.prototype.onDocumentMouseUp = function (e) {
  23075. var chart = charts[pick(H.hoverChartIndex, -1)];
  23076. if (chart) {
  23077. chart.pointer.drop(e);
  23078. }
  23079. };
  23080. /**
  23081. * Handle touch events with two touches
  23082. *
  23083. * @private
  23084. * @function Highcharts.Pointer#pinch
  23085. *
  23086. * @param {Highcharts.PointerEventObject} e
  23087. *
  23088. * @return {void}
  23089. */
  23090. Pointer.prototype.pinch = function (e) {
  23091. var self = this, chart = self.chart, pinchDown = self.pinchDown, touches = (e.touches || []), touchesLength = touches.length, lastValidTouch = self.lastValidTouch, hasZoom = self.hasZoom, selectionMarker = self.selectionMarker, transform = {}, fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, 'highcharts-tracker') &&
  23092. chart.runTrackerClick) ||
  23093. self.runChartClick), clip = {};
  23094. // Don't initiate panning until the user has pinched. This prevents us
  23095. // from blocking page scrolling as users scroll down a long page
  23096. // (#4210).
  23097. if (touchesLength > 1) {
  23098. self.initiated = true;
  23099. }
  23100. // On touch devices, only proceed to trigger click if a handler is
  23101. // defined
  23102. if (hasZoom && self.initiated && !fireClickEvent) {
  23103. e.preventDefault();
  23104. }
  23105. // Normalize each touch
  23106. [].map.call(touches, function (e) {
  23107. return self.normalize(e);
  23108. });
  23109. // Register the touch start position
  23110. if (e.type === 'touchstart') {
  23111. [].forEach.call(touches, function (e, i) {
  23112. pinchDown[i] = { chartX: e.chartX, chartY: e.chartY };
  23113. });
  23114. lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] &&
  23115. pinchDown[1].chartX];
  23116. lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] &&
  23117. pinchDown[1].chartY];
  23118. // Identify the data bounds in pixels
  23119. chart.axes.forEach(function (axis) {
  23120. if (axis.zoomEnabled) {
  23121. var 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);
  23122. // Store the bounds for use in the touchmove handler
  23123. bounds.min = Math.min(axis.pos, absMin - minPixelPadding);
  23124. bounds.max = Math.max(axis.pos + axis.len, absMax + minPixelPadding);
  23125. }
  23126. });
  23127. self.res = true; // reset on next move
  23128. // Optionally move the tooltip on touchmove
  23129. }
  23130. else if (self.followTouchMove && touchesLength === 1) {
  23131. this.runPointActions(self.normalize(e));
  23132. // Event type is touchmove, handle panning and pinching
  23133. }
  23134. else if (pinchDown.length) { // can be 0 when releasing, if touchend
  23135. // fires first
  23136. // Set the marker
  23137. if (!selectionMarker) {
  23138. self.selectionMarker = selectionMarker = extend({
  23139. destroy: noop,
  23140. touch: true
  23141. }, chart.plotBox);
  23142. }
  23143. self.pinchTranslate(pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  23144. self.hasPinched = hasZoom;
  23145. // Scale and translate the groups to provide visual feedback during
  23146. // pinching
  23147. self.scaleGroups(transform, clip);
  23148. if (self.res) {
  23149. self.res = false;
  23150. this.reset(false, 0);
  23151. }
  23152. }
  23153. };
  23154. /**
  23155. * Run translation operations
  23156. *
  23157. * @private
  23158. * @function Highcharts.Pointer#pinchTranslate
  23159. *
  23160. * @param {Array<*>} pinchDown
  23161. *
  23162. * @param {Array<Highcharts.PointerEventObject>} touches
  23163. *
  23164. * @param {*} transform
  23165. *
  23166. * @param {*} selectionMarker
  23167. *
  23168. * @param {*} clip
  23169. *
  23170. * @param {*} lastValidTouch
  23171. *
  23172. * @return {void}
  23173. */
  23174. Pointer.prototype.pinchTranslate = function (pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) {
  23175. if (this.zoomHor) {
  23176. this.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  23177. }
  23178. if (this.zoomVert) {
  23179. this.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch);
  23180. }
  23181. };
  23182. /**
  23183. * Run translation operations for each direction (horizontal and vertical)
  23184. * independently.
  23185. *
  23186. * @private
  23187. * @function Highcharts.Pointer#pinchTranslateDirection
  23188. *
  23189. * @param {boolean} horiz
  23190. *
  23191. * @param {Array<*>} pinchDown
  23192. *
  23193. * @param {Array<Highcharts.PointerEventObject>} touches
  23194. *
  23195. * @param {*} transform
  23196. *
  23197. * @param {*} selectionMarker
  23198. *
  23199. * @param {*} clip
  23200. *
  23201. * @param {*} lastValidTouch
  23202. *
  23203. * @param {number|undefined} [forcedScale=1]
  23204. *
  23205. * @return {void}
  23206. */
  23207. Pointer.prototype.pinchTranslateDirection = function (horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch, forcedScale) {
  23208. var chart = this.chart, xy = horiz ? 'x' : 'y', XY = horiz ? 'X' : 'Y', sChartXY = ('chart' + XY), wh = horiz ? 'width' : 'height', plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')], selectionWH, selectionXY, clipXY, scale = forcedScale || 1, inverted = chart.inverted, bounds = chart.bounds[horiz ? 'h' : 'v'], singleTouch = pinchDown.length === 1, touch0Start = pinchDown[0][sChartXY], touch0Now = touches[0][sChartXY], touch1Start = !singleTouch && pinchDown[1][sChartXY], touch1Now = !singleTouch && touches[1][sChartXY], outOfBounds, transformScale, scaleKey, setScale = function () {
  23209. // Don't zoom if fingers are too close on this axis
  23210. if (typeof touch1Now === 'number' &&
  23211. Math.abs(touch0Start - touch1Start) > 20) {
  23212. scale = forcedScale ||
  23213. Math.abs(touch0Now - touch1Now) /
  23214. Math.abs(touch0Start - touch1Start);
  23215. }
  23216. clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start;
  23217. selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale;
  23218. };
  23219. // Set the scale, first pass
  23220. setScale();
  23221. // The clip position (x or y) is altered if out of bounds, the selection
  23222. // position is not
  23223. selectionXY = clipXY;
  23224. // Out of bounds
  23225. if (selectionXY < bounds.min) {
  23226. selectionXY = bounds.min;
  23227. outOfBounds = true;
  23228. }
  23229. else if (selectionXY + selectionWH > bounds.max) {
  23230. selectionXY = bounds.max - selectionWH;
  23231. outOfBounds = true;
  23232. }
  23233. // Is the chart dragged off its bounds, determined by dataMin and
  23234. // dataMax?
  23235. if (outOfBounds) {
  23236. // Modify the touchNow position in order to create an elastic drag
  23237. // movement. This indicates to the user that the chart is responsive
  23238. // but can't be dragged further.
  23239. touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]);
  23240. if (typeof touch1Now === 'number') {
  23241. touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]);
  23242. }
  23243. // Set the scale, second pass to adapt to the modified touchNow
  23244. // positions
  23245. setScale();
  23246. }
  23247. else {
  23248. lastValidTouch[xy] = [touch0Now, touch1Now];
  23249. }
  23250. // Set geometry for clipping, selection and transformation
  23251. if (!inverted) {
  23252. clip[xy] = clipXY - plotLeftTop;
  23253. clip[wh] = selectionWH;
  23254. }
  23255. scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY;
  23256. transformScale = inverted ? 1 / scale : scale;
  23257. selectionMarker[wh] = selectionWH;
  23258. selectionMarker[xy] = selectionXY;
  23259. transform[scaleKey] = scale;
  23260. transform['translate' + XY] = (transformScale * plotLeftTop) +
  23261. (touch0Now - (transformScale * touch0Start));
  23262. };
  23263. /**
  23264. * Reset the tracking by hiding the tooltip, the hover series state and the
  23265. * hover point
  23266. *
  23267. * @function Highcharts.Pointer#reset
  23268. *
  23269. * @param {boolean} [allowMove]
  23270. * Instead of destroying the tooltip altogether, allow moving it if
  23271. * possible.
  23272. *
  23273. * @param {number} [delay]
  23274. *
  23275. * @return {void}
  23276. */
  23277. Pointer.prototype.reset = function (allowMove, delay) {
  23278. var pointer = this, chart = pointer.chart, hoverSeries = chart.hoverSeries, hoverPoint = chart.hoverPoint, hoverPoints = chart.hoverPoints, tooltip = chart.tooltip, tooltipPoints = tooltip && tooltip.shared ?
  23279. hoverPoints :
  23280. hoverPoint;
  23281. // Check if the points have moved outside the plot area (#1003, #4736,
  23282. // #5101)
  23283. if (allowMove && tooltipPoints) {
  23284. splat(tooltipPoints).forEach(function (point) {
  23285. if (point.series.isCartesian &&
  23286. typeof point.plotX === 'undefined') {
  23287. allowMove = false;
  23288. }
  23289. });
  23290. }
  23291. // Just move the tooltip, #349
  23292. if (allowMove) {
  23293. if (tooltip && tooltipPoints && splat(tooltipPoints).length) {
  23294. tooltip.refresh(tooltipPoints);
  23295. if (tooltip.shared && hoverPoints) { // #8284
  23296. hoverPoints.forEach(function (point) {
  23297. point.setState(point.state, true);
  23298. if (point.series.isCartesian) {
  23299. if (point.series.xAxis.crosshair) {
  23300. point.series.xAxis
  23301. .drawCrosshair(null, point);
  23302. }
  23303. if (point.series.yAxis.crosshair) {
  23304. point.series.yAxis
  23305. .drawCrosshair(null, point);
  23306. }
  23307. }
  23308. });
  23309. }
  23310. else if (hoverPoint) { // #2500
  23311. hoverPoint.setState(hoverPoint.state, true);
  23312. chart.axes.forEach(function (axis) {
  23313. if (axis.crosshair &&
  23314. hoverPoint.series[axis.coll] === axis) {
  23315. axis.drawCrosshair(null, hoverPoint);
  23316. }
  23317. });
  23318. }
  23319. }
  23320. // Full reset
  23321. }
  23322. else {
  23323. if (hoverPoint) {
  23324. hoverPoint.onMouseOut();
  23325. }
  23326. if (hoverPoints) {
  23327. hoverPoints.forEach(function (point) {
  23328. point.setState();
  23329. });
  23330. }
  23331. if (hoverSeries) {
  23332. hoverSeries.onMouseOut();
  23333. }
  23334. if (tooltip) {
  23335. tooltip.hide(delay);
  23336. }
  23337. if (pointer.unDocMouseMove) {
  23338. pointer.unDocMouseMove = pointer.unDocMouseMove();
  23339. }
  23340. // Remove crosshairs
  23341. chart.axes.forEach(function (axis) {
  23342. axis.hideCrosshair();
  23343. });
  23344. pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
  23345. }
  23346. };
  23347. /**
  23348. * With line type charts with a single tracker, get the point closest to the
  23349. * mouse. Run Point.onMouseOver and display tooltip for the point or points.
  23350. *
  23351. * @private
  23352. * @function Highcharts.Pointer#runPointActions
  23353. *
  23354. * @param {global.Event} e
  23355. *
  23356. * @param {Highcharts.PointerEventObject} [p]
  23357. *
  23358. * @return {void}
  23359. *
  23360. * @fires Highcharts.Point#event:mouseOut
  23361. * @fires Highcharts.Point#event:mouseOver
  23362. */
  23363. Pointer.prototype.runPointActions = function (e, p) {
  23364. var pointer = this, chart = pointer.chart, series = chart.series, tooltip = (chart.tooltip && chart.tooltip.options.enabled ?
  23365. chart.tooltip :
  23366. void 0), shared = (tooltip ?
  23367. tooltip.shared :
  23368. false), hoverPoint = p || chart.hoverPoint, hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries,
  23369. // onMouseOver or already hovering a series with directTouch
  23370. isDirectTouch = (!e || e.type !== 'touchmove') && (!!p || ((hoverSeries && hoverSeries.directTouch) &&
  23371. pointer.isDirectTouch)), hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e), useSharedTooltip, followPointer, anchor, points;
  23372. // Update variables from hoverData.
  23373. hoverPoint = hoverData.hoverPoint;
  23374. points = hoverData.hoverPoints;
  23375. hoverSeries = hoverData.hoverSeries;
  23376. followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
  23377. useSharedTooltip = (shared &&
  23378. hoverSeries &&
  23379. !hoverSeries.noSharedTooltip);
  23380. // Refresh tooltip for kdpoint if new hover point or tooltip was hidden
  23381. // #3926, #4200
  23382. if (hoverPoint &&
  23383. // !(hoverSeries && hoverSeries.directTouch) &&
  23384. (hoverPoint !== chart.hoverPoint || (tooltip && tooltip.isHidden))) {
  23385. (chart.hoverPoints || []).forEach(function (p) {
  23386. if (points.indexOf(p) === -1) {
  23387. p.setState();
  23388. }
  23389. });
  23390. // Set normal state to previous series
  23391. if (chart.hoverSeries !== hoverSeries) {
  23392. hoverSeries.onMouseOver();
  23393. }
  23394. pointer.applyInactiveState(points);
  23395. // Do mouseover on all points (#3919, #3985, #4410, #5622)
  23396. (points || []).forEach(function (p) {
  23397. p.setState('hover');
  23398. });
  23399. // If tracking is on series in stead of on each point,
  23400. // fire mouseOver on hover point. // #4448
  23401. if (chart.hoverPoint) {
  23402. chart.hoverPoint.firePointEvent('mouseOut');
  23403. }
  23404. // Hover point may have been destroyed in the event handlers (#7127)
  23405. if (!hoverPoint.series) {
  23406. return;
  23407. }
  23408. /**
  23409. * Contains all hovered points.
  23410. *
  23411. * @name Highcharts.Chart#hoverPoints
  23412. * @type {Array<Highcharts.Point>|null}
  23413. */
  23414. chart.hoverPoints = points;
  23415. /**
  23416. * Contains the original hovered point.
  23417. *
  23418. * @name Highcharts.Chart#hoverPoint
  23419. * @type {Highcharts.Point|null}
  23420. */
  23421. chart.hoverPoint = hoverPoint;
  23422. /**
  23423. * Hover state should not be lost when axis is updated (#12569)
  23424. * Axis.update runs pointer.reset which uses chart.hoverPoint.state
  23425. * to apply state which does not exist in hoverPoint yet.
  23426. * The mouseOver event should be triggered when hoverPoint
  23427. * is correct.
  23428. */
  23429. hoverPoint.firePointEvent('mouseOver');
  23430. // Draw tooltip if necessary
  23431. if (tooltip) {
  23432. tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
  23433. }
  23434. // Update positions (regardless of kdpoint or hoverPoint)
  23435. }
  23436. else if (followPointer && tooltip && !tooltip.isHidden) {
  23437. anchor = tooltip.getAnchor([{}], e);
  23438. tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] });
  23439. }
  23440. // Start the event listener to pick up the tooltip and crosshairs
  23441. if (!pointer.unDocMouseMove) {
  23442. pointer.unDocMouseMove = addEvent(chart.container.ownerDocument, 'mousemove', function (e) {
  23443. var chart = charts[H.hoverChartIndex];
  23444. if (chart) {
  23445. chart.pointer.onDocumentMouseMove(e);
  23446. }
  23447. });
  23448. }
  23449. // Issues related to crosshair #4927, #5269 #5066, #5658
  23450. chart.axes.forEach(function drawAxisCrosshair(axis) {
  23451. var snap = pick((axis.crosshair || {}).snap, true);
  23452. var point;
  23453. if (snap) {
  23454. point = chart.hoverPoint; // #13002
  23455. if (!point || point.series[axis.coll] !== axis) {
  23456. point = find(points, function (p) {
  23457. return p.series[axis.coll] === axis;
  23458. });
  23459. }
  23460. }
  23461. // Axis has snapping crosshairs, and one of the hover points belongs
  23462. // to axis. Always call drawCrosshair when it is not snap.
  23463. if (point || !snap) {
  23464. axis.drawCrosshair(e, point);
  23465. // Axis has snapping crosshairs, but no hover point belongs to axis
  23466. }
  23467. else {
  23468. axis.hideCrosshair();
  23469. }
  23470. });
  23471. };
  23472. /**
  23473. * Scale series groups to a certain scale and translation.
  23474. *
  23475. * @private
  23476. * @function Highcharts.Pointer#scaleGroups
  23477. *
  23478. * @param {Highcharts.SeriesPlotBoxObject} [attribs]
  23479. *
  23480. * @param {boolean} [clip]
  23481. *
  23482. * @return {void}
  23483. */
  23484. Pointer.prototype.scaleGroups = function (attribs, clip) {
  23485. var chart = this.chart, seriesAttribs;
  23486. // Scale each series
  23487. chart.series.forEach(function (series) {
  23488. seriesAttribs = attribs || series.getPlotBox(); // #1701
  23489. if (series.xAxis && series.xAxis.zoomEnabled && series.group) {
  23490. series.group.attr(seriesAttribs);
  23491. if (series.markerGroup) {
  23492. series.markerGroup.attr(seriesAttribs);
  23493. series.markerGroup.clip(clip ? chart.clipRect : null);
  23494. }
  23495. if (series.dataLabelsGroup) {
  23496. series.dataLabelsGroup.attr(seriesAttribs);
  23497. }
  23498. }
  23499. });
  23500. // Clip
  23501. chart.clipRect.attr(clip || chart.clipBox);
  23502. };
  23503. /**
  23504. * Set the JS DOM events on the container and document. This method should
  23505. * contain a one-to-one assignment between methods and their handlers. Any
  23506. * advanced logic should be moved to the handler reflecting the event's
  23507. * name.
  23508. *
  23509. * @private
  23510. * @function Highcharts.Pointer#setDOMEvents
  23511. *
  23512. * @return {void}
  23513. */
  23514. Pointer.prototype.setDOMEvents = function () {
  23515. var container = this.chart.container, ownerDoc = container.ownerDocument;
  23516. container.onmousedown = this.onContainerMouseDown.bind(this);
  23517. container.onmousemove = this.onContainerMouseMove.bind(this);
  23518. container.onclick = this.onContainerClick.bind(this);
  23519. this.unbindContainerMouseLeave = addEvent(container, 'mouseleave', this.onContainerMouseLeave.bind(this));
  23520. if (!H.unbindDocumentMouseUp) {
  23521. H.unbindDocumentMouseUp = addEvent(ownerDoc, 'mouseup', this.onDocumentMouseUp.bind(this));
  23522. }
  23523. if (H.hasTouch) {
  23524. addEvent(container, 'touchstart', this.onContainerTouchStart.bind(this));
  23525. addEvent(container, 'touchmove', this.onContainerTouchMove.bind(this));
  23526. if (!H.unbindDocumentTouchEnd) {
  23527. H.unbindDocumentTouchEnd = addEvent(ownerDoc, 'touchend', this.onDocumentTouchEnd.bind(this));
  23528. }
  23529. }
  23530. };
  23531. /**
  23532. * Sets the index of the hovered chart and leaves the previous hovered
  23533. * chart, to reset states like tooltip.
  23534. *
  23535. * @private
  23536. * @function Highcharts.Pointer#setHoverChartIndex
  23537. */
  23538. Pointer.prototype.setHoverChartIndex = function () {
  23539. var chart = this.chart;
  23540. var hoverChart = H.charts[pick(H.hoverChartIndex, -1)];
  23541. if (hoverChart &&
  23542. hoverChart !== chart) {
  23543. hoverChart.pointer.onContainerMouseLeave({ relatedTarget: true });
  23544. }
  23545. if (!hoverChart ||
  23546. !hoverChart.mouseIsDown) {
  23547. H.hoverChartIndex = chart.index;
  23548. }
  23549. };
  23550. /**
  23551. * General touch handler shared by touchstart and touchmove.
  23552. *
  23553. * @private
  23554. * @function Highcharts.Pointer#touch
  23555. *
  23556. * @param {Highcharts.PointerEventObject} e
  23557. *
  23558. * @param {boolean} [start]
  23559. *
  23560. * @return {void}
  23561. */
  23562. Pointer.prototype.touch = function (e, start) {
  23563. var chart = this.chart, hasMoved, pinchDown, isInside;
  23564. this.setHoverChartIndex();
  23565. if (e.touches.length === 1) {
  23566. e = this.normalize(e);
  23567. isInside = chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop);
  23568. if (isInside && !chart.openMenu) {
  23569. // Run mouse events and display tooltip etc
  23570. if (start) {
  23571. this.runPointActions(e);
  23572. }
  23573. // Android fires touchmove events after the touchstart even if
  23574. // the finger hasn't moved, or moved only a pixel or two. In iOS
  23575. // however, the touchmove doesn't fire unless the finger moves
  23576. // more than ~4px. So we emulate this behaviour in Android by
  23577. // checking how much it moved, and cancelling on small
  23578. // distances. #3450.
  23579. if (e.type === 'touchmove') {
  23580. pinchDown = this.pinchDown;
  23581. hasMoved = pinchDown[0] ? Math.sqrt(// #5266
  23582. Math.pow(pinchDown[0].chartX - e.chartX, 2) +
  23583. Math.pow(pinchDown[0].chartY - e.chartY, 2)) >= 4 : false;
  23584. }
  23585. if (pick(hasMoved, true)) {
  23586. this.pinch(e);
  23587. }
  23588. }
  23589. else if (start) {
  23590. // Hide the tooltip on touching outside the plot area (#1203)
  23591. this.reset();
  23592. }
  23593. }
  23594. else if (e.touches.length === 2) {
  23595. this.pinch(e);
  23596. }
  23597. };
  23598. /**
  23599. * Resolve the zoomType option, this is reset on all touch start and mouse
  23600. * down events.
  23601. *
  23602. * @private
  23603. * @function Highcharts.Pointer#zoomOption
  23604. *
  23605. * @param {global.Event} e
  23606. * Event object.
  23607. *
  23608. * @param {void}
  23609. */
  23610. Pointer.prototype.zoomOption = function (e) {
  23611. var chart = this.chart, options = chart.options.chart, zoomType = options.zoomType || '', inverted = chart.inverted, zoomX, zoomY;
  23612. // Look for the pinchType option
  23613. if (/touch/.test(e.type)) {
  23614. zoomType = pick(options.pinchType, zoomType);
  23615. }
  23616. this.zoomX = zoomX = /x/.test(zoomType);
  23617. this.zoomY = zoomY = /y/.test(zoomType);
  23618. this.zoomHor = (zoomX && !inverted) || (zoomY && inverted);
  23619. this.zoomVert = (zoomY && !inverted) || (zoomX && inverted);
  23620. this.hasZoom = zoomX || zoomY;
  23621. };
  23622. return Pointer;
  23623. }());
  23624. H.Pointer = Pointer;
  23625. return Pointer;
  23626. });
  23627. _registerModule(_modules, 'parts/MSPointer.js', [_modules['parts/Globals.js'], _modules['parts/Pointer.js'], _modules['parts/Utilities.js']], function (H, Pointer, U) {
  23628. /* *
  23629. *
  23630. * (c) 2010-2020 Torstein Honsi
  23631. *
  23632. * License: www.highcharts.com/license
  23633. *
  23634. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  23635. *
  23636. * */
  23637. var __extends = (this && this.__extends) || (function () {
  23638. var extendStatics = function (d, b) {
  23639. extendStatics = Object.setPrototypeOf ||
  23640. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  23641. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  23642. return extendStatics(d, b);
  23643. };
  23644. return function (d, b) {
  23645. extendStatics(d, b);
  23646. function __() { this.constructor = d; }
  23647. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  23648. };
  23649. })();
  23650. var charts = H.charts, doc = H.doc, noop = H.noop, win = H.win;
  23651. var addEvent = U.addEvent, css = U.css, objectEach = U.objectEach, removeEvent = U.removeEvent;
  23652. /* globals MSPointerEvent, PointerEvent */
  23653. // The touches object keeps track of the points being touched at all times
  23654. var touches = {};
  23655. var hasPointerEvent = !!win.PointerEvent;
  23656. /* eslint-disable valid-jsdoc */
  23657. /** @private */
  23658. function getWebkitTouches() {
  23659. var fake = [];
  23660. fake.item = function (i) {
  23661. return this[i];
  23662. };
  23663. objectEach(touches, function (touch) {
  23664. fake.push({
  23665. pageX: touch.pageX,
  23666. pageY: touch.pageY,
  23667. target: touch.target
  23668. });
  23669. });
  23670. return fake;
  23671. }
  23672. /** @private */
  23673. function translateMSPointer(e, method, wktype, func) {
  23674. var p;
  23675. if ((e.pointerType === 'touch' ||
  23676. e.pointerType === e.MSPOINTER_TYPE_TOUCH) && charts[H.hoverChartIndex]) {
  23677. func(e);
  23678. p = charts[H.hoverChartIndex].pointer;
  23679. p[method]({
  23680. type: wktype,
  23681. target: e.currentTarget,
  23682. preventDefault: noop,
  23683. touches: getWebkitTouches()
  23684. });
  23685. }
  23686. }
  23687. /** @private */
  23688. var MSPointer = /** @class */ (function (_super) {
  23689. __extends(MSPointer, _super);
  23690. function MSPointer() {
  23691. return _super !== null && _super.apply(this, arguments) || this;
  23692. }
  23693. /* *
  23694. *
  23695. * Functions
  23696. *
  23697. * */
  23698. /**
  23699. * Add or remove the MS Pointer specific events
  23700. *
  23701. * @private
  23702. * @function Highcharts.Pointer#batchMSEvents
  23703. *
  23704. * @param {Function} fn
  23705. *
  23706. * @return {void}
  23707. */
  23708. MSPointer.prototype.batchMSEvents = function (fn) {
  23709. fn(this.chart.container, hasPointerEvent ? 'pointerdown' : 'MSPointerDown', this.onContainerPointerDown);
  23710. fn(this.chart.container, hasPointerEvent ? 'pointermove' : 'MSPointerMove', this.onContainerPointerMove);
  23711. fn(doc, hasPointerEvent ? 'pointerup' : 'MSPointerUp', this.onDocumentPointerUp);
  23712. };
  23713. // Destroy MS events also
  23714. MSPointer.prototype.destroy = function () {
  23715. this.batchMSEvents(removeEvent);
  23716. _super.prototype.destroy.call(this);
  23717. };
  23718. // Disable default IE actions for pinch and such on chart element
  23719. MSPointer.prototype.init = function (chart, options) {
  23720. _super.prototype.init.call(this, chart, options);
  23721. if (this.hasZoom) { // #4014
  23722. css(chart.container, {
  23723. '-ms-touch-action': 'none',
  23724. 'touch-action': 'none'
  23725. });
  23726. }
  23727. };
  23728. /**
  23729. * @private
  23730. * @function Highcharts.Pointer#onContainerPointerDown
  23731. *
  23732. * @param {Highcharts.PointerEventObject} e
  23733. *
  23734. * @return {void}
  23735. */
  23736. MSPointer.prototype.onContainerPointerDown = function (e) {
  23737. translateMSPointer(e, 'onContainerTouchStart', 'touchstart', function (e) {
  23738. touches[e.pointerId] = {
  23739. pageX: e.pageX,
  23740. pageY: e.pageY,
  23741. target: e.currentTarget
  23742. };
  23743. });
  23744. };
  23745. /**
  23746. * @private
  23747. * @function Highcharts.Pointer#onContainerPointerMove
  23748. *
  23749. * @param {Highcharts.PointerEventObject} e
  23750. *
  23751. * @return {void}
  23752. */
  23753. MSPointer.prototype.onContainerPointerMove = function (e) {
  23754. translateMSPointer(e, 'onContainerTouchMove', 'touchmove', function (e) {
  23755. touches[e.pointerId] = ({ pageX: e.pageX, pageY: e.pageY });
  23756. if (!touches[e.pointerId].target) {
  23757. touches[e.pointerId].target = e.currentTarget;
  23758. }
  23759. });
  23760. };
  23761. /**
  23762. * @private
  23763. * @function Highcharts.Pointer#onDocumentPointerUp
  23764. *
  23765. * @param {Highcharts.PointerEventObject} e
  23766. *
  23767. * @return {void}
  23768. */
  23769. MSPointer.prototype.onDocumentPointerUp = function (e) {
  23770. translateMSPointer(e, 'onDocumentTouchEnd', 'touchend', function (e) {
  23771. delete touches[e.pointerId];
  23772. });
  23773. };
  23774. // Add IE specific touch events to chart
  23775. MSPointer.prototype.setDOMEvents = function () {
  23776. _super.prototype.setDOMEvents.call(this);
  23777. if (this.hasZoom || this.followTouchMove) {
  23778. this.batchMSEvents(addEvent);
  23779. }
  23780. };
  23781. return MSPointer;
  23782. }(Pointer));
  23783. return MSPointer;
  23784. });
  23785. _registerModule(_modules, 'parts/Legend.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  23786. /* *
  23787. *
  23788. * (c) 2010-2020 Torstein Honsi
  23789. *
  23790. * License: www.highcharts.com/license
  23791. *
  23792. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  23793. *
  23794. * */
  23795. /**
  23796. * Gets fired when the legend item belonging to a point is clicked. The default
  23797. * action is to toggle the visibility of the point. This can be prevented by
  23798. * returning `false` or calling `event.preventDefault()`.
  23799. *
  23800. * @callback Highcharts.PointLegendItemClickCallbackFunction
  23801. *
  23802. * @param {Highcharts.Point} this
  23803. * The point on which the event occured.
  23804. *
  23805. * @param {Highcharts.PointLegendItemClickEventObject} event
  23806. * The event that occured.
  23807. */
  23808. /**
  23809. * Information about the legend click event.
  23810. *
  23811. * @interface Highcharts.PointLegendItemClickEventObject
  23812. */ /**
  23813. * Related browser event.
  23814. * @name Highcharts.PointLegendItemClickEventObject#browserEvent
  23815. * @type {Highcharts.PointerEvent}
  23816. */ /**
  23817. * Prevent the default action of toggle the visibility of the point.
  23818. * @name Highcharts.PointLegendItemClickEventObject#preventDefault
  23819. * @type {Function}
  23820. */ /**
  23821. * Related point.
  23822. * @name Highcharts.PointLegendItemClickEventObject#target
  23823. * @type {Highcharts.Point}
  23824. */ /**
  23825. * Event type.
  23826. * @name Highcharts.PointLegendItemClickEventObject#type
  23827. * @type {"legendItemClick"}
  23828. */
  23829. /**
  23830. * Gets fired when the legend item belonging to a series is clicked. The default
  23831. * action is to toggle the visibility of the series. This can be prevented by
  23832. * returning `false` or calling `event.preventDefault()`.
  23833. *
  23834. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  23835. *
  23836. * @param {Highcharts.Series} this
  23837. * The series where the event occured.
  23838. *
  23839. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  23840. * The event that occured.
  23841. */
  23842. /**
  23843. * Information about the legend click event.
  23844. *
  23845. * @interface Highcharts.SeriesLegendItemClickEventObject
  23846. */ /**
  23847. * Related browser event.
  23848. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  23849. * @type {Highcharts.PointerEvent}
  23850. */ /**
  23851. * Prevent the default action of toggle the visibility of the series.
  23852. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  23853. * @type {Function}
  23854. */ /**
  23855. * Related series.
  23856. * @name Highcharts.SeriesLegendItemClickEventObject#target
  23857. * @type {Highcharts.Series}
  23858. */ /**
  23859. * Event type.
  23860. * @name Highcharts.SeriesLegendItemClickEventObject#type
  23861. * @type {"legendItemClick"}
  23862. */
  23863. var addEvent = U.addEvent, animObject = U.animObject, css = U.css, defined = U.defined, discardElement = U.discardElement, find = U.find, fireEvent = U.fireEvent, format = U.format, isNumber = U.isNumber, merge = U.merge, pick = U.pick, relativeLength = U.relativeLength, setAnimation = U.setAnimation, stableSort = U.stableSort, syncTimeout = U.syncTimeout, wrap = U.wrap;
  23864. var isFirefox = H.isFirefox, marginNames = H.marginNames, win = H.win;
  23865. /* eslint-disable no-invalid-this, valid-jsdoc */
  23866. /**
  23867. * The overview of the chart's series. The legend object is instanciated
  23868. * internally in the chart constructor, and is available from the `chart.legend`
  23869. * property. Each chart has only one legend.
  23870. *
  23871. * @class
  23872. * @name Highcharts.Legend
  23873. *
  23874. * @param {Highcharts.Chart} chart
  23875. * The chart instance.
  23876. *
  23877. * @param {Highcharts.LegendOptions} options
  23878. * Legend options.
  23879. */
  23880. var Legend = /** @class */ (function () {
  23881. /* *
  23882. *
  23883. * Constructors
  23884. *
  23885. * */
  23886. function Legend(chart, options) {
  23887. /* *
  23888. *
  23889. * Properties
  23890. *
  23891. * */
  23892. this.allItems = [];
  23893. this.box = void 0;
  23894. this.contentGroup = void 0;
  23895. this.display = false;
  23896. this.group = void 0;
  23897. this.initialItemY = 0;
  23898. this.itemHeight = 0;
  23899. this.itemMarginBottom = 0;
  23900. this.itemMarginTop = 0;
  23901. this.itemX = 0;
  23902. this.itemY = 0;
  23903. this.lastItemY = 0;
  23904. this.lastLineHeight = 0;
  23905. this.legendHeight = 0;
  23906. this.legendWidth = 0;
  23907. this.maxItemWidth = 0;
  23908. this.maxLegendWidth = 0;
  23909. this.offsetWidth = 0;
  23910. this.options = {};
  23911. this.padding = 0;
  23912. this.pages = [];
  23913. this.proximate = false;
  23914. this.scrollGroup = void 0;
  23915. this.symbolHeight = 0;
  23916. this.symbolWidth = 0;
  23917. this.titleHeight = 0;
  23918. this.totalItemWidth = 0;
  23919. this.widthOption = 0;
  23920. this.chart = chart;
  23921. this.init(chart, options);
  23922. }
  23923. /* *
  23924. *
  23925. * Functions
  23926. *
  23927. * */
  23928. /**
  23929. * Initialize the legend.
  23930. *
  23931. * @private
  23932. * @function Highcharts.Legend#init
  23933. *
  23934. * @param {Highcharts.Chart} chart
  23935. * The chart instance.
  23936. *
  23937. * @param {Highcharts.LegendOptions} options
  23938. * Legend options.
  23939. */
  23940. Legend.prototype.init = function (chart, options) {
  23941. /**
  23942. * Chart of this legend.
  23943. *
  23944. * @readonly
  23945. * @name Highcharts.Legend#chart
  23946. * @type {Highcharts.Chart}
  23947. */
  23948. this.chart = chart;
  23949. this.setOptions(options);
  23950. if (options.enabled) {
  23951. // Render it
  23952. this.render();
  23953. // move checkboxes
  23954. addEvent(this.chart, 'endResize', function () {
  23955. this.legend.positionCheckboxes();
  23956. });
  23957. if (this.proximate) {
  23958. this.unchartrender = addEvent(this.chart, 'render', function () {
  23959. this.legend.proximatePositions();
  23960. this.legend.positionItems();
  23961. });
  23962. }
  23963. else if (this.unchartrender) {
  23964. this.unchartrender();
  23965. }
  23966. }
  23967. };
  23968. /**
  23969. * @private
  23970. * @function Highcharts.Legend#setOptions
  23971. * @param {Highcharts.LegendOptions} options
  23972. */
  23973. Legend.prototype.setOptions = function (options) {
  23974. var padding = pick(options.padding, 8);
  23975. /**
  23976. * Legend options.
  23977. *
  23978. * @readonly
  23979. * @name Highcharts.Legend#options
  23980. * @type {Highcharts.LegendOptions}
  23981. */
  23982. this.options = options;
  23983. if (!this.chart.styledMode) {
  23984. this.itemStyle = options.itemStyle;
  23985. this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle);
  23986. }
  23987. this.itemMarginTop = options.itemMarginTop || 0;
  23988. this.itemMarginBottom = options.itemMarginBottom || 0;
  23989. this.padding = padding;
  23990. this.initialItemY = padding - 5; // 5 is pixels above the text
  23991. this.symbolWidth = pick(options.symbolWidth, 16);
  23992. this.pages = [];
  23993. this.proximate = options.layout === 'proximate' && !this.chart.inverted;
  23994. this.baseline = void 0; // #12705: baseline has to be reset on every update
  23995. };
  23996. /**
  23997. * Update the legend with new options. Equivalent to running `chart.update`
  23998. * with a legend configuration option.
  23999. *
  24000. * @sample highcharts/legend/legend-update/
  24001. * Legend update
  24002. *
  24003. * @function Highcharts.Legend#update
  24004. *
  24005. * @param {Highcharts.LegendOptions} options
  24006. * Legend options.
  24007. *
  24008. * @param {boolean} [redraw=true]
  24009. * Whether to redraw the chart after the axis is altered. If doing more
  24010. * operations on the chart, it is a good idea to set redraw to false and
  24011. * call {@link Chart#redraw} after. Whether to redraw the chart.
  24012. *
  24013. * @fires Highcharts.Legends#event:afterUpdate
  24014. */
  24015. Legend.prototype.update = function (options, redraw) {
  24016. var chart = this.chart;
  24017. this.setOptions(merge(true, this.options, options));
  24018. this.destroy();
  24019. chart.isDirtyLegend = chart.isDirtyBox = true;
  24020. if (pick(redraw, true)) {
  24021. chart.redraw();
  24022. }
  24023. fireEvent(this, 'afterUpdate');
  24024. };
  24025. /**
  24026. * Set the colors for the legend item.
  24027. *
  24028. * @private
  24029. * @function Highcharts.Legend#colorizeItem
  24030. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  24031. * A Series or Point instance
  24032. * @param {boolean} [visible=false]
  24033. * Dimmed or colored
  24034. *
  24035. * @todo
  24036. * Make events official: Fires the event `afterColorizeItem`.
  24037. */
  24038. Legend.prototype.colorizeItem = function (item, visible) {
  24039. item.legendGroup[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden');
  24040. if (!this.chart.styledMode) {
  24041. var legend = this, options = legend.options, legendItem = item.legendItem, legendLine = item.legendLine, legendSymbol = item.legendSymbol, hiddenColor = legend.itemHiddenStyle.color, textColor = visible ?
  24042. options.itemStyle.color :
  24043. hiddenColor, symbolColor = visible ?
  24044. (item.color || hiddenColor) :
  24045. hiddenColor, markerOptions = item.options && item.options.marker, symbolAttr = { fill: symbolColor };
  24046. if (legendItem) {
  24047. legendItem.css({
  24048. fill: textColor,
  24049. color: textColor // #1553, oldIE
  24050. });
  24051. }
  24052. if (legendLine) {
  24053. legendLine.attr({ stroke: symbolColor });
  24054. }
  24055. if (legendSymbol) {
  24056. // Apply marker options
  24057. if (markerOptions && legendSymbol.isMarker) { // #585
  24058. symbolAttr = item.pointAttribs();
  24059. if (!visible) {
  24060. // #6769
  24061. symbolAttr.stroke = symbolAttr.fill = hiddenColor;
  24062. }
  24063. }
  24064. legendSymbol.attr(symbolAttr);
  24065. }
  24066. }
  24067. fireEvent(this, 'afterColorizeItem', { item: item, visible: visible });
  24068. };
  24069. /**
  24070. * @private
  24071. * @function Highcharts.Legend#positionItems
  24072. */
  24073. Legend.prototype.positionItems = function () {
  24074. // Now that the legend width and height are established, put the items
  24075. // in the final position
  24076. this.allItems.forEach(this.positionItem, this);
  24077. if (!this.chart.isResizing) {
  24078. this.positionCheckboxes();
  24079. }
  24080. };
  24081. /**
  24082. * Position the legend item.
  24083. *
  24084. * @private
  24085. * @function Highcharts.Legend#positionItem
  24086. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  24087. * The item to position
  24088. */
  24089. Legend.prototype.positionItem = function (item) {
  24090. var _this = this;
  24091. var legend = this, options = legend.options, symbolPadding = options.symbolPadding, ltr = !options.rtl, legendItemPos = item._legendItemPos, itemX = legendItemPos[0], itemY = legendItemPos[1], checkbox = item.checkbox, legendGroup = item.legendGroup;
  24092. if (legendGroup && legendGroup.element) {
  24093. var attribs = {
  24094. translateX: ltr ?
  24095. itemX :
  24096. legend.legendWidth - itemX - 2 * symbolPadding - 4,
  24097. translateY: itemY
  24098. };
  24099. var complete = function () {
  24100. fireEvent(_this, 'afterPositionItem', { item: item });
  24101. };
  24102. if (defined(legendGroup.translateY)) {
  24103. legendGroup.animate(attribs, { complete: complete });
  24104. }
  24105. else {
  24106. legendGroup.attr(attribs);
  24107. complete();
  24108. }
  24109. }
  24110. if (checkbox) {
  24111. checkbox.x = itemX;
  24112. checkbox.y = itemY;
  24113. }
  24114. };
  24115. /**
  24116. * Destroy a single legend item, used internally on removing series items.
  24117. *
  24118. * @private
  24119. * @function Highcharts.Legend#destroyItem
  24120. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  24121. * The item to remove
  24122. */
  24123. Legend.prototype.destroyItem = function (item) {
  24124. var checkbox = item.checkbox;
  24125. // destroy SVG elements
  24126. ['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'].forEach(function (key) {
  24127. if (item[key]) {
  24128. item[key] = item[key].destroy();
  24129. }
  24130. });
  24131. if (checkbox) {
  24132. discardElement(item.checkbox);
  24133. }
  24134. };
  24135. /**
  24136. * Destroy the legend. Used internally. To reflow objects, `chart.redraw`
  24137. * must be called after destruction.
  24138. *
  24139. * @private
  24140. * @function Highcharts.Legend#destroy
  24141. */
  24142. Legend.prototype.destroy = function () {
  24143. /**
  24144. * @private
  24145. * @param {string} key
  24146. * @return {void}
  24147. */
  24148. function destroyItems(key) {
  24149. if (this[key]) {
  24150. this[key] = this[key].destroy();
  24151. }
  24152. }
  24153. // Destroy items
  24154. this.getAllItems().forEach(function (item) {
  24155. ['legendItem', 'legendGroup'].forEach(destroyItems, item);
  24156. });
  24157. // Destroy legend elements
  24158. [
  24159. 'clipRect',
  24160. 'up',
  24161. 'down',
  24162. 'pager',
  24163. 'nav',
  24164. 'box',
  24165. 'title',
  24166. 'group'
  24167. ].forEach(destroyItems, this);
  24168. this.display = null; // Reset in .render on update.
  24169. };
  24170. /**
  24171. * Position the checkboxes after the width is determined.
  24172. *
  24173. * @private
  24174. * @function Highcharts.Legend#positionCheckboxes
  24175. */
  24176. Legend.prototype.positionCheckboxes = function () {
  24177. var alignAttr = this.group && this.group.alignAttr, translateY, clipHeight = this.clipHeight || this.legendHeight, titleHeight = this.titleHeight;
  24178. if (alignAttr) {
  24179. translateY = alignAttr.translateY;
  24180. this.allItems.forEach(function (item) {
  24181. var checkbox = item.checkbox, top;
  24182. if (checkbox) {
  24183. top = translateY + titleHeight + checkbox.y +
  24184. (this.scrollOffset || 0) + 3;
  24185. css(checkbox, {
  24186. left: (alignAttr.translateX + item.checkboxOffset +
  24187. checkbox.x - 20) + 'px',
  24188. top: top + 'px',
  24189. display: this.proximate || (top > translateY - 6 &&
  24190. top < translateY + clipHeight - 6) ?
  24191. '' :
  24192. 'none'
  24193. });
  24194. }
  24195. }, this);
  24196. }
  24197. };
  24198. /**
  24199. * Render the legend title on top of the legend.
  24200. *
  24201. * @private
  24202. * @function Highcharts.Legend#renderTitle
  24203. */
  24204. Legend.prototype.renderTitle = function () {
  24205. var options = this.options, padding = this.padding, titleOptions = options.title, titleHeight = 0, bBox;
  24206. if (titleOptions.text) {
  24207. if (!this.title) {
  24208. /**
  24209. * SVG element of the legend title.
  24210. *
  24211. * @readonly
  24212. * @name Highcharts.Legend#title
  24213. * @type {Highcharts.SVGElement}
  24214. */
  24215. this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, options.useHTML, null, 'legend-title')
  24216. .attr({ zIndex: 1 });
  24217. if (!this.chart.styledMode) {
  24218. this.title.css(titleOptions.style);
  24219. }
  24220. this.title.add(this.group);
  24221. }
  24222. // Set the max title width (#7253)
  24223. if (!titleOptions.width) {
  24224. this.title.css({
  24225. width: this.maxLegendWidth + 'px'
  24226. });
  24227. }
  24228. bBox = this.title.getBBox();
  24229. titleHeight = bBox.height;
  24230. this.offsetWidth = bBox.width; // #1717
  24231. this.contentGroup.attr({ translateY: titleHeight });
  24232. }
  24233. this.titleHeight = titleHeight;
  24234. };
  24235. /**
  24236. * Set the legend item text.
  24237. *
  24238. * @function Highcharts.Legend#setText
  24239. * @param {Highcharts.Point|Highcharts.Series} item
  24240. * The item for which to update the text in the legend.
  24241. */
  24242. Legend.prototype.setText = function (item) {
  24243. var options = this.options;
  24244. item.legendItem.attr({
  24245. text: options.labelFormat ?
  24246. format(options.labelFormat, item, this.chart) :
  24247. options.labelFormatter.call(item)
  24248. });
  24249. };
  24250. /**
  24251. * Render a single specific legend item. Called internally from the `render`
  24252. * function.
  24253. *
  24254. * @private
  24255. * @function Highcharts.Legend#renderItem
  24256. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  24257. * The item to render.
  24258. */
  24259. Legend.prototype.renderItem = function (item) {
  24260. var legend = this, chart = legend.chart, renderer = chart.renderer, options = legend.options, horizontal = options.layout === 'horizontal', symbolWidth = legend.symbolWidth, symbolPadding = options.symbolPadding, itemStyle = legend.itemStyle, itemHiddenStyle = legend.itemHiddenStyle, itemDistance = horizontal ? pick(options.itemDistance, 20) : 0, ltr = !options.rtl, bBox, li = item.legendItem, isSeries = !item.series, series = !isSeries && item.series.drawLegendSymbol ?
  24261. item.series :
  24262. item, seriesOptions = series.options, showCheckbox = legend.createCheckboxForItem &&
  24263. seriesOptions &&
  24264. seriesOptions.showCheckbox,
  24265. // full width minus text width
  24266. itemExtraWidth = symbolWidth + symbolPadding +
  24267. itemDistance + (showCheckbox ? 20 : 0), useHTML = options.useHTML, itemClassName = item.options.className;
  24268. if (!li) { // generate it once, later move it
  24269. // Generate the group box, a group to hold the symbol and text. Text
  24270. // is to be appended in Legend class.
  24271. item.legendGroup = renderer
  24272. .g('legend-item')
  24273. .addClass('highcharts-' + series.type + '-series ' +
  24274. 'highcharts-color-' + item.colorIndex +
  24275. (itemClassName ? ' ' + itemClassName : '') +
  24276. (isSeries ?
  24277. ' highcharts-series-' + item.index :
  24278. ''))
  24279. .attr({ zIndex: 1 })
  24280. .add(legend.scrollGroup);
  24281. // Generate the list item text and add it to the group
  24282. item.legendItem = li = renderer.text('', ltr ?
  24283. symbolWidth + symbolPadding :
  24284. -symbolPadding, legend.baseline || 0, useHTML);
  24285. if (!chart.styledMode) {
  24286. // merge to prevent modifying original (#1021)
  24287. li.css(merge(item.visible ?
  24288. itemStyle :
  24289. itemHiddenStyle));
  24290. }
  24291. li
  24292. .attr({
  24293. align: ltr ? 'left' : 'right',
  24294. zIndex: 2
  24295. })
  24296. .add(item.legendGroup);
  24297. // Get the baseline for the first item - the font size is equal for
  24298. // all
  24299. if (!legend.baseline) {
  24300. legend.fontMetrics = renderer.fontMetrics(chart.styledMode ? 12 : itemStyle.fontSize, li);
  24301. legend.baseline =
  24302. legend.fontMetrics.f + 3 + legend.itemMarginTop;
  24303. li.attr('y', legend.baseline);
  24304. }
  24305. // Draw the legend symbol inside the group box
  24306. legend.symbolHeight =
  24307. options.symbolHeight || legend.fontMetrics.f;
  24308. series.drawLegendSymbol(legend, item);
  24309. if (legend.setItemEvents) {
  24310. legend.setItemEvents(item, li, useHTML);
  24311. }
  24312. }
  24313. // Add the HTML checkbox on top
  24314. if (showCheckbox && !item.checkbox && legend.createCheckboxForItem) {
  24315. legend.createCheckboxForItem(item);
  24316. }
  24317. // Colorize the items
  24318. legend.colorizeItem(item, item.visible);
  24319. // Take care of max width and text overflow (#6659)
  24320. if (chart.styledMode || !itemStyle.width) {
  24321. li.css({
  24322. width: ((options.itemWidth ||
  24323. legend.widthOption ||
  24324. chart.spacingBox.width) - itemExtraWidth) + 'px'
  24325. });
  24326. }
  24327. // Always update the text
  24328. legend.setText(item);
  24329. // calculate the positions for the next line
  24330. bBox = li.getBBox();
  24331. item.itemWidth = item.checkboxOffset =
  24332. options.itemWidth ||
  24333. item.legendItemWidth ||
  24334. bBox.width + itemExtraWidth;
  24335. legend.maxItemWidth = Math.max(legend.maxItemWidth, item.itemWidth);
  24336. legend.totalItemWidth += item.itemWidth;
  24337. legend.itemHeight = item.itemHeight = Math.round(item.legendItemHeight || bBox.height || legend.symbolHeight);
  24338. };
  24339. /**
  24340. * Get the position of the item in the layout. We now know the
  24341. * maxItemWidth from the previous loop.
  24342. *
  24343. * @private
  24344. * @function Highcharts.Legend#layoutItem
  24345. * @param {Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series} item
  24346. */
  24347. Legend.prototype.layoutItem = function (item) {
  24348. var 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 &&
  24349. this.totalItemWidth > maxLegendWidth) ?
  24350. this.maxItemWidth :
  24351. item.itemWidth;
  24352. // If the item exceeds the width, start a new line
  24353. if (horizontal &&
  24354. this.itemX - padding + itemWidth > maxLegendWidth) {
  24355. this.itemX = padding;
  24356. if (this.lastLineHeight) { // Not for the first line (#10167)
  24357. this.itemY += (itemMarginTop +
  24358. this.lastLineHeight +
  24359. itemMarginBottom);
  24360. }
  24361. this.lastLineHeight = 0; // reset for next line (#915, #3976)
  24362. }
  24363. // Set the edge positions
  24364. this.lastItemY = itemMarginTop + this.itemY + itemMarginBottom;
  24365. this.lastLineHeight = Math.max(// #915
  24366. itemHeight, this.lastLineHeight);
  24367. // cache the position of the newly generated or reordered items
  24368. item._legendItemPos = [this.itemX, this.itemY];
  24369. // advance
  24370. if (horizontal) {
  24371. this.itemX += itemWidth;
  24372. }
  24373. else {
  24374. this.itemY +=
  24375. itemMarginTop + itemHeight + itemMarginBottom;
  24376. this.lastLineHeight = itemHeight;
  24377. }
  24378. // the width of the widest item
  24379. this.offsetWidth = this.widthOption || Math.max((horizontal ? this.itemX - padding - (item.checkbox ?
  24380. // decrease by itemDistance only when no checkbox #4853
  24381. 0 :
  24382. itemDistance) : itemWidth) + padding, this.offsetWidth);
  24383. };
  24384. /**
  24385. * Get all items, which is one item per series for most series and one
  24386. * item per point for pie series and its derivatives. Fires the event
  24387. * `afterGetAllItems`.
  24388. *
  24389. * @private
  24390. * @function Highcharts.Legend#getAllItems
  24391. * @return {Array<(Highcharts.BubbleLegend|Highcharts.Point|Highcharts.Series)>}
  24392. * The current items in the legend.
  24393. * @fires Highcharts.Legend#event:afterGetAllItems
  24394. */
  24395. Legend.prototype.getAllItems = function () {
  24396. var allItems = [];
  24397. this.chart.series.forEach(function (series) {
  24398. var seriesOptions = series && series.options;
  24399. // Handle showInLegend. If the series is linked to another series,
  24400. // defaults to false.
  24401. if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? void 0 : false, true)) {
  24402. // Use points or series for the legend item depending on
  24403. // legendType
  24404. allItems = allItems.concat(series.legendItems ||
  24405. (seriesOptions.legendType === 'point' ?
  24406. series.data :
  24407. series));
  24408. }
  24409. });
  24410. fireEvent(this, 'afterGetAllItems', { allItems: allItems });
  24411. return allItems;
  24412. };
  24413. /**
  24414. * Get a short, three letter string reflecting the alignment and layout.
  24415. *
  24416. * @private
  24417. * @function Highcharts.Legend#getAlignment
  24418. * @return {string}
  24419. * The alignment, empty string if floating
  24420. */
  24421. Legend.prototype.getAlignment = function () {
  24422. var options = this.options;
  24423. // Use the first letter of each alignment option in order to detect
  24424. // the side. (#4189 - use charAt(x) notation instead of [x] for IE7)
  24425. if (this.proximate) {
  24426. return options.align.charAt(0) + 'tv';
  24427. }
  24428. return options.floating ? '' : (options.align.charAt(0) +
  24429. options.verticalAlign.charAt(0) +
  24430. options.layout.charAt(0));
  24431. };
  24432. /**
  24433. * Adjust the chart margins by reserving space for the legend on only one
  24434. * side of the chart. If the position is set to a corner, top or bottom is
  24435. * reserved for horizontal legends and left or right for vertical ones.
  24436. *
  24437. * @private
  24438. * @function Highcharts.Legend#adjustMargins
  24439. * @param {Array<number>} margin
  24440. * @param {Array<number>} spacing
  24441. */
  24442. Legend.prototype.adjustMargins = function (margin, spacing) {
  24443. var chart = this.chart, options = this.options, alignment = this.getAlignment();
  24444. if (alignment) {
  24445. ([
  24446. /(lth|ct|rth)/,
  24447. /(rtv|rm|rbv)/,
  24448. /(rbh|cb|lbh)/,
  24449. /(lbv|lm|ltv)/
  24450. ]).forEach(function (alignments, side) {
  24451. if (alignments.test(alignment) && !defined(margin[side])) {
  24452. // Now we have detected on which side of the chart we should
  24453. // reserve space for the legend
  24454. chart[marginNames[side]] = Math.max(chart[marginNames[side]], (chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] +
  24455. [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] +
  24456. pick(options.margin, 12) +
  24457. spacing[side] +
  24458. (chart.titleOffset[side] || 0)));
  24459. }
  24460. });
  24461. }
  24462. };
  24463. /**
  24464. * @private
  24465. * @function Highcharts.Legend#proximatePositions
  24466. */
  24467. Legend.prototype.proximatePositions = function () {
  24468. var chart = this.chart, boxes = [], alignLeft = this.options.align === 'left';
  24469. this.allItems.forEach(function (item) {
  24470. var lastPoint, height, useFirstPoint = alignLeft, target, top;
  24471. if (item.yAxis && item.points) {
  24472. if (item.xAxis.options.reversed) {
  24473. useFirstPoint = !useFirstPoint;
  24474. }
  24475. lastPoint = find(useFirstPoint ?
  24476. item.points :
  24477. item.points.slice(0).reverse(), function (item) {
  24478. return isNumber(item.plotY);
  24479. });
  24480. height = this.itemMarginTop +
  24481. item.legendItem.getBBox().height +
  24482. this.itemMarginBottom;
  24483. top = item.yAxis.top - chart.plotTop;
  24484. if (item.visible) {
  24485. target = lastPoint ?
  24486. lastPoint.plotY :
  24487. item.yAxis.height;
  24488. target += top - 0.3 * height;
  24489. }
  24490. else {
  24491. target = top + item.yAxis.height;
  24492. }
  24493. boxes.push({
  24494. target: target,
  24495. size: height,
  24496. item: item
  24497. });
  24498. }
  24499. }, this);
  24500. H.distribute(boxes, chart.plotHeight);
  24501. boxes.forEach(function (box) {
  24502. box.item._legendItemPos[1] =
  24503. chart.plotTop - chart.spacing[0] + box.pos;
  24504. });
  24505. };
  24506. /**
  24507. * Render the legend. This method can be called both before and after
  24508. * `chart.render`. If called after, it will only rearrange items instead
  24509. * of creating new ones. Called internally on initial render and after
  24510. * redraws.
  24511. *
  24512. * @private
  24513. * @function Highcharts.Legend#render
  24514. */
  24515. Legend.prototype.render = function () {
  24516. var legend = this, chart = legend.chart, renderer = chart.renderer, legendGroup = legend.group, allItems, display, legendWidth, legendHeight, box = legend.box, options = legend.options, padding = legend.padding, allowedWidth;
  24517. legend.itemX = padding;
  24518. legend.itemY = legend.initialItemY;
  24519. legend.offsetWidth = 0;
  24520. legend.lastItemY = 0;
  24521. legend.widthOption = relativeLength(options.width, chart.spacingBox.width - padding);
  24522. // Compute how wide the legend is allowed to be
  24523. allowedWidth =
  24524. chart.spacingBox.width - 2 * padding - options.x;
  24525. if (['rm', 'lm'].indexOf(legend.getAlignment().substring(0, 2)) > -1) {
  24526. allowedWidth /= 2;
  24527. }
  24528. legend.maxLegendWidth = legend.widthOption || allowedWidth;
  24529. if (!legendGroup) {
  24530. /**
  24531. * SVG group of the legend.
  24532. *
  24533. * @readonly
  24534. * @name Highcharts.Legend#group
  24535. * @type {Highcharts.SVGElement}
  24536. */
  24537. legend.group = legendGroup = renderer.g('legend')
  24538. .attr({ zIndex: 7 })
  24539. .add();
  24540. legend.contentGroup = renderer.g()
  24541. .attr({ zIndex: 1 }) // above background
  24542. .add(legendGroup);
  24543. legend.scrollGroup = renderer.g()
  24544. .add(legend.contentGroup);
  24545. }
  24546. legend.renderTitle();
  24547. // add each series or point
  24548. allItems = legend.getAllItems();
  24549. // sort by legendIndex
  24550. stableSort(allItems, function (a, b) {
  24551. return ((a.options && a.options.legendIndex) || 0) -
  24552. ((b.options && b.options.legendIndex) || 0);
  24553. });
  24554. // reversed legend
  24555. if (options.reversed) {
  24556. allItems.reverse();
  24557. }
  24558. /**
  24559. * All items for the legend, which is an array of series for most series
  24560. * and an array of points for pie series and its derivatives.
  24561. *
  24562. * @readonly
  24563. * @name Highcharts.Legend#allItems
  24564. * @type {Array<(Highcharts.Point|Highcharts.Series)>}
  24565. */
  24566. legend.allItems = allItems;
  24567. legend.display = display = !!allItems.length;
  24568. // Render the items. First we run a loop to set the text and properties
  24569. // and read all the bounding boxes. The next loop computes the item
  24570. // positions based on the bounding boxes.
  24571. legend.lastLineHeight = 0;
  24572. legend.maxItemWidth = 0;
  24573. legend.totalItemWidth = 0;
  24574. legend.itemHeight = 0;
  24575. allItems.forEach(legend.renderItem, legend);
  24576. allItems.forEach(legend.layoutItem, legend);
  24577. // Get the box
  24578. legendWidth = (legend.widthOption || legend.offsetWidth) + padding;
  24579. legendHeight = legend.lastItemY + legend.lastLineHeight +
  24580. legend.titleHeight;
  24581. legendHeight = legend.handleOverflow(legendHeight);
  24582. legendHeight += padding;
  24583. // Draw the border and/or background
  24584. if (!box) {
  24585. /**
  24586. * SVG element of the legend box.
  24587. *
  24588. * @readonly
  24589. * @name Highcharts.Legend#box
  24590. * @type {Highcharts.SVGElement}
  24591. */
  24592. legend.box = box = renderer.rect()
  24593. .addClass('highcharts-legend-box')
  24594. .attr({
  24595. r: options.borderRadius
  24596. })
  24597. .add(legendGroup);
  24598. box.isNew = true;
  24599. }
  24600. // Presentational
  24601. if (!chart.styledMode) {
  24602. box
  24603. .attr({
  24604. stroke: options.borderColor,
  24605. 'stroke-width': options.borderWidth || 0,
  24606. fill: options.backgroundColor || 'none'
  24607. })
  24608. .shadow(options.shadow);
  24609. }
  24610. if (legendWidth > 0 && legendHeight > 0) {
  24611. box[box.isNew ? 'attr' : 'animate'](box.crisp.call({}, {
  24612. x: 0,
  24613. y: 0,
  24614. width: legendWidth,
  24615. height: legendHeight
  24616. }, box.strokeWidth()));
  24617. box.isNew = false;
  24618. }
  24619. // hide the border if no items
  24620. box[display ? 'show' : 'hide']();
  24621. // Open for responsiveness
  24622. if (chart.styledMode && legendGroup.getStyle('display') === 'none') {
  24623. legendWidth = legendHeight = 0;
  24624. }
  24625. legend.legendWidth = legendWidth;
  24626. legend.legendHeight = legendHeight;
  24627. if (display) {
  24628. legend.align();
  24629. }
  24630. if (!this.proximate) {
  24631. this.positionItems();
  24632. }
  24633. fireEvent(this, 'afterRender');
  24634. };
  24635. /**
  24636. * Align the legend to chart's box.
  24637. *
  24638. * @private
  24639. * @function Highcharts.align
  24640. * @param {Highcharts.BBoxObject} alignTo
  24641. * @return {void}
  24642. */
  24643. Legend.prototype.align = function (alignTo) {
  24644. if (alignTo === void 0) { alignTo = this.chart.spacingBox; }
  24645. var chart = this.chart, options = this.options;
  24646. // If aligning to the top and the layout is horizontal, adjust for
  24647. // the title (#7428)
  24648. var y = alignTo.y;
  24649. if (/(lth|ct|rth)/.test(this.getAlignment()) &&
  24650. chart.titleOffset[0] > 0) {
  24651. y += chart.titleOffset[0];
  24652. }
  24653. else if (/(lbh|cb|rbh)/.test(this.getAlignment()) &&
  24654. chart.titleOffset[2] > 0) {
  24655. y -= chart.titleOffset[2];
  24656. }
  24657. if (y !== alignTo.y) {
  24658. alignTo = merge(alignTo, { y: y });
  24659. }
  24660. this.group.align(merge(options, {
  24661. width: this.legendWidth,
  24662. height: this.legendHeight,
  24663. verticalAlign: this.proximate ? 'top' : options.verticalAlign
  24664. }), true, alignTo);
  24665. };
  24666. /**
  24667. * Set up the overflow handling by adding navigation with up and down arrows
  24668. * below the legend.
  24669. *
  24670. * @private
  24671. * @function Highcharts.Legend#handleOverflow
  24672. * @param {number} legendHeight
  24673. * @return {number}
  24674. */
  24675. Legend.prototype.handleOverflow = function (legendHeight) {
  24676. var legend = this, chart = this.chart, renderer = chart.renderer, options = this.options, optionsY = options.y, alignTop = options.verticalAlign === 'top', padding = this.padding, spaceHeight = (chart.spacingBox.height +
  24677. (alignTop ? -optionsY : optionsY) - padding), maxHeight = options.maxHeight, clipHeight, clipRect = this.clipRect, navOptions = options.navigation, animation = pick(navOptions.animation, true), arrowSize = navOptions.arrowSize || 12, nav = this.nav, pages = this.pages, lastY, allItems = this.allItems, clipToHeight = function (height) {
  24678. if (typeof height === 'number') {
  24679. clipRect.attr({
  24680. height: height
  24681. });
  24682. }
  24683. else if (clipRect) { // Reset (#5912)
  24684. legend.clipRect = clipRect.destroy();
  24685. legend.contentGroup.clip();
  24686. }
  24687. // useHTML
  24688. if (legend.contentGroup.div) {
  24689. legend.contentGroup.div.style.clip = height ?
  24690. 'rect(' + padding + 'px,9999px,' +
  24691. (padding + height) + 'px,0)' :
  24692. 'auto';
  24693. }
  24694. }, addTracker = function (key) {
  24695. legend[key] = renderer
  24696. .circle(0, 0, arrowSize * 1.3)
  24697. .translate(arrowSize / 2, arrowSize / 2)
  24698. .add(nav);
  24699. if (!chart.styledMode) {
  24700. legend[key].attr('fill', 'rgba(0,0,0,0.0001)');
  24701. }
  24702. return legend[key];
  24703. };
  24704. // Adjust the height
  24705. if (options.layout === 'horizontal' &&
  24706. options.verticalAlign !== 'middle' &&
  24707. !options.floating) {
  24708. spaceHeight /= 2;
  24709. }
  24710. if (maxHeight) {
  24711. spaceHeight = Math.min(spaceHeight, maxHeight);
  24712. }
  24713. // Reset the legend height and adjust the clipping rectangle
  24714. pages.length = 0;
  24715. if (legendHeight > spaceHeight &&
  24716. navOptions.enabled !== false) {
  24717. this.clipHeight = clipHeight =
  24718. Math.max(spaceHeight - 20 - this.titleHeight - padding, 0);
  24719. this.currentPage = pick(this.currentPage, 1);
  24720. this.fullHeight = legendHeight;
  24721. // Fill pages with Y positions so that the top of each a legend item
  24722. // defines the scroll top for each page (#2098)
  24723. allItems.forEach(function (item, i) {
  24724. var y = item._legendItemPos[1], h = Math.round(item.legendItem.getBBox().height), len = pages.length;
  24725. if (!len || (y - pages[len - 1] > clipHeight &&
  24726. (lastY || y) !== pages[len - 1])) {
  24727. pages.push(lastY || y);
  24728. len++;
  24729. }
  24730. // Keep track of which page each item is on
  24731. item.pageIx = len - 1;
  24732. if (lastY) {
  24733. allItems[i - 1].pageIx = len - 1;
  24734. }
  24735. if (i === allItems.length - 1 &&
  24736. y + h - pages[len - 1] > clipHeight &&
  24737. y !== lastY // #2617
  24738. ) {
  24739. pages.push(y);
  24740. item.pageIx = len;
  24741. }
  24742. if (y !== lastY) {
  24743. lastY = y;
  24744. }
  24745. });
  24746. // Only apply clipping if needed. Clipping causes blurred legend in
  24747. // PDF export (#1787)
  24748. if (!clipRect) {
  24749. clipRect = legend.clipRect =
  24750. renderer.clipRect(0, padding, 9999, 0);
  24751. legend.contentGroup.clip(clipRect);
  24752. }
  24753. clipToHeight(clipHeight);
  24754. // Add navigation elements
  24755. if (!nav) {
  24756. this.nav = nav = renderer.g()
  24757. .attr({ zIndex: 1 })
  24758. .add(this.group);
  24759. this.up = renderer
  24760. .symbol('triangle', 0, 0, arrowSize, arrowSize)
  24761. .add(nav);
  24762. addTracker('upTracker')
  24763. .on('click', function () {
  24764. legend.scroll(-1, animation);
  24765. });
  24766. this.pager = renderer.text('', 15, 10)
  24767. .addClass('highcharts-legend-navigation');
  24768. if (!chart.styledMode) {
  24769. this.pager.css(navOptions.style);
  24770. }
  24771. this.pager.add(nav);
  24772. this.down = renderer
  24773. .symbol('triangle-down', 0, 0, arrowSize, arrowSize)
  24774. .add(nav);
  24775. addTracker('downTracker')
  24776. .on('click', function () {
  24777. legend.scroll(1, animation);
  24778. });
  24779. }
  24780. // Set initial position
  24781. legend.scroll(0);
  24782. legendHeight = spaceHeight;
  24783. // Reset
  24784. }
  24785. else if (nav) {
  24786. clipToHeight();
  24787. this.nav = nav.destroy(); // #6322
  24788. this.scrollGroup.attr({
  24789. translateY: 1
  24790. });
  24791. this.clipHeight = 0; // #1379
  24792. }
  24793. return legendHeight;
  24794. };
  24795. /**
  24796. * Scroll the legend by a number of pages.
  24797. *
  24798. * @private
  24799. * @function Highcharts.Legend#scroll
  24800. *
  24801. * @param {number} scrollBy
  24802. * The number of pages to scroll.
  24803. *
  24804. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  24805. * Whether and how to apply animation.
  24806. *
  24807. * @return {void}
  24808. */
  24809. Legend.prototype.scroll = function (scrollBy, animation) {
  24810. var _this = this;
  24811. var chart = this.chart, pages = this.pages, pageCount = pages.length, currentPage = this.currentPage + scrollBy, clipHeight = this.clipHeight, navOptions = this.options.navigation, pager = this.pager, padding = this.padding;
  24812. // When resizing while looking at the last page
  24813. if (currentPage > pageCount) {
  24814. currentPage = pageCount;
  24815. }
  24816. if (currentPage > 0) {
  24817. if (typeof animation !== 'undefined') {
  24818. setAnimation(animation, chart);
  24819. }
  24820. this.nav.attr({
  24821. translateX: padding,
  24822. translateY: clipHeight + this.padding + 7 + this.titleHeight,
  24823. visibility: 'visible'
  24824. });
  24825. [this.up, this.upTracker].forEach(function (elem) {
  24826. elem.attr({
  24827. 'class': currentPage === 1 ?
  24828. 'highcharts-legend-nav-inactive' :
  24829. 'highcharts-legend-nav-active'
  24830. });
  24831. });
  24832. pager.attr({
  24833. text: currentPage + '/' + pageCount
  24834. });
  24835. [this.down, this.downTracker].forEach(function (elem) {
  24836. elem.attr({
  24837. // adjust to text width
  24838. x: 18 + this.pager.getBBox().width,
  24839. 'class': currentPage === pageCount ?
  24840. 'highcharts-legend-nav-inactive' :
  24841. 'highcharts-legend-nav-active'
  24842. });
  24843. }, this);
  24844. if (!chart.styledMode) {
  24845. this.up
  24846. .attr({
  24847. fill: currentPage === 1 ?
  24848. navOptions.inactiveColor :
  24849. navOptions.activeColor
  24850. });
  24851. this.upTracker
  24852. .css({
  24853. cursor: currentPage === 1 ? 'default' : 'pointer'
  24854. });
  24855. this.down
  24856. .attr({
  24857. fill: currentPage === pageCount ?
  24858. navOptions.inactiveColor :
  24859. navOptions.activeColor
  24860. });
  24861. this.downTracker
  24862. .css({
  24863. cursor: currentPage === pageCount ?
  24864. 'default' :
  24865. 'pointer'
  24866. });
  24867. }
  24868. this.scrollOffset = -pages[currentPage - 1] + this.initialItemY;
  24869. this.scrollGroup.animate({
  24870. translateY: this.scrollOffset
  24871. });
  24872. this.currentPage = currentPage;
  24873. this.positionCheckboxes();
  24874. // Fire event after scroll animation is complete
  24875. var animOptions = animObject(pick(animation, chart.renderer.globalAnimation, true));
  24876. syncTimeout(function () {
  24877. fireEvent(_this, 'afterScroll', { currentPage: currentPage });
  24878. }, animOptions.duration || 0);
  24879. }
  24880. };
  24881. return Legend;
  24882. }());
  24883. // Workaround for #2030, horizontal legend items not displaying in IE11 Preview,
  24884. // and for #2580, a similar drawing flaw in Firefox 26.
  24885. // Explore if there's a general cause for this. The problem may be related
  24886. // to nested group elements, as the legend item texts are within 4 group
  24887. // elements.
  24888. if (/Trident\/7\.0/.test(win.navigator && win.navigator.userAgent) ||
  24889. isFirefox) {
  24890. wrap(Legend.prototype, 'positionItem', function (proceed, item) {
  24891. var legend = this,
  24892. // If chart destroyed in sync, this is undefined (#2030)
  24893. runPositionItem = function () {
  24894. if (item._legendItemPos) {
  24895. proceed.call(legend, item);
  24896. }
  24897. };
  24898. // Do it now, for export and to get checkbox placement
  24899. runPositionItem();
  24900. // Do it after to work around the core issue
  24901. if (!legend.bubbleLegend) {
  24902. setTimeout(runPositionItem);
  24903. }
  24904. });
  24905. }
  24906. H.Legend = Legend;
  24907. return H.Legend;
  24908. });
  24909. _registerModule(_modules, 'parts/Chart.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/Legend.js'], _modules['parts/MSPointer.js'], _modules['parts/Options.js'], _modules['parts/Pointer.js'], _modules['parts/Time.js'], _modules['parts/Utilities.js']], function (Axis, H, Legend, MSPointer, O, Pointer, Time, U) {
  24910. /* *
  24911. *
  24912. * (c) 2010-2020 Torstein Honsi
  24913. *
  24914. * License: www.highcharts.com/license
  24915. *
  24916. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  24917. *
  24918. * */
  24919. var charts = H.charts, doc = H.doc, seriesTypes = H.seriesTypes, win = H.win;
  24920. var defaultOptions = O.defaultOptions;
  24921. var addEvent = U.addEvent, animate = U.animate, animObject = U.animObject, attr = U.attr, createElement = U.createElement, css = U.css, defined = U.defined, discardElement = U.discardElement, erase = U.erase, error = U.error, extend = U.extend, find = U.find, fireEvent = U.fireEvent, getStyle = U.getStyle, isArray = U.isArray, isFunction = U.isFunction, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, merge = U.merge, numberFormat = U.numberFormat, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, relativeLength = U.relativeLength, removeEvent = U.removeEvent, setAnimation = U.setAnimation, splat = U.splat, syncTimeout = U.syncTimeout, uniqueKey = U.uniqueKey;
  24922. /**
  24923. * Callback for chart constructors.
  24924. *
  24925. * @callback Highcharts.ChartCallbackFunction
  24926. *
  24927. * @param {Highcharts.Chart} chart
  24928. * Created chart.
  24929. */
  24930. /**
  24931. * Format a number and return a string based on input settings.
  24932. *
  24933. * @callback Highcharts.NumberFormatterCallbackFunction
  24934. *
  24935. * @param {number} number
  24936. * The input number to format.
  24937. *
  24938. * @param {number} decimals
  24939. * The amount of decimals. A value of -1 preserves the amount in the
  24940. * input number.
  24941. *
  24942. * @param {string} [decimalPoint]
  24943. * The decimal point, defaults to the one given in the lang options, or
  24944. * a dot.
  24945. *
  24946. * @param {string} [thousandsSep]
  24947. * The thousands separator, defaults to the one given in the lang
  24948. * options, or a space character.
  24949. *
  24950. * @return {string} The formatted number.
  24951. */
  24952. /**
  24953. * The chart title. The title has an `update` method that allows modifying the
  24954. * options directly or indirectly via `chart.update`.
  24955. *
  24956. * @interface Highcharts.TitleObject
  24957. * @extends Highcharts.SVGElement
  24958. */ /**
  24959. * Modify options for the title.
  24960. *
  24961. * @function Highcharts.TitleObject#update
  24962. *
  24963. * @param {Highcharts.TitleOptions} titleOptions
  24964. * Options to modify.
  24965. *
  24966. * @param {boolean} [redraw=true]
  24967. * Whether to redraw the chart after the title is altered. If doing more
  24968. * operations on the chart, it is a good idea to set redraw to false and
  24969. * call {@link Chart#redraw} after.
  24970. */
  24971. /**
  24972. * The chart subtitle. The subtitle has an `update` method that
  24973. * allows modifying the options directly or indirectly via
  24974. * `chart.update`.
  24975. *
  24976. * @interface Highcharts.SubtitleObject
  24977. * @extends Highcharts.SVGElement
  24978. */ /**
  24979. * Modify options for the subtitle.
  24980. *
  24981. * @function Highcharts.SubtitleObject#update
  24982. *
  24983. * @param {Highcharts.SubtitleOptions} subtitleOptions
  24984. * Options to modify.
  24985. *
  24986. * @param {boolean} [redraw=true]
  24987. * Whether to redraw the chart after the subtitle is altered. If doing
  24988. * more operations on the chart, it is a good idea to set redraw to false
  24989. * and call {@link Chart#redraw} after.
  24990. */
  24991. /**
  24992. * The chart caption. The caption has an `update` method that
  24993. * allows modifying the options directly or indirectly via
  24994. * `chart.update`.
  24995. *
  24996. * @interface Highcharts.CaptionObject
  24997. * @extends Highcharts.SVGElement
  24998. */ /**
  24999. * Modify options for the caption.
  25000. *
  25001. * @function Highcharts.CaptionObject#update
  25002. *
  25003. * @param {Highcharts.CaptionOptions} captionOptions
  25004. * Options to modify.
  25005. *
  25006. * @param {boolean} [redraw=true]
  25007. * Whether to redraw the chart after the caption is altered. If doing
  25008. * more operations on the chart, it is a good idea to set redraw to false
  25009. * and call {@link Chart#redraw} after.
  25010. */
  25011. var marginNames = H.marginNames;
  25012. /* eslint-disable no-invalid-this, valid-jsdoc */
  25013. /**
  25014. * The Chart class. The recommended constructor is {@link Highcharts#chart}.
  25015. *
  25016. * @example
  25017. * var chart = Highcharts.chart('container', {
  25018. * title: {
  25019. * text: 'My chart'
  25020. * },
  25021. * series: [{
  25022. * data: [1, 3, 2, 4]
  25023. * }]
  25024. * })
  25025. *
  25026. * @class
  25027. * @name Highcharts.Chart
  25028. *
  25029. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  25030. * The DOM element to render to, or its id.
  25031. *
  25032. * @param {Highcharts.Options} options
  25033. * The chart options structure.
  25034. *
  25035. * @param {Highcharts.ChartCallbackFunction} [callback]
  25036. * Function to run when the chart has loaded and and all external images
  25037. * are loaded. Defining a
  25038. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  25039. * handler is equivalent.
  25040. */
  25041. var Chart = /** @class */ (function () {
  25042. function Chart(a, b, c) {
  25043. this.axes = void 0;
  25044. this.axisOffset = void 0;
  25045. this.bounds = void 0;
  25046. this.chartHeight = void 0;
  25047. this.chartWidth = void 0;
  25048. this.clipBox = void 0;
  25049. this.colorCounter = void 0;
  25050. this.container = void 0;
  25051. this.index = void 0;
  25052. this.isResizing = void 0;
  25053. this.labelCollectors = void 0;
  25054. this.legend = void 0;
  25055. this.margin = void 0;
  25056. this.numberFormatter = void 0;
  25057. this.options = void 0;
  25058. this.plotBox = void 0;
  25059. this.plotHeight = void 0;
  25060. this.plotLeft = void 0;
  25061. this.plotTop = void 0;
  25062. this.plotWidth = void 0;
  25063. this.pointCount = void 0;
  25064. this.pointer = void 0;
  25065. this.renderer = void 0;
  25066. this.renderTo = void 0;
  25067. this.series = void 0;
  25068. this.spacing = void 0;
  25069. this.spacingBox = void 0;
  25070. this.symbolCounter = void 0;
  25071. this.time = void 0;
  25072. this.titleOffset = void 0;
  25073. this.userOptions = void 0;
  25074. this.xAxis = void 0;
  25075. this.yAxis = void 0;
  25076. this.getArgs(a, b, c);
  25077. }
  25078. /* *
  25079. *
  25080. * Functions
  25081. *
  25082. * */
  25083. /**
  25084. * Handle the arguments passed to the constructor.
  25085. *
  25086. * @private
  25087. * @function Highcharts.Chart#getArgs
  25088. *
  25089. * @param {...Array<*>} arguments
  25090. * All arguments for the constructor.
  25091. *
  25092. * @fires Highcharts.Chart#event:init
  25093. * @fires Highcharts.Chart#event:afterInit
  25094. */
  25095. Chart.prototype.getArgs = function (a, b, c) {
  25096. // Remove the optional first argument, renderTo, and
  25097. // set it on this.
  25098. if (isString(a) || a.nodeName) {
  25099. this.renderTo = a;
  25100. this.init(b, c);
  25101. }
  25102. else {
  25103. this.init(a, b);
  25104. }
  25105. };
  25106. /**
  25107. * Overridable function that initializes the chart. The constructor's
  25108. * arguments are passed on directly.
  25109. *
  25110. * @function Highcharts.Chart#init
  25111. *
  25112. * @param {Highcharts.Options} userOptions
  25113. * Custom options.
  25114. *
  25115. * @param {Function} [callback]
  25116. * Function to run when the chart has loaded and and all external
  25117. * images are loaded.
  25118. *
  25119. * @return {void}
  25120. *
  25121. * @fires Highcharts.Chart#event:init
  25122. * @fires Highcharts.Chart#event:afterInit
  25123. */
  25124. Chart.prototype.init = function (userOptions, callback) {
  25125. // Handle regular options
  25126. var options,
  25127. // skip merging data points to increase performance
  25128. seriesOptions = userOptions.series, userPlotOptions = userOptions.plotOptions || {};
  25129. // Fire the event with a default function
  25130. fireEvent(this, 'init', { args: arguments }, function () {
  25131. userOptions.series = null;
  25132. options = merge(defaultOptions, userOptions); // do the merge
  25133. var optionsChart = options.chart || {};
  25134. // Override (by copy of user options) or clear tooltip options
  25135. // in chart.options.plotOptions (#6218)
  25136. objectEach(options.plotOptions, function (typeOptions, type) {
  25137. if (isObject(typeOptions)) { // #8766
  25138. typeOptions.tooltip = (userPlotOptions[type] && // override by copy:
  25139. merge(userPlotOptions[type].tooltip)) || void 0; // or clear
  25140. }
  25141. });
  25142. // User options have higher priority than default options
  25143. // (#6218). In case of exporting: path is changed
  25144. options.tooltip.userOptions = (userOptions.chart &&
  25145. userOptions.chart.forExport &&
  25146. userOptions.tooltip.userOptions) || userOptions.tooltip;
  25147. // set back the series data
  25148. options.series = userOptions.series = seriesOptions;
  25149. /**
  25150. * The original options given to the constructor or a chart factory
  25151. * like {@link Highcharts.chart} and {@link Highcharts.stockChart}.
  25152. *
  25153. * @name Highcharts.Chart#userOptions
  25154. * @type {Highcharts.Options}
  25155. */
  25156. this.userOptions = userOptions;
  25157. var chartEvents = optionsChart.events;
  25158. this.margin = [];
  25159. this.spacing = [];
  25160. // Pixel data bounds for touch zoom
  25161. this.bounds = { h: {}, v: {} };
  25162. // An array of functions that returns labels that should be
  25163. // considered for anti-collision
  25164. this.labelCollectors = [];
  25165. this.callback = callback;
  25166. this.isResizing = 0;
  25167. /**
  25168. * The options structure for the chart after merging
  25169. * {@link #defaultOptions} and {@link #userOptions}. It contains
  25170. * members for the sub elements like series, legend, tooltip etc.
  25171. *
  25172. * @name Highcharts.Chart#options
  25173. * @type {Highcharts.Options}
  25174. */
  25175. this.options = options;
  25176. /**
  25177. * All the axes in the chart.
  25178. *
  25179. * @see Highcharts.Chart.xAxis
  25180. * @see Highcharts.Chart.yAxis
  25181. *
  25182. * @name Highcharts.Chart#axes
  25183. * @type {Array<Highcharts.Axis>}
  25184. */
  25185. this.axes = [];
  25186. /**
  25187. * All the current series in the chart.
  25188. *
  25189. * @name Highcharts.Chart#series
  25190. * @type {Array<Highcharts.Series>}
  25191. */
  25192. this.series = [];
  25193. /**
  25194. * The `Time` object associated with the chart. Since v6.0.5,
  25195. * time settings can be applied individually for each chart. If
  25196. * no individual settings apply, the `Time` object is shared by
  25197. * all instances.
  25198. *
  25199. * @name Highcharts.Chart#time
  25200. * @type {Highcharts.Time}
  25201. */
  25202. this.time =
  25203. userOptions.time && Object.keys(userOptions.time).length ?
  25204. new Time(userOptions.time) :
  25205. H.time;
  25206. /**
  25207. * Callback function to override the default function that formats
  25208. * all the numbers in the chart. Returns a string with the formatted
  25209. * number.
  25210. *
  25211. * @name Highcharts.Chart#numberFormatter
  25212. * @type {Highcharts.NumberFormatterCallbackFunction}
  25213. */
  25214. this.numberFormatter = optionsChart.numberFormatter || numberFormat;
  25215. /**
  25216. * Whether the chart is in styled mode, meaning all presentatinoal
  25217. * attributes are avoided.
  25218. *
  25219. * @name Highcharts.Chart#styledMode
  25220. * @type {boolean}
  25221. */
  25222. this.styledMode = optionsChart.styledMode;
  25223. this.hasCartesianSeries = optionsChart.showAxes;
  25224. var chart = this;
  25225. /**
  25226. * Index position of the chart in the {@link Highcharts#charts}
  25227. * property.
  25228. *
  25229. * @name Highcharts.Chart#index
  25230. * @type {number}
  25231. * @readonly
  25232. */
  25233. chart.index = charts.length; // Add the chart to the global lookup
  25234. charts.push(chart);
  25235. H.chartCount++;
  25236. // Chart event handlers
  25237. if (chartEvents) {
  25238. objectEach(chartEvents, function (event, eventType) {
  25239. if (isFunction(event)) {
  25240. addEvent(chart, eventType, event);
  25241. }
  25242. });
  25243. }
  25244. /**
  25245. * A collection of the X axes in the chart.
  25246. *
  25247. * @name Highcharts.Chart#xAxis
  25248. * @type {Array<Highcharts.Axis>}
  25249. */
  25250. chart.xAxis = [];
  25251. /**
  25252. * A collection of the Y axes in the chart.
  25253. *
  25254. * @name Highcharts.Chart#yAxis
  25255. * @type {Array<Highcharts.Axis>}
  25256. *
  25257. * @todo
  25258. * Make events official: Fire the event `afterInit`.
  25259. */
  25260. chart.yAxis = [];
  25261. chart.pointCount = chart.colorCounter = chart.symbolCounter = 0;
  25262. // Fire after init but before first render, before axes and series
  25263. // have been initialized.
  25264. fireEvent(chart, 'afterInit');
  25265. chart.firstRender();
  25266. });
  25267. };
  25268. /**
  25269. * Internal function to unitialize an individual series.
  25270. *
  25271. * @private
  25272. * @function Highcharts.Chart#initSeries
  25273. */
  25274. Chart.prototype.initSeries = function (options) {
  25275. var chart = this, optionsChart = chart.options.chart, type = (options.type ||
  25276. optionsChart.type ||
  25277. optionsChart.defaultSeriesType), series, Constr = seriesTypes[type];
  25278. // No such series type
  25279. if (!Constr) {
  25280. error(17, true, chart, { missingModuleFor: type });
  25281. }
  25282. series = new Constr();
  25283. series.init(this, options);
  25284. return series;
  25285. };
  25286. /**
  25287. * Internal function to set data for all series with enabled sorting.
  25288. *
  25289. * @private
  25290. * @function Highcharts.Chart#setSeriesData
  25291. */
  25292. Chart.prototype.setSeriesData = function () {
  25293. this.getSeriesOrderByLinks().forEach(function (series) {
  25294. // We need to set data for series with sorting after series init
  25295. if (!series.points && !series.data && series.enabledDataSorting) {
  25296. series.setData(series.options.data, false);
  25297. }
  25298. });
  25299. };
  25300. /**
  25301. * Sort and return chart series in order depending on the number of linked
  25302. * series.
  25303. *
  25304. * @private
  25305. * @function Highcharts.Series#getSeriesOrderByLinks
  25306. * @return {Array<Highcharts.Series>}
  25307. */
  25308. Chart.prototype.getSeriesOrderByLinks = function () {
  25309. return this.series.concat().sort(function (a, b) {
  25310. if (a.linkedSeries.length || b.linkedSeries.length) {
  25311. return b.linkedSeries.length - a.linkedSeries.length;
  25312. }
  25313. return 0;
  25314. });
  25315. };
  25316. /**
  25317. * Order all series above a given index. When series are added and ordered
  25318. * by configuration, only the last series is handled (#248, #1123, #2456,
  25319. * #6112). This function is called on series initialization and destroy.
  25320. *
  25321. * @private
  25322. * @function Highcharts.Series#orderSeries
  25323. * @param {number} [fromIndex]
  25324. * If this is given, only the series above this index are handled.
  25325. */
  25326. Chart.prototype.orderSeries = function (fromIndex) {
  25327. var series = this.series, i = fromIndex || 0;
  25328. for (; i < series.length; i++) {
  25329. if (series[i]) {
  25330. /**
  25331. * Contains the series' index in the `Chart.series` array.
  25332. *
  25333. * @name Highcharts.Series#index
  25334. * @type {number}
  25335. * @readonly
  25336. */
  25337. series[i].index = i;
  25338. series[i].name = series[i].getName();
  25339. }
  25340. }
  25341. };
  25342. /**
  25343. * Check whether a given point is within the plot area.
  25344. *
  25345. * @function Highcharts.Chart#isInsidePlot
  25346. *
  25347. * @param {number} plotX
  25348. * Pixel x relative to the plot area.
  25349. *
  25350. * @param {number} plotY
  25351. * Pixel y relative to the plot area.
  25352. *
  25353. * @param {boolean} [inverted]
  25354. * Whether the chart is inverted.
  25355. *
  25356. * @return {boolean}
  25357. * Returns true if the given point is inside the plot area.
  25358. */
  25359. Chart.prototype.isInsidePlot = function (plotX, plotY, inverted) {
  25360. var x = inverted ? plotY : plotX, y = inverted ? plotX : plotY, e = {
  25361. x: x,
  25362. y: y,
  25363. isInsidePlot: x >= 0 &&
  25364. x <= this.plotWidth &&
  25365. y >= 0 &&
  25366. y <= this.plotHeight
  25367. };
  25368. fireEvent(this, 'afterIsInsidePlot', e);
  25369. return e.isInsidePlot;
  25370. };
  25371. /**
  25372. * Redraw the chart after changes have been done to the data, axis extremes
  25373. * chart size or chart elements. All methods for updating axes, series or
  25374. * points have a parameter for redrawing the chart. This is `true` by
  25375. * default. But in many cases you want to do more than one operation on the
  25376. * chart before redrawing, for example add a number of points. In those
  25377. * cases it is a waste of resources to redraw the chart for each new point
  25378. * added. So you add the points and call `chart.redraw()` after.
  25379. *
  25380. * @function Highcharts.Chart#redraw
  25381. *
  25382. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  25383. * If or how to apply animation to the redraw.
  25384. *
  25385. * @fires Highcharts.Chart#event:afterSetExtremes
  25386. * @fires Highcharts.Chart#event:beforeRedraw
  25387. * @fires Highcharts.Chart#event:predraw
  25388. * @fires Highcharts.Chart#event:redraw
  25389. * @fires Highcharts.Chart#event:render
  25390. * @fires Highcharts.Chart#event:updatedData
  25391. */
  25392. Chart.prototype.redraw = function (animation) {
  25393. fireEvent(this, 'beforeRedraw');
  25394. var chart = this, axes = chart.axes, series = chart.series, pointer = chart.pointer, legend = chart.legend, legendUserOptions = chart.userOptions.legend, redrawLegend = chart.isDirtyLegend, hasStackedSeries, hasDirtyStacks, hasCartesianSeries = chart.hasCartesianSeries, isDirtyBox = chart.isDirtyBox, i, serie, renderer = chart.renderer, isHiddenChart = renderer.isHidden(), afterRedraw = [];
  25395. // Handle responsive rules, not only on resize (#6130)
  25396. if (chart.setResponsive) {
  25397. chart.setResponsive(false);
  25398. }
  25399. // Set the global animation. When chart.hasRendered is not true, the
  25400. // redraw call comes from a responsive rule and animation should not
  25401. // occur.
  25402. setAnimation(chart.hasRendered ? animation : false, chart);
  25403. if (isHiddenChart) {
  25404. chart.temporaryDisplay();
  25405. }
  25406. // Adjust title layout (reflow multiline text)
  25407. chart.layOutTitles();
  25408. // link stacked series
  25409. i = series.length;
  25410. while (i--) {
  25411. serie = series[i];
  25412. if (serie.options.stacking) {
  25413. hasStackedSeries = true;
  25414. if (serie.isDirty) {
  25415. hasDirtyStacks = true;
  25416. break;
  25417. }
  25418. }
  25419. }
  25420. if (hasDirtyStacks) { // mark others as dirty
  25421. i = series.length;
  25422. while (i--) {
  25423. serie = series[i];
  25424. if (serie.options.stacking) {
  25425. serie.isDirty = true;
  25426. }
  25427. }
  25428. }
  25429. // Handle updated data in the series
  25430. series.forEach(function (serie) {
  25431. if (serie.isDirty) {
  25432. if (serie.options.legendType === 'point') {
  25433. if (serie.updateTotals) {
  25434. serie.updateTotals();
  25435. }
  25436. redrawLegend = true;
  25437. }
  25438. else if (legendUserOptions &&
  25439. (legendUserOptions.labelFormatter ||
  25440. legendUserOptions.labelFormat)) {
  25441. redrawLegend = true; // #2165
  25442. }
  25443. }
  25444. if (serie.isDirtyData) {
  25445. fireEvent(serie, 'updatedData');
  25446. }
  25447. });
  25448. // handle added or removed series
  25449. if (redrawLegend && legend && legend.options.enabled) {
  25450. // draw legend graphics
  25451. legend.render();
  25452. chart.isDirtyLegend = false;
  25453. }
  25454. // reset stacks
  25455. if (hasStackedSeries) {
  25456. chart.getStacks();
  25457. }
  25458. if (hasCartesianSeries) {
  25459. // set axes scales
  25460. axes.forEach(function (axis) {
  25461. // Don't do setScale again if we're only resizing. Regression
  25462. // #13507. But we need it after chart.update (responsive), as
  25463. // axis is initialized again (#12137).
  25464. if (!chart.isResizing || !isNumber(axis.min)) {
  25465. axis.updateNames();
  25466. axis.setScale();
  25467. }
  25468. });
  25469. }
  25470. chart.getMargins(); // #3098
  25471. if (hasCartesianSeries) {
  25472. // If one axis is dirty, all axes must be redrawn (#792, #2169)
  25473. axes.forEach(function (axis) {
  25474. if (axis.isDirty) {
  25475. isDirtyBox = true;
  25476. }
  25477. });
  25478. // redraw axes
  25479. axes.forEach(function (axis) {
  25480. // Fire 'afterSetExtremes' only if extremes are set
  25481. var key = axis.min + ',' + axis.max;
  25482. if (axis.extKey !== key) { // #821, #4452
  25483. axis.extKey = key;
  25484. // prevent a recursive call to chart.redraw() (#1119)
  25485. afterRedraw.push(function () {
  25486. fireEvent(axis, 'afterSetExtremes', extend(axis.eventArgs, axis.getExtremes())); // #747, #751
  25487. delete axis.eventArgs;
  25488. });
  25489. }
  25490. if (isDirtyBox || hasStackedSeries) {
  25491. axis.redraw();
  25492. }
  25493. });
  25494. }
  25495. // the plot areas size has changed
  25496. if (isDirtyBox) {
  25497. chart.drawChartBox();
  25498. }
  25499. // Fire an event before redrawing series, used by the boost module to
  25500. // clear previous series renderings.
  25501. fireEvent(chart, 'predraw');
  25502. // redraw affected series
  25503. series.forEach(function (serie) {
  25504. if ((isDirtyBox || serie.isDirty) && serie.visible) {
  25505. serie.redraw();
  25506. }
  25507. // Set it here, otherwise we will have unlimited 'updatedData' calls
  25508. // for a hidden series after setData(). Fixes #6012
  25509. serie.isDirtyData = false;
  25510. });
  25511. // move tooltip or reset
  25512. if (pointer) {
  25513. pointer.reset(true);
  25514. }
  25515. // redraw if canvas
  25516. renderer.draw();
  25517. // Fire the events
  25518. fireEvent(chart, 'redraw');
  25519. fireEvent(chart, 'render');
  25520. if (isHiddenChart) {
  25521. chart.temporaryDisplay(true);
  25522. }
  25523. // Fire callbacks that are put on hold until after the redraw
  25524. afterRedraw.forEach(function (callback) {
  25525. callback.call();
  25526. });
  25527. };
  25528. /**
  25529. * Get an axis, series or point object by `id` as given in the configuration
  25530. * options. Returns `undefined` if no item is found.
  25531. *
  25532. * @sample highcharts/plotoptions/series-id/
  25533. * Get series by id
  25534. *
  25535. * @function Highcharts.Chart#get
  25536. *
  25537. * @param {string} id
  25538. * The id as given in the configuration options.
  25539. *
  25540. * @return {Highcharts.Axis|Highcharts.Series|Highcharts.Point|undefined}
  25541. * The retrieved item.
  25542. */
  25543. Chart.prototype.get = function (id) {
  25544. var ret, series = this.series, i;
  25545. /**
  25546. * @private
  25547. * @param {Highcharts.Axis|Highcharts.Series} item
  25548. * @return {boolean}
  25549. */
  25550. function itemById(item) {
  25551. return (item.id === id ||
  25552. (item.options && item.options.id === id));
  25553. }
  25554. ret =
  25555. // Search axes
  25556. find(this.axes, itemById) ||
  25557. // Search series
  25558. find(this.series, itemById);
  25559. // Search points
  25560. for (i = 0; !ret && i < series.length; i++) {
  25561. ret = find(series[i].points || [], itemById);
  25562. }
  25563. return ret;
  25564. };
  25565. /**
  25566. * Create the Axis instances based on the config options.
  25567. *
  25568. * @private
  25569. * @function Highcharts.Chart#getAxes
  25570. * @fires Highcharts.Chart#event:afterGetAxes
  25571. * @fires Highcharts.Chart#event:getAxes
  25572. */
  25573. Chart.prototype.getAxes = function () {
  25574. var chart = this, options = this.options, xAxisOptions = options.xAxis = splat(options.xAxis || {}), yAxisOptions = options.yAxis = splat(options.yAxis || {}), optionsArray;
  25575. fireEvent(this, 'getAxes');
  25576. // make sure the options are arrays and add some members
  25577. xAxisOptions.forEach(function (axis, i) {
  25578. axis.index = i;
  25579. axis.isX = true;
  25580. });
  25581. yAxisOptions.forEach(function (axis, i) {
  25582. axis.index = i;
  25583. });
  25584. // concatenate all axis options into one array
  25585. optionsArray = xAxisOptions.concat(yAxisOptions);
  25586. optionsArray.forEach(function (axisOptions) {
  25587. new Axis(chart, axisOptions); // eslint-disable-line no-new
  25588. });
  25589. fireEvent(this, 'afterGetAxes');
  25590. };
  25591. /**
  25592. * Returns an array of all currently selected points in the chart. Points
  25593. * can be selected by clicking or programmatically by the
  25594. * {@link Highcharts.Point#select}
  25595. * function.
  25596. *
  25597. * @sample highcharts/plotoptions/series-allowpointselect-line/
  25598. * Get selected points
  25599. *
  25600. * @function Highcharts.Chart#getSelectedPoints
  25601. *
  25602. * @return {Array<Highcharts.Point>}
  25603. * The currently selected points.
  25604. */
  25605. Chart.prototype.getSelectedPoints = function () {
  25606. var points = [];
  25607. this.series.forEach(function (serie) {
  25608. // For one-to-one points inspect series.data in order to retrieve
  25609. // points outside the visible range (#6445). For grouped data,
  25610. // inspect the generated series.points.
  25611. points = points.concat(serie.getPointsCollection().filter(function (point) {
  25612. return pick(point.selectedStaging, point.selected);
  25613. }));
  25614. });
  25615. return points;
  25616. };
  25617. /**
  25618. * Returns an array of all currently selected series in the chart. Series
  25619. * can be selected either programmatically by the
  25620. * {@link Highcharts.Series#select}
  25621. * function or by checking the checkbox next to the legend item if
  25622. * [series.showCheckBox](https://api.highcharts.com/highcharts/plotOptions.series.showCheckbox)
  25623. * is true.
  25624. *
  25625. * @sample highcharts/members/chart-getselectedseries/
  25626. * Get selected series
  25627. *
  25628. * @function Highcharts.Chart#getSelectedSeries
  25629. *
  25630. * @return {Array<Highcharts.Series>}
  25631. * The currently selected series.
  25632. */
  25633. Chart.prototype.getSelectedSeries = function () {
  25634. return this.series.filter(function (serie) {
  25635. return serie.selected;
  25636. });
  25637. };
  25638. /**
  25639. * Set a new title or subtitle for the chart.
  25640. *
  25641. * @sample highcharts/members/chart-settitle/
  25642. * Set title text and styles
  25643. *
  25644. * @function Highcharts.Chart#setTitle
  25645. *
  25646. * @param {Highcharts.TitleOptions} [titleOptions]
  25647. * New title options. The title text itself is set by the
  25648. * `titleOptions.text` property.
  25649. *
  25650. * @param {Highcharts.SubtitleOptions} [subtitleOptions]
  25651. * New subtitle options. The subtitle text itself is set by the
  25652. * `subtitleOptions.text` property.
  25653. *
  25654. * @param {boolean} [redraw]
  25655. * Whether to redraw the chart or wait for a later call to
  25656. * `chart.redraw()`.
  25657. */
  25658. Chart.prototype.setTitle = function (titleOptions, subtitleOptions, redraw) {
  25659. this.applyDescription('title', titleOptions);
  25660. this.applyDescription('subtitle', subtitleOptions);
  25661. // The initial call also adds the caption. On update, chart.update will
  25662. // relay to Chart.setCaption.
  25663. this.applyDescription('caption', void 0);
  25664. this.layOutTitles(redraw);
  25665. };
  25666. /**
  25667. * Apply a title, subtitle or caption for the chart
  25668. *
  25669. * @private
  25670. * @function Highcharts.Chart#applyDescription
  25671. * @param name {string}
  25672. * Either title, subtitle or caption
  25673. * @param {Highcharts.TitleOptions|Highcharts.SubtitleOptions|Highcharts.CaptionOptions|undefined} explicitOptions
  25674. * The options to set, will be merged with default options.
  25675. */
  25676. Chart.prototype.applyDescription = function (name, explicitOptions) {
  25677. var chart = this;
  25678. // Default style
  25679. var style = name === 'title' ? {
  25680. color: '#333333',
  25681. fontSize: this.options.isStock ? '16px' : '18px' // #2944
  25682. } : {
  25683. color: '#666666'
  25684. };
  25685. // Merge default options with explicit options
  25686. var options = this.options[name] = merge(
  25687. // Default styles
  25688. (!this.styledMode && { style: style }), this.options[name], explicitOptions);
  25689. var elem = this[name];
  25690. if (elem && explicitOptions) {
  25691. this[name] = elem = elem.destroy(); // remove old
  25692. }
  25693. if (options && !elem) {
  25694. elem = this.renderer.text(options.text, 0, 0, options.useHTML)
  25695. .attr({
  25696. align: options.align,
  25697. 'class': 'highcharts-' + name,
  25698. zIndex: options.zIndex || 4
  25699. })
  25700. .add();
  25701. // Update methods, shortcut to Chart.setTitle, Chart.setSubtitle and
  25702. // Chart.setCaption
  25703. elem.update = function (updateOptions) {
  25704. var fn = {
  25705. title: 'setTitle',
  25706. subtitle: 'setSubtitle',
  25707. caption: 'setCaption'
  25708. }[name];
  25709. chart[fn](updateOptions);
  25710. };
  25711. // Presentational
  25712. if (!this.styledMode) {
  25713. elem.css(options.style);
  25714. }
  25715. /**
  25716. * The chart title. The title has an `update` method that allows
  25717. * modifying the options directly or indirectly via
  25718. * `chart.update`.
  25719. *
  25720. * @sample highcharts/members/title-update/
  25721. * Updating titles
  25722. *
  25723. * @name Highcharts.Chart#title
  25724. * @type {Highcharts.TitleObject}
  25725. */
  25726. /**
  25727. * The chart subtitle. The subtitle has an `update` method that
  25728. * allows modifying the options directly or indirectly via
  25729. * `chart.update`.
  25730. *
  25731. * @name Highcharts.Chart#subtitle
  25732. * @type {Highcharts.SubtitleObject}
  25733. */
  25734. this[name] = elem;
  25735. }
  25736. };
  25737. /**
  25738. * Internal function to lay out the chart title, subtitle and caption, and
  25739. * cache the full offset height for use in `getMargins`. The result is
  25740. * stored in `this.titleOffset`.
  25741. *
  25742. * @private
  25743. * @function Highcharts.Chart#layOutTitles
  25744. *
  25745. * @param {boolean} [redraw=true]
  25746. * @fires Highcharts.Chart#event:afterLayOutTitles
  25747. */
  25748. Chart.prototype.layOutTitles = function (redraw) {
  25749. var titleOffset = [0, 0, 0], requiresDirtyBox, renderer = this.renderer, spacingBox = this.spacingBox;
  25750. // Lay out the title and the subtitle respectively
  25751. ['title', 'subtitle', 'caption'].forEach(function (key) {
  25752. var title = this[key], titleOptions = this.options[key], verticalAlign = titleOptions.verticalAlign || 'top', offset = key === 'title' ? -3 :
  25753. // Floating subtitle (#6574)
  25754. verticalAlign === 'top' ? titleOffset[0] + 2 : 0, titleSize, height;
  25755. if (title) {
  25756. if (!this.styledMode) {
  25757. titleSize = titleOptions.style.fontSize;
  25758. }
  25759. titleSize = renderer.fontMetrics(titleSize, title).b;
  25760. title
  25761. .css({
  25762. width: (titleOptions.width ||
  25763. spacingBox.width + (titleOptions.widthAdjust || 0)) + 'px'
  25764. });
  25765. // Skip the cache for HTML (#3481, #11666)
  25766. height = Math.round(title.getBBox(titleOptions.useHTML).height);
  25767. title.align(extend({
  25768. y: verticalAlign === 'bottom' ?
  25769. titleSize :
  25770. offset + titleSize,
  25771. height: height
  25772. }, titleOptions), false, 'spacingBox');
  25773. if (!titleOptions.floating) {
  25774. if (verticalAlign === 'top') {
  25775. titleOffset[0] = Math.ceil(titleOffset[0] +
  25776. height);
  25777. }
  25778. else if (verticalAlign === 'bottom') {
  25779. titleOffset[2] = Math.ceil(titleOffset[2] +
  25780. height);
  25781. }
  25782. }
  25783. }
  25784. }, this);
  25785. // Handle title.margin and caption.margin
  25786. if (titleOffset[0] &&
  25787. (this.options.title.verticalAlign || 'top') === 'top') {
  25788. titleOffset[0] += this.options.title.margin;
  25789. }
  25790. if (titleOffset[2] &&
  25791. this.options.caption.verticalAlign === 'bottom') {
  25792. titleOffset[2] += this.options.caption.margin;
  25793. }
  25794. requiresDirtyBox = (!this.titleOffset ||
  25795. this.titleOffset.join(',') !== titleOffset.join(','));
  25796. // Used in getMargins
  25797. this.titleOffset = titleOffset;
  25798. fireEvent(this, 'afterLayOutTitles');
  25799. if (!this.isDirtyBox && requiresDirtyBox) {
  25800. this.isDirtyBox = this.isDirtyLegend = requiresDirtyBox;
  25801. // Redraw if necessary (#2719, #2744)
  25802. if (this.hasRendered && pick(redraw, true) && this.isDirtyBox) {
  25803. this.redraw();
  25804. }
  25805. }
  25806. };
  25807. /**
  25808. * Internal function to get the chart width and height according to options
  25809. * and container size. Sets {@link Chart.chartWidth} and
  25810. * {@link Chart.chartHeight}.
  25811. *
  25812. * @private
  25813. * @function Highcharts.Chart#getChartSize
  25814. */
  25815. Chart.prototype.getChartSize = function () {
  25816. var chart = this, optionsChart = chart.options.chart, widthOption = optionsChart.width, heightOption = optionsChart.height, renderTo = chart.renderTo;
  25817. // Get inner width and height
  25818. if (!defined(widthOption)) {
  25819. chart.containerWidth = getStyle(renderTo, 'width');
  25820. }
  25821. if (!defined(heightOption)) {
  25822. chart.containerHeight = getStyle(renderTo, 'height');
  25823. }
  25824. /**
  25825. * The current pixel width of the chart.
  25826. *
  25827. * @name Highcharts.Chart#chartWidth
  25828. * @type {number}
  25829. */
  25830. chart.chartWidth = Math.max(// #1393
  25831. 0, widthOption || chart.containerWidth || 600 // #1460
  25832. );
  25833. /**
  25834. * The current pixel height of the chart.
  25835. *
  25836. * @name Highcharts.Chart#chartHeight
  25837. * @type {number}
  25838. */
  25839. chart.chartHeight = Math.max(0, relativeLength(heightOption, chart.chartWidth) ||
  25840. (chart.containerHeight > 1 ?
  25841. chart.containerHeight :
  25842. 400));
  25843. };
  25844. /**
  25845. * If the renderTo element has no offsetWidth, most likely one or more of
  25846. * its parents are hidden. Loop up the DOM tree to temporarily display the
  25847. * parents, then save the original display properties, and when the true
  25848. * size is retrieved, reset them. Used on first render and on redraws.
  25849. *
  25850. * @private
  25851. * @function Highcharts.Chart#temporaryDisplay
  25852. *
  25853. * @param {boolean} [revert]
  25854. * Revert to the saved original styles.
  25855. */
  25856. Chart.prototype.temporaryDisplay = function (revert) {
  25857. var node = this.renderTo, tempStyle;
  25858. if (!revert) {
  25859. while (node && node.style) {
  25860. // When rendering to a detached node, it needs to be temporarily
  25861. // attached in order to read styling and bounding boxes (#5783,
  25862. // #7024).
  25863. if (!doc.body.contains(node) && !node.parentNode) {
  25864. node.hcOrigDetached = true;
  25865. doc.body.appendChild(node);
  25866. }
  25867. if (getStyle(node, 'display', false) === 'none' ||
  25868. node.hcOricDetached) {
  25869. node.hcOrigStyle = {
  25870. display: node.style.display,
  25871. height: node.style.height,
  25872. overflow: node.style.overflow
  25873. };
  25874. tempStyle = {
  25875. display: 'block',
  25876. overflow: 'hidden'
  25877. };
  25878. if (node !== this.renderTo) {
  25879. tempStyle.height = 0;
  25880. }
  25881. css(node, tempStyle);
  25882. // If it still doesn't have an offset width after setting
  25883. // display to block, it probably has an !important priority
  25884. // #2631, 6803
  25885. if (!node.offsetWidth) {
  25886. node.style.setProperty('display', 'block', 'important');
  25887. }
  25888. }
  25889. node = node.parentNode;
  25890. if (node === doc.body) {
  25891. break;
  25892. }
  25893. }
  25894. }
  25895. else {
  25896. while (node && node.style) {
  25897. if (node.hcOrigStyle) {
  25898. css(node, node.hcOrigStyle);
  25899. delete node.hcOrigStyle;
  25900. }
  25901. if (node.hcOrigDetached) {
  25902. doc.body.removeChild(node);
  25903. node.hcOrigDetached = false;
  25904. }
  25905. node = node.parentNode;
  25906. }
  25907. }
  25908. };
  25909. /**
  25910. * Set the {@link Chart.container|chart container's} class name, in
  25911. * addition to `highcharts-container`.
  25912. *
  25913. * @function Highcharts.Chart#setClassName
  25914. *
  25915. * @param {string} [className]
  25916. * The additional class name.
  25917. */
  25918. Chart.prototype.setClassName = function (className) {
  25919. this.container.className = 'highcharts-container ' + (className || '');
  25920. };
  25921. /**
  25922. * Get the containing element, determine the size and create the inner
  25923. * container div to hold the chart.
  25924. *
  25925. * @private
  25926. * @function Highcharts.Chart#afterGetContainer
  25927. * @fires Highcharts.Chart#event:afterGetContainer
  25928. */
  25929. Chart.prototype.getContainer = function () {
  25930. var chart = this, container, options = chart.options, optionsChart = options.chart, chartWidth, chartHeight, renderTo = chart.renderTo, indexAttrName = 'data-highcharts-chart', oldChartIndex, Ren, containerId = uniqueKey(), containerStyle, key;
  25931. if (!renderTo) {
  25932. chart.renderTo = renderTo =
  25933. optionsChart.renderTo;
  25934. }
  25935. if (isString(renderTo)) {
  25936. chart.renderTo = renderTo =
  25937. doc.getElementById(renderTo);
  25938. }
  25939. // Display an error if the renderTo is wrong
  25940. if (!renderTo) {
  25941. error(13, true, chart);
  25942. }
  25943. // If the container already holds a chart, destroy it. The check for
  25944. // hasRendered is there because web pages that are saved to disk from
  25945. // the browser, will preserve the data-highcharts-chart attribute and
  25946. // the SVG contents, but not an interactive chart. So in this case,
  25947. // charts[oldChartIndex] will point to the wrong chart if any (#2609).
  25948. oldChartIndex = pInt(attr(renderTo, indexAttrName));
  25949. if (isNumber(oldChartIndex) &&
  25950. charts[oldChartIndex] &&
  25951. charts[oldChartIndex].hasRendered) {
  25952. charts[oldChartIndex].destroy();
  25953. }
  25954. // Make a reference to the chart from the div
  25955. attr(renderTo, indexAttrName, chart.index);
  25956. // remove previous chart
  25957. renderTo.innerHTML = '';
  25958. // If the container doesn't have an offsetWidth, it has or is a child of
  25959. // a node that has display:none. We need to temporarily move it out to a
  25960. // visible state to determine the size, else the legend and tooltips
  25961. // won't render properly. The skipClone option is used in sparklines as
  25962. // a micro optimization, saving about 1-2 ms each chart.
  25963. if (!optionsChart.skipClone && !renderTo.offsetWidth) {
  25964. chart.temporaryDisplay();
  25965. }
  25966. // get the width and height
  25967. chart.getChartSize();
  25968. chartWidth = chart.chartWidth;
  25969. chartHeight = chart.chartHeight;
  25970. // Allow table cells and flex-boxes to shrink without the chart blocking
  25971. // them out (#6427)
  25972. css(renderTo, { overflow: 'hidden' });
  25973. // Create the inner container
  25974. if (!chart.styledMode) {
  25975. containerStyle = extend({
  25976. position: 'relative',
  25977. // needed for context menu (avoidscrollbars) and content
  25978. // overflow in IE
  25979. overflow: 'hidden',
  25980. width: chartWidth + 'px',
  25981. height: chartHeight + 'px',
  25982. textAlign: 'left',
  25983. lineHeight: 'normal',
  25984. zIndex: 0,
  25985. '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
  25986. userSelect: 'none' // #13503
  25987. }, optionsChart.style);
  25988. }
  25989. /**
  25990. * The containing HTML element of the chart. The container is
  25991. * dynamically inserted into the element given as the `renderTo`
  25992. * parameter in the {@link Highcharts#chart} constructor.
  25993. *
  25994. * @name Highcharts.Chart#container
  25995. * @type {Highcharts.HTMLDOMElement}
  25996. */
  25997. container = createElement('div', {
  25998. id: containerId
  25999. }, containerStyle, renderTo);
  26000. chart.container = container;
  26001. // cache the cursor (#1650)
  26002. chart._cursor = container.style.cursor;
  26003. // Initialize the renderer
  26004. Ren = H[optionsChart.renderer] || H.Renderer;
  26005. /**
  26006. * The renderer instance of the chart. Each chart instance has only one
  26007. * associated renderer.
  26008. *
  26009. * @name Highcharts.Chart#renderer
  26010. * @type {Highcharts.SVGRenderer}
  26011. */
  26012. chart.renderer = new Ren(container, chartWidth, chartHeight, null, optionsChart.forExport, options.exporting && options.exporting.allowHTML, chart.styledMode);
  26013. // Set the initial animation from the options
  26014. setAnimation(void 0, chart);
  26015. chart.setClassName(optionsChart.className);
  26016. if (!chart.styledMode) {
  26017. chart.renderer.setStyle(optionsChart.style);
  26018. }
  26019. else {
  26020. // Initialize definitions
  26021. for (key in options.defs) { // eslint-disable-line guard-for-in
  26022. this.renderer.definition(options.defs[key]);
  26023. }
  26024. }
  26025. // Add a reference to the charts index
  26026. chart.renderer.chartIndex = chart.index;
  26027. fireEvent(this, 'afterGetContainer');
  26028. };
  26029. /**
  26030. * Calculate margins by rendering axis labels in a preliminary position.
  26031. * Title, subtitle and legend have already been rendered at this stage, but
  26032. * will be moved into their final positions.
  26033. *
  26034. * @private
  26035. * @function Highcharts.Chart#getMargins
  26036. * @fires Highcharts.Chart#event:getMargins
  26037. */
  26038. Chart.prototype.getMargins = function (skipAxes) {
  26039. var _a = this, spacing = _a.spacing, margin = _a.margin, titleOffset = _a.titleOffset;
  26040. this.resetMargins();
  26041. // Adjust for title and subtitle
  26042. if (titleOffset[0] && !defined(margin[0])) {
  26043. this.plotTop = Math.max(this.plotTop, titleOffset[0] + spacing[0]);
  26044. }
  26045. if (titleOffset[2] && !defined(margin[2])) {
  26046. this.marginBottom = Math.max(this.marginBottom, titleOffset[2] + spacing[2]);
  26047. }
  26048. // Adjust for legend
  26049. if (this.legend && this.legend.display) {
  26050. this.legend.adjustMargins(margin, spacing);
  26051. }
  26052. fireEvent(this, 'getMargins');
  26053. if (!skipAxes) {
  26054. this.getAxisMargins();
  26055. }
  26056. };
  26057. /**
  26058. * @private
  26059. * @function Highcharts.Chart#getAxisMargins
  26060. */
  26061. Chart.prototype.getAxisMargins = function () {
  26062. var chart = this,
  26063. // [top, right, bottom, left]
  26064. axisOffset = chart.axisOffset = [0, 0, 0, 0], colorAxis = chart.colorAxis, margin = chart.margin, getOffset = function (axes) {
  26065. axes.forEach(function (axis) {
  26066. if (axis.visible) {
  26067. axis.getOffset();
  26068. }
  26069. });
  26070. };
  26071. // pre-render axes to get labels offset width
  26072. if (chart.hasCartesianSeries) {
  26073. getOffset(chart.axes);
  26074. }
  26075. else if (colorAxis && colorAxis.length) {
  26076. getOffset(colorAxis);
  26077. }
  26078. // Add the axis offsets
  26079. marginNames.forEach(function (m, side) {
  26080. if (!defined(margin[side])) {
  26081. chart[m] += axisOffset[side];
  26082. }
  26083. });
  26084. chart.setChartSize();
  26085. };
  26086. /**
  26087. * Reflows the chart to its container. By default, the chart reflows
  26088. * automatically to its container following a `window.resize` event, as per
  26089. * the [chart.reflow](https://api.highcharts.com/highcharts/chart.reflow)
  26090. * option. However, there are no reliable events for div resize, so if the
  26091. * container is resized without a window resize event, this must be called
  26092. * explicitly.
  26093. *
  26094. * @sample highcharts/members/chart-reflow/
  26095. * Resize div and reflow
  26096. * @sample highcharts/chart/events-container/
  26097. * Pop up and reflow
  26098. *
  26099. * @function Highcharts.Chart#reflow
  26100. *
  26101. * @param {global.Event} [e]
  26102. * Event arguments. Used primarily when the function is called
  26103. * internally as a response to window resize.
  26104. */
  26105. Chart.prototype.reflow = function (e) {
  26106. var chart = this, optionsChart = chart.options.chart, renderTo = chart.renderTo, hasUserSize = (defined(optionsChart.width) &&
  26107. defined(optionsChart.height)), width = optionsChart.width || getStyle(renderTo, 'width'), height = optionsChart.height || getStyle(renderTo, 'height'), target = e ? e.target : win;
  26108. // Width and height checks for display:none. Target is doc in IE8 and
  26109. // Opera, win in Firefox, Chrome and IE9.
  26110. if (!hasUserSize &&
  26111. !chart.isPrinting &&
  26112. width &&
  26113. height &&
  26114. (target === win || target === doc)) {
  26115. if (width !== chart.containerWidth ||
  26116. height !== chart.containerHeight) {
  26117. U.clearTimeout(chart.reflowTimeout);
  26118. // When called from window.resize, e is set, else it's called
  26119. // directly (#2224)
  26120. chart.reflowTimeout = syncTimeout(function () {
  26121. // Set size, it may have been destroyed in the meantime
  26122. // (#1257)
  26123. if (chart.container) {
  26124. chart.setSize(void 0, void 0, false);
  26125. }
  26126. }, e ? 100 : 0);
  26127. }
  26128. chart.containerWidth = width;
  26129. chart.containerHeight = height;
  26130. }
  26131. };
  26132. /**
  26133. * Toggle the event handlers necessary for auto resizing, depending on the
  26134. * `chart.reflow` option.
  26135. *
  26136. * @private
  26137. * @function Highcharts.Chart#setReflow
  26138. */
  26139. Chart.prototype.setReflow = function (reflow) {
  26140. var chart = this;
  26141. if (reflow !== false && !this.unbindReflow) {
  26142. this.unbindReflow = addEvent(win, 'resize', function (e) {
  26143. // a removed event listener still runs in Edge and IE if the
  26144. // listener was removed while the event runs, so check if the
  26145. // chart is not destroyed (#11609)
  26146. if (chart.options) {
  26147. chart.reflow(e);
  26148. }
  26149. });
  26150. addEvent(this, 'destroy', this.unbindReflow);
  26151. }
  26152. else if (reflow === false && this.unbindReflow) {
  26153. // Unbind and unset
  26154. this.unbindReflow = this.unbindReflow();
  26155. }
  26156. // The following will add listeners to re-fit the chart before and after
  26157. // printing (#2284). However it only works in WebKit. Should have worked
  26158. // in Firefox, but not supported in IE.
  26159. /*
  26160. if (win.matchMedia) {
  26161. win.matchMedia('print').addListener(function reflow() {
  26162. chart.reflow();
  26163. });
  26164. }
  26165. //*/
  26166. };
  26167. /**
  26168. * Resize the chart to a given width and height. In order to set the width
  26169. * only, the height argument may be skipped. To set the height only, pass
  26170. * `undefined` for the width.
  26171. *
  26172. * @sample highcharts/members/chart-setsize-button/
  26173. * Test resizing from buttons
  26174. * @sample highcharts/members/chart-setsize-jquery-resizable/
  26175. * Add a jQuery UI resizable
  26176. * @sample stock/members/chart-setsize/
  26177. * Highstock with UI resizable
  26178. *
  26179. * @function Highcharts.Chart#setSize
  26180. *
  26181. * @param {number|null} [width]
  26182. * The new pixel width of the chart. Since v4.2.6, the argument can
  26183. * be `undefined` in order to preserve the current value (when
  26184. * setting height only), or `null` to adapt to the width of the
  26185. * containing element.
  26186. *
  26187. * @param {number|null} [height]
  26188. * The new pixel height of the chart. Since v4.2.6, the argument can
  26189. * be `undefined` in order to preserve the current value, or `null`
  26190. * in order to adapt to the height of the containing element.
  26191. *
  26192. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  26193. * Whether and how to apply animation.
  26194. *
  26195. * @return {void}
  26196. *
  26197. * @fires Highcharts.Chart#event:endResize
  26198. * @fires Highcharts.Chart#event:resize
  26199. */
  26200. Chart.prototype.setSize = function (width, height, animation) {
  26201. var chart = this, renderer = chart.renderer, globalAnimation;
  26202. // Handle the isResizing counter
  26203. chart.isResizing += 1;
  26204. // set the animation for the current process
  26205. setAnimation(animation, chart);
  26206. globalAnimation = renderer.globalAnimation;
  26207. chart.oldChartHeight = chart.chartHeight;
  26208. chart.oldChartWidth = chart.chartWidth;
  26209. if (typeof width !== 'undefined') {
  26210. chart.options.chart.width = width;
  26211. }
  26212. if (typeof height !== 'undefined') {
  26213. chart.options.chart.height = height;
  26214. }
  26215. chart.getChartSize();
  26216. // Resize the container with the global animation applied if enabled
  26217. // (#2503)
  26218. if (!chart.styledMode) {
  26219. (globalAnimation ? animate : css)(chart.container, {
  26220. width: chart.chartWidth + 'px',
  26221. height: chart.chartHeight + 'px'
  26222. }, globalAnimation);
  26223. }
  26224. chart.setChartSize(true);
  26225. renderer.setSize(chart.chartWidth, chart.chartHeight, globalAnimation);
  26226. // handle axes
  26227. chart.axes.forEach(function (axis) {
  26228. axis.isDirty = true;
  26229. axis.setScale();
  26230. });
  26231. chart.isDirtyLegend = true; // force legend redraw
  26232. chart.isDirtyBox = true; // force redraw of plot and chart border
  26233. chart.layOutTitles(); // #2857
  26234. chart.getMargins();
  26235. chart.redraw(globalAnimation);
  26236. chart.oldChartHeight = null;
  26237. fireEvent(chart, 'resize');
  26238. // Fire endResize and set isResizing back. If animation is disabled,
  26239. // fire without delay
  26240. syncTimeout(function () {
  26241. if (chart) {
  26242. fireEvent(chart, 'endResize', null, function () {
  26243. chart.isResizing -= 1;
  26244. });
  26245. }
  26246. }, animObject(globalAnimation).duration || 0);
  26247. };
  26248. /**
  26249. * Set the public chart properties. This is done before and after the
  26250. * pre-render to determine margin sizes.
  26251. *
  26252. * @private
  26253. * @function Highcharts.Chart#setChartSize
  26254. * @fires Highcharts.Chart#event:afterSetChartSize
  26255. */
  26256. Chart.prototype.setChartSize = function (skipAxes) {
  26257. var chart = this, inverted = chart.inverted, renderer = chart.renderer, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, optionsChart = chart.options.chart, spacing = chart.spacing, clipOffset = chart.clipOffset, clipX, clipY, plotLeft, plotTop, plotWidth, plotHeight, plotBorderWidth;
  26258. /**
  26259. * The current left position of the plot area in pixels.
  26260. *
  26261. * @name Highcharts.Chart#plotLeft
  26262. * @type {number}
  26263. */
  26264. chart.plotLeft = plotLeft = Math.round(chart.plotLeft);
  26265. /**
  26266. * The current top position of the plot area in pixels.
  26267. *
  26268. * @name Highcharts.Chart#plotTop
  26269. * @type {number}
  26270. */
  26271. chart.plotTop = plotTop = Math.round(chart.plotTop);
  26272. /**
  26273. * The current width of the plot area in pixels.
  26274. *
  26275. * @name Highcharts.Chart#plotWidth
  26276. * @type {number}
  26277. */
  26278. chart.plotWidth = plotWidth = Math.max(0, Math.round(chartWidth - plotLeft - chart.marginRight));
  26279. /**
  26280. * The current height of the plot area in pixels.
  26281. *
  26282. * @name Highcharts.Chart#plotHeight
  26283. * @type {number}
  26284. */
  26285. chart.plotHeight = plotHeight = Math.max(0, Math.round(chartHeight - plotTop - chart.marginBottom));
  26286. chart.plotSizeX = inverted ? plotHeight : plotWidth;
  26287. chart.plotSizeY = inverted ? plotWidth : plotHeight;
  26288. chart.plotBorderWidth = optionsChart.plotBorderWidth || 0;
  26289. // Set boxes used for alignment
  26290. chart.spacingBox = renderer.spacingBox = {
  26291. x: spacing[3],
  26292. y: spacing[0],
  26293. width: chartWidth - spacing[3] - spacing[1],
  26294. height: chartHeight - spacing[0] - spacing[2]
  26295. };
  26296. chart.plotBox = renderer.plotBox = {
  26297. x: plotLeft,
  26298. y: plotTop,
  26299. width: plotWidth,
  26300. height: plotHeight
  26301. };
  26302. plotBorderWidth = 2 * Math.floor(chart.plotBorderWidth / 2);
  26303. clipX = Math.ceil(Math.max(plotBorderWidth, clipOffset[3]) / 2);
  26304. clipY = Math.ceil(Math.max(plotBorderWidth, clipOffset[0]) / 2);
  26305. chart.clipBox = {
  26306. x: clipX,
  26307. y: clipY,
  26308. width: Math.floor(chart.plotSizeX -
  26309. Math.max(plotBorderWidth, clipOffset[1]) / 2 -
  26310. clipX),
  26311. height: Math.max(0, Math.floor(chart.plotSizeY -
  26312. Math.max(plotBorderWidth, clipOffset[2]) / 2 -
  26313. clipY))
  26314. };
  26315. if (!skipAxes) {
  26316. chart.axes.forEach(function (axis) {
  26317. axis.setAxisSize();
  26318. axis.setAxisTranslation();
  26319. });
  26320. }
  26321. fireEvent(chart, 'afterSetChartSize', { skipAxes: skipAxes });
  26322. };
  26323. /**
  26324. * Initial margins before auto size margins are applied.
  26325. *
  26326. * @private
  26327. * @function Highcharts.Chart#resetMargins
  26328. */
  26329. Chart.prototype.resetMargins = function () {
  26330. fireEvent(this, 'resetMargins');
  26331. var chart = this, chartOptions = chart.options.chart;
  26332. // Create margin and spacing array
  26333. ['margin', 'spacing'].forEach(function splashArrays(target) {
  26334. var value = chartOptions[target], values = isObject(value) ? value : [value, value, value, value];
  26335. [
  26336. 'Top',
  26337. 'Right',
  26338. 'Bottom',
  26339. 'Left'
  26340. ].forEach(function (sideName, side) {
  26341. chart[target][side] = pick(chartOptions[target + sideName], values[side]);
  26342. });
  26343. });
  26344. // Set margin names like chart.plotTop, chart.plotLeft,
  26345. // chart.marginRight, chart.marginBottom.
  26346. marginNames.forEach(function (m, side) {
  26347. chart[m] = pick(chart.margin[side], chart.spacing[side]);
  26348. });
  26349. chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left
  26350. chart.clipOffset = [0, 0, 0, 0];
  26351. };
  26352. /**
  26353. * Internal function to draw or redraw the borders and backgrounds for chart
  26354. * and plot area.
  26355. *
  26356. * @private
  26357. * @function Highcharts.Chart#drawChartBox
  26358. * @fires Highcharts.Chart#event:afterDrawChartBox
  26359. */
  26360. Chart.prototype.drawChartBox = function () {
  26361. var chart = this, optionsChart = chart.options.chart, renderer = chart.renderer, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, chartBackground = chart.chartBackground, plotBackground = chart.plotBackground, plotBorder = chart.plotBorder, chartBorderWidth, styledMode = chart.styledMode, plotBGImage = chart.plotBGImage, chartBackgroundColor = optionsChart.backgroundColor, plotBackgroundColor = optionsChart.plotBackgroundColor, plotBackgroundImage = optionsChart.plotBackgroundImage, mgn, bgAttr, plotLeft = chart.plotLeft, plotTop = chart.plotTop, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotBox = chart.plotBox, clipRect = chart.clipRect, clipBox = chart.clipBox, verb = 'animate';
  26362. // Chart area
  26363. if (!chartBackground) {
  26364. chart.chartBackground = chartBackground = renderer.rect()
  26365. .addClass('highcharts-background')
  26366. .add();
  26367. verb = 'attr';
  26368. }
  26369. if (!styledMode) {
  26370. // Presentational
  26371. chartBorderWidth = optionsChart.borderWidth || 0;
  26372. mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0);
  26373. bgAttr = {
  26374. fill: chartBackgroundColor || 'none'
  26375. };
  26376. if (chartBorderWidth || chartBackground['stroke-width']) { // #980
  26377. bgAttr.stroke = optionsChart.borderColor;
  26378. bgAttr['stroke-width'] = chartBorderWidth;
  26379. }
  26380. chartBackground
  26381. .attr(bgAttr)
  26382. .shadow(optionsChart.shadow);
  26383. }
  26384. else {
  26385. chartBorderWidth = mgn = chartBackground.strokeWidth();
  26386. }
  26387. chartBackground[verb]({
  26388. x: mgn / 2,
  26389. y: mgn / 2,
  26390. width: chartWidth - mgn - chartBorderWidth % 2,
  26391. height: chartHeight - mgn - chartBorderWidth % 2,
  26392. r: optionsChart.borderRadius
  26393. });
  26394. // Plot background
  26395. verb = 'animate';
  26396. if (!plotBackground) {
  26397. verb = 'attr';
  26398. chart.plotBackground = plotBackground = renderer.rect()
  26399. .addClass('highcharts-plot-background')
  26400. .add();
  26401. }
  26402. plotBackground[verb](plotBox);
  26403. if (!styledMode) {
  26404. // Presentational attributes for the background
  26405. plotBackground
  26406. .attr({
  26407. fill: plotBackgroundColor || 'none'
  26408. })
  26409. .shadow(optionsChart.plotShadow);
  26410. // Create the background image
  26411. if (plotBackgroundImage) {
  26412. if (!plotBGImage) {
  26413. chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight).add();
  26414. }
  26415. else {
  26416. if (plotBackgroundImage !== plotBGImage.attr('href')) {
  26417. plotBGImage.attr('href', plotBackgroundImage);
  26418. }
  26419. plotBGImage.animate(plotBox);
  26420. }
  26421. }
  26422. }
  26423. // Plot clip
  26424. if (!clipRect) {
  26425. chart.clipRect = renderer.clipRect(clipBox);
  26426. }
  26427. else {
  26428. clipRect.animate({
  26429. width: clipBox.width,
  26430. height: clipBox.height
  26431. });
  26432. }
  26433. // Plot area border
  26434. verb = 'animate';
  26435. if (!plotBorder) {
  26436. verb = 'attr';
  26437. chart.plotBorder = plotBorder = renderer.rect()
  26438. .addClass('highcharts-plot-border')
  26439. .attr({
  26440. zIndex: 1 // Above the grid
  26441. })
  26442. .add();
  26443. }
  26444. if (!styledMode) {
  26445. // Presentational
  26446. plotBorder.attr({
  26447. stroke: optionsChart.plotBorderColor,
  26448. 'stroke-width': optionsChart.plotBorderWidth || 0,
  26449. fill: 'none'
  26450. });
  26451. }
  26452. plotBorder[verb](plotBorder.crisp({
  26453. x: plotLeft,
  26454. y: plotTop,
  26455. width: plotWidth,
  26456. height: plotHeight
  26457. }, -plotBorder.strokeWidth())); // #3282 plotBorder should be negative;
  26458. // reset
  26459. chart.isDirtyBox = false;
  26460. fireEvent(this, 'afterDrawChartBox');
  26461. };
  26462. /**
  26463. * Detect whether a certain chart property is needed based on inspecting its
  26464. * options and series. This mainly applies to the chart.inverted property,
  26465. * and in extensions to the chart.angular and chart.polar properties.
  26466. *
  26467. * @private
  26468. * @function Highcharts.Chart#propFromSeries
  26469. * @return {void}
  26470. */
  26471. Chart.prototype.propFromSeries = function () {
  26472. var chart = this, optionsChart = chart.options.chart, klass, seriesOptions = chart.options.series, i, value;
  26473. /**
  26474. * The flag is set to `true` if a series of the chart is inverted.
  26475. *
  26476. * @name Highcharts.Chart#inverted
  26477. * @type {boolean|undefined}
  26478. */
  26479. ['inverted', 'angular', 'polar'].forEach(function (key) {
  26480. // The default series type's class
  26481. klass = seriesTypes[(optionsChart.type ||
  26482. optionsChart.defaultSeriesType)];
  26483. // Get the value from available chart-wide properties
  26484. value =
  26485. // It is set in the options:
  26486. optionsChart[key] ||
  26487. // The default series class:
  26488. (klass && klass.prototype[key]);
  26489. // requires it
  26490. // 4. Check if any the chart's series require it
  26491. i = seriesOptions && seriesOptions.length;
  26492. while (!value && i--) {
  26493. klass = seriesTypes[seriesOptions[i].type];
  26494. if (klass && klass.prototype[key]) {
  26495. value = true;
  26496. }
  26497. }
  26498. // Set the chart property
  26499. chart[key] = value;
  26500. });
  26501. };
  26502. /**
  26503. * Internal function to link two or more series together, based on the
  26504. * `linkedTo` option. This is done from `Chart.render`, and after
  26505. * `Chart.addSeries` and `Series.remove`.
  26506. *
  26507. * @private
  26508. * @function Highcharts.Chart#linkSeries
  26509. * @fires Highcharts.Chart#event:afterLinkSeries
  26510. */
  26511. Chart.prototype.linkSeries = function () {
  26512. var chart = this, chartSeries = chart.series;
  26513. // Reset links
  26514. chartSeries.forEach(function (series) {
  26515. series.linkedSeries.length = 0;
  26516. });
  26517. // Apply new links
  26518. chartSeries.forEach(function (series) {
  26519. var linkedTo = series.options.linkedTo;
  26520. if (isString(linkedTo)) {
  26521. if (linkedTo === ':previous') {
  26522. linkedTo = chart.series[series.index - 1];
  26523. }
  26524. else {
  26525. linkedTo = chart.get(linkedTo);
  26526. }
  26527. // #3341 avoid mutual linking
  26528. if (linkedTo && linkedTo.linkedParent !== series) {
  26529. linkedTo.linkedSeries.push(series);
  26530. series.linkedParent = linkedTo;
  26531. if (linkedTo.enabledDataSorting) {
  26532. series.setDataSortingOptions();
  26533. }
  26534. series.visible = pick(series.options.visible, linkedTo.options.visible, series.visible); // #3879
  26535. }
  26536. }
  26537. });
  26538. fireEvent(this, 'afterLinkSeries');
  26539. };
  26540. /**
  26541. * Render series for the chart.
  26542. *
  26543. * @private
  26544. * @function Highcharts.Chart#renderSeries
  26545. */
  26546. Chart.prototype.renderSeries = function () {
  26547. this.series.forEach(function (serie) {
  26548. serie.translate();
  26549. serie.render();
  26550. });
  26551. };
  26552. /**
  26553. * Render labels for the chart.
  26554. *
  26555. * @private
  26556. * @function Highcharts.Chart#renderLabels
  26557. */
  26558. Chart.prototype.renderLabels = function () {
  26559. var chart = this, labels = chart.options.labels;
  26560. if (labels.items) {
  26561. labels.items.forEach(function (label) {
  26562. var style = extend(labels.style, label.style), x = pInt(style.left) + chart.plotLeft, y = pInt(style.top) + chart.plotTop + 12;
  26563. // delete to prevent rewriting in IE
  26564. delete style.left;
  26565. delete style.top;
  26566. chart.renderer.text(label.html, x, y)
  26567. .attr({ zIndex: 2 })
  26568. .css(style)
  26569. .add();
  26570. });
  26571. }
  26572. };
  26573. /**
  26574. * Render all graphics for the chart. Runs internally on initialization.
  26575. *
  26576. * @private
  26577. * @function Highcharts.Chart#render
  26578. */
  26579. Chart.prototype.render = function () {
  26580. var chart = this, axes = chart.axes, colorAxis = chart.colorAxis, renderer = chart.renderer, options = chart.options, correction = 0, // correction for X axis labels
  26581. tempWidth, tempHeight, redoHorizontal, redoVertical, renderAxes = function (axes) {
  26582. axes.forEach(function (axis) {
  26583. if (axis.visible) {
  26584. axis.render();
  26585. }
  26586. });
  26587. };
  26588. // Title
  26589. chart.setTitle();
  26590. /**
  26591. * The overview of the chart's series.
  26592. *
  26593. * @name Highcharts.Chart#legend
  26594. * @type {Highcharts.Legend}
  26595. */
  26596. chart.legend = new Legend(chart, options.legend);
  26597. // Get stacks
  26598. if (chart.getStacks) {
  26599. chart.getStacks();
  26600. }
  26601. // Get chart margins
  26602. chart.getMargins(true);
  26603. chart.setChartSize();
  26604. // Record preliminary dimensions for later comparison
  26605. tempWidth = chart.plotWidth;
  26606. axes.some(function (axis) {
  26607. if (axis.horiz &&
  26608. axis.visible &&
  26609. axis.options.labels.enabled &&
  26610. axis.series.length) {
  26611. // 21 is the most common correction for X axis labels
  26612. correction = 21;
  26613. return true;
  26614. }
  26615. });
  26616. // use Math.max to prevent negative plotHeight
  26617. chart.plotHeight = Math.max(chart.plotHeight - correction, 0);
  26618. tempHeight = chart.plotHeight;
  26619. // Get margins by pre-rendering axes
  26620. axes.forEach(function (axis) {
  26621. axis.setScale();
  26622. });
  26623. chart.getAxisMargins();
  26624. // If the plot area size has changed significantly, calculate tick
  26625. // positions again
  26626. redoHorizontal = tempWidth / chart.plotWidth > 1.1;
  26627. // Height is more sensitive, use lower threshold
  26628. redoVertical = tempHeight / chart.plotHeight > 1.05;
  26629. if (redoHorizontal || redoVertical) {
  26630. axes.forEach(function (axis) {
  26631. if ((axis.horiz && redoHorizontal) ||
  26632. (!axis.horiz && redoVertical)) {
  26633. // update to reflect the new margins
  26634. axis.setTickInterval(true);
  26635. }
  26636. });
  26637. chart.getMargins(); // second pass to check for new labels
  26638. }
  26639. // Draw the borders and backgrounds
  26640. chart.drawChartBox();
  26641. // Axes
  26642. if (chart.hasCartesianSeries) {
  26643. renderAxes(axes);
  26644. }
  26645. else if (colorAxis && colorAxis.length) {
  26646. renderAxes(colorAxis);
  26647. }
  26648. // The series
  26649. if (!chart.seriesGroup) {
  26650. chart.seriesGroup = renderer.g('series-group')
  26651. .attr({ zIndex: 3 })
  26652. .add();
  26653. }
  26654. chart.renderSeries();
  26655. // Labels
  26656. chart.renderLabels();
  26657. // Credits
  26658. chart.addCredits();
  26659. // Handle responsiveness
  26660. if (chart.setResponsive) {
  26661. chart.setResponsive();
  26662. }
  26663. // Handle scaling
  26664. chart.updateContainerScaling();
  26665. // Set flag
  26666. chart.hasRendered = true;
  26667. };
  26668. /**
  26669. * Set a new credits label for the chart.
  26670. *
  26671. * @sample highcharts/credits/credits-update/
  26672. * Add and update credits
  26673. *
  26674. * @function Highcharts.Chart#addCredits
  26675. *
  26676. * @param {Highcharts.CreditsOptions} [credits]
  26677. * A configuration object for the new credits.
  26678. */
  26679. Chart.prototype.addCredits = function (credits) {
  26680. var chart = this, creds = merge(true, this.options.credits, credits);
  26681. if (creds.enabled && !this.credits) {
  26682. /**
  26683. * The chart's credits label. The label has an `update` method that
  26684. * allows setting new options as per the
  26685. * [credits options set](https://api.highcharts.com/highcharts/credits).
  26686. *
  26687. * @name Highcharts.Chart#credits
  26688. * @type {Highcharts.SVGElement}
  26689. */
  26690. this.credits = this.renderer.text(creds.text + (this.mapCredits || ''), 0, 0)
  26691. .addClass('highcharts-credits')
  26692. .on('click', function () {
  26693. if (creds.href) {
  26694. win.location.href = creds.href;
  26695. }
  26696. })
  26697. .attr({
  26698. align: creds.position.align,
  26699. zIndex: 8
  26700. });
  26701. if (!chart.styledMode) {
  26702. this.credits.css(creds.style);
  26703. }
  26704. this.credits
  26705. .add()
  26706. .align(creds.position);
  26707. // Dynamically update
  26708. this.credits.update = function (options) {
  26709. chart.credits = chart.credits.destroy();
  26710. chart.addCredits(options);
  26711. };
  26712. }
  26713. };
  26714. /**
  26715. * Handle scaling, #11329 - when there is scaling/transform on the container
  26716. * or on a parent element, we need to take this into account. We calculate
  26717. * the scaling once here and it is picked up where we need to use it
  26718. * (Pointer, Tooltip).
  26719. *
  26720. * @private
  26721. * @function Highcharts.Chart#updateContainerScaling
  26722. */
  26723. Chart.prototype.updateContainerScaling = function () {
  26724. var container = this.container;
  26725. // #13342 - tooltip was not visible in Chrome, when chart
  26726. // updates height.
  26727. if (container.offsetWidth > 2 && // #13342
  26728. container.offsetHeight > 2 && // #13342
  26729. container.getBoundingClientRect) {
  26730. var bb = container.getBoundingClientRect(), scaleX = bb.width / container.offsetWidth, scaleY = bb.height / container.offsetHeight;
  26731. if (scaleX !== 1 || scaleY !== 1) {
  26732. this.containerScaling = { scaleX: scaleX, scaleY: scaleY };
  26733. }
  26734. else {
  26735. delete this.containerScaling;
  26736. }
  26737. }
  26738. };
  26739. /**
  26740. * Remove the chart and purge memory. This method is called internally
  26741. * before adding a second chart into the same container, as well as on
  26742. * window unload to prevent leaks.
  26743. *
  26744. * @sample highcharts/members/chart-destroy/
  26745. * Destroy the chart from a button
  26746. * @sample stock/members/chart-destroy/
  26747. * Destroy with Highstock
  26748. *
  26749. * @function Highcharts.Chart#destroy
  26750. *
  26751. * @fires Highcharts.Chart#event:destroy
  26752. */
  26753. Chart.prototype.destroy = function () {
  26754. var chart = this, axes = chart.axes, series = chart.series, container = chart.container, i, parentNode = container && container.parentNode;
  26755. // fire the chart.destoy event
  26756. fireEvent(chart, 'destroy');
  26757. // Delete the chart from charts lookup array
  26758. if (chart.renderer.forExport) {
  26759. erase(charts, chart); // #6569
  26760. }
  26761. else {
  26762. charts[chart.index] = void 0;
  26763. }
  26764. H.chartCount--;
  26765. chart.renderTo.removeAttribute('data-highcharts-chart');
  26766. // remove events
  26767. removeEvent(chart);
  26768. // ==== Destroy collections:
  26769. // Destroy axes
  26770. i = axes.length;
  26771. while (i--) {
  26772. axes[i] = axes[i].destroy();
  26773. }
  26774. // Destroy scroller & scroller series before destroying base series
  26775. if (this.scroller && this.scroller.destroy) {
  26776. this.scroller.destroy();
  26777. }
  26778. // Destroy each series
  26779. i = series.length;
  26780. while (i--) {
  26781. series[i] = series[i].destroy();
  26782. }
  26783. // ==== Destroy chart properties:
  26784. [
  26785. 'title', 'subtitle', 'chartBackground', 'plotBackground',
  26786. 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits',
  26787. 'pointer', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip',
  26788. 'renderer'
  26789. ].forEach(function (name) {
  26790. var prop = chart[name];
  26791. if (prop && prop.destroy) {
  26792. chart[name] = prop.destroy();
  26793. }
  26794. });
  26795. // Remove container and all SVG, check container as it can break in IE
  26796. // when destroyed before finished loading
  26797. if (container) {
  26798. container.innerHTML = '';
  26799. removeEvent(container);
  26800. if (parentNode) {
  26801. discardElement(container);
  26802. }
  26803. }
  26804. // clean it all up
  26805. objectEach(chart, function (val, key) {
  26806. delete chart[key];
  26807. });
  26808. };
  26809. /**
  26810. * Prepare for first rendering after all data are loaded.
  26811. *
  26812. * @private
  26813. * @function Highcharts.Chart#firstRender
  26814. * @fires Highcharts.Chart#event:beforeRender
  26815. */
  26816. Chart.prototype.firstRender = function () {
  26817. var chart = this, options = chart.options;
  26818. // Hook for oldIE to check whether the chart is ready to render
  26819. if (chart.isReadyToRender && !chart.isReadyToRender()) {
  26820. return;
  26821. }
  26822. // Create the container
  26823. chart.getContainer();
  26824. chart.resetMargins();
  26825. chart.setChartSize();
  26826. // Set the common chart properties (mainly invert) from the given series
  26827. chart.propFromSeries();
  26828. // get axes
  26829. chart.getAxes();
  26830. // Initialize the series
  26831. (isArray(options.series) ? options.series : []).forEach(
  26832. // #9680
  26833. function (serieOptions) {
  26834. chart.initSeries(serieOptions);
  26835. });
  26836. chart.linkSeries();
  26837. chart.setSeriesData();
  26838. // Run an event after axes and series are initialized, but before
  26839. // render. At this stage, the series data is indexed and cached in the
  26840. // xData and yData arrays, so we can access those before rendering. Used
  26841. // in Highstock.
  26842. fireEvent(chart, 'beforeRender');
  26843. // depends on inverted and on margins being set
  26844. if (Pointer) {
  26845. if (!H.hasTouch && (win.PointerEvent || win.MSPointerEvent)) {
  26846. chart.pointer = new MSPointer(chart, options);
  26847. }
  26848. else {
  26849. /**
  26850. * The Pointer that keeps track of mouse and touch interaction.
  26851. *
  26852. * @memberof Highcharts.Chart
  26853. * @name pointer
  26854. * @type {Highcharts.Pointer}
  26855. * @instance
  26856. */
  26857. chart.pointer = new Pointer(chart, options);
  26858. }
  26859. }
  26860. chart.render();
  26861. // Fire the load event if there are no external images
  26862. if (!chart.renderer.imgCount && !chart.hasLoaded) {
  26863. chart.onload();
  26864. }
  26865. // If the chart was rendered outside the top container, put it back in
  26866. // (#3679)
  26867. chart.temporaryDisplay(true);
  26868. };
  26869. /**
  26870. * Internal function that runs on chart load, async if any images are loaded
  26871. * in the chart. Runs the callbacks and triggers the `load` and `render`
  26872. * events.
  26873. *
  26874. * @private
  26875. * @function Highcharts.Chart#onload
  26876. * @fires Highcharts.Chart#event:load
  26877. * @fires Highcharts.Chart#event:render
  26878. */
  26879. Chart.prototype.onload = function () {
  26880. // Run callbacks, first the ones registered by modules, then user's one
  26881. this.callbacks.concat([this.callback]).forEach(function (fn) {
  26882. // Chart destroyed in its own callback (#3600)
  26883. if (fn && typeof this.index !== 'undefined') {
  26884. fn.apply(this, [this]);
  26885. }
  26886. }, this);
  26887. fireEvent(this, 'load');
  26888. fireEvent(this, 'render');
  26889. // Set up auto resize, check for not destroyed (#6068)
  26890. if (defined(this.index)) {
  26891. this.setReflow(this.options.chart.reflow);
  26892. }
  26893. // Don't run again
  26894. this.hasLoaded = true;
  26895. };
  26896. return Chart;
  26897. }());
  26898. // Hook for adding callbacks in modules
  26899. Chart.prototype.callbacks = [];
  26900. /**
  26901. * Factory function for basic charts.
  26902. *
  26903. * @example
  26904. * // Render a chart in to div#container
  26905. * var chart = Highcharts.chart('container', {
  26906. * title: {
  26907. * text: 'My chart'
  26908. * },
  26909. * series: [{
  26910. * data: [1, 3, 2, 4]
  26911. * }]
  26912. * });
  26913. *
  26914. * @function Highcharts.chart
  26915. *
  26916. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  26917. * The DOM element to render to, or its id.
  26918. *
  26919. * @param {Highcharts.Options} options
  26920. * The chart options structure.
  26921. *
  26922. * @param {Highcharts.ChartCallbackFunction} [callback]
  26923. * Function to run when the chart has loaded and and all external images
  26924. * are loaded. Defining a
  26925. * [chart.events.load](https://api.highcharts.com/highcharts/chart.events.load)
  26926. * handler is equivalent.
  26927. *
  26928. * @return {Highcharts.Chart}
  26929. * Returns the Chart object.
  26930. */
  26931. function chart(a, b, c) {
  26932. return new Chart(a, b, c);
  26933. }
  26934. H.chart = chart;
  26935. H.Chart = Chart;
  26936. return Chart;
  26937. });
  26938. _registerModule(_modules, 'parts/ScrollablePlotArea.js', [_modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Chart, H, U) {
  26939. /* *
  26940. *
  26941. * (c) 2010-2020 Torstein Honsi
  26942. *
  26943. * License: www.highcharts.com/license
  26944. *
  26945. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  26946. *
  26947. * Highcharts feature to make the Y axis stay fixed when scrolling the chart
  26948. * horizontally on mobile devices. Supports left and right side axes.
  26949. */
  26950. /*
  26951. WIP on vertical scrollable plot area (#9378). To do:
  26952. - Bottom axis positioning
  26953. - Test with Gantt
  26954. - Look for size optimizing the code
  26955. - API and demos
  26956. */
  26957. var addEvent = U.addEvent, createElement = U.createElement, pick = U.pick, stop = U.stop;
  26958. /**
  26959. * Options for a scrollable plot area. This feature provides a minimum size for
  26960. * the plot area of the chart. If the size gets smaller than this, typically
  26961. * on mobile devices, a native browser scrollbar is presented. This scrollbar
  26962. * provides smooth scrolling for the contents of the plot area, whereas the
  26963. * title, legend and unaffected axes are fixed.
  26964. *
  26965. * Since v7.1.2, a scrollable plot area can be defined for either horizontal or
  26966. * vertical scrolling, depending on whether the `minWidth` or `minHeight`
  26967. * option is set.
  26968. *
  26969. * @sample highcharts/chart/scrollable-plotarea
  26970. * Scrollable plot area
  26971. * @sample highcharts/chart/scrollable-plotarea-vertical
  26972. * Vertically scrollable plot area
  26973. * @sample {gantt} highcharts/chart/scrollable-plotarea-vertical
  26974. * Gantt chart with vertically scrollable plot area
  26975. *
  26976. * @since 6.1.0
  26977. * @product highcharts gantt
  26978. * @apioption chart.scrollablePlotArea
  26979. */
  26980. /**
  26981. * The minimum height for the plot area. If it gets smaller than this, the plot
  26982. * area will become scrollable.
  26983. *
  26984. * @type {number}
  26985. * @apioption chart.scrollablePlotArea.minHeight
  26986. */
  26987. /**
  26988. * The minimum width for the plot area. If it gets smaller than this, the plot
  26989. * area will become scrollable.
  26990. *
  26991. * @type {number}
  26992. * @apioption chart.scrollablePlotArea.minWidth
  26993. */
  26994. /**
  26995. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  26996. * 1, where 0 aligns the plot area to the left and 1 aligns it to the right.
  26997. * Typically we would use 1 if the chart has right aligned Y axes.
  26998. *
  26999. * @type {number}
  27000. * @apioption chart.scrollablePlotArea.scrollPositionX
  27001. */
  27002. /**
  27003. * The initial scrolling position of the scrollable plot area. Ranges from 0 to
  27004. * 1, where 0 aligns the plot area to the top and 1 aligns it to the bottom.
  27005. *
  27006. * @type {number}
  27007. * @apioption chart.scrollablePlotArea.scrollPositionY
  27008. */
  27009. /**
  27010. * The opacity of mask applied on one of the sides of the plot
  27011. * area.
  27012. *
  27013. * @sample {highcharts} highcharts/chart/scrollable-plotarea-opacity
  27014. * Disabled opacity for the mask
  27015. *
  27016. * @type {number}
  27017. * @default 0.85
  27018. * @since 7.1.1
  27019. * @apioption chart.scrollablePlotArea.opacity
  27020. */
  27021. ''; // detach API doclets
  27022. /* eslint-disable no-invalid-this, valid-jsdoc */
  27023. addEvent(Chart, 'afterSetChartSize', function (e) {
  27024. var scrollablePlotArea = this.options.chart.scrollablePlotArea, scrollableMinWidth = scrollablePlotArea && scrollablePlotArea.minWidth, scrollableMinHeight = scrollablePlotArea && scrollablePlotArea.minHeight, scrollablePixelsX, scrollablePixelsY, corrections;
  27025. if (!this.renderer.forExport) {
  27026. // The amount of pixels to scroll, the difference between chart
  27027. // width and scrollable width
  27028. if (scrollableMinWidth) {
  27029. this.scrollablePixelsX = scrollablePixelsX = Math.max(0, scrollableMinWidth - this.chartWidth);
  27030. if (scrollablePixelsX) {
  27031. this.plotWidth += scrollablePixelsX;
  27032. if (this.inverted) {
  27033. this.clipBox.height += scrollablePixelsX;
  27034. this.plotBox.height += scrollablePixelsX;
  27035. }
  27036. else {
  27037. this.clipBox.width += scrollablePixelsX;
  27038. this.plotBox.width += scrollablePixelsX;
  27039. }
  27040. corrections = {
  27041. // Corrections for right side
  27042. 1: { name: 'right', value: scrollablePixelsX }
  27043. };
  27044. }
  27045. // Currently we can only do either X or Y
  27046. }
  27047. else if (scrollableMinHeight) {
  27048. this.scrollablePixelsY = scrollablePixelsY = Math.max(0, scrollableMinHeight - this.chartHeight);
  27049. if (scrollablePixelsY) {
  27050. this.plotHeight += scrollablePixelsY;
  27051. if (this.inverted) {
  27052. this.clipBox.width += scrollablePixelsY;
  27053. this.plotBox.width += scrollablePixelsY;
  27054. }
  27055. else {
  27056. this.clipBox.height += scrollablePixelsY;
  27057. this.plotBox.height += scrollablePixelsY;
  27058. }
  27059. corrections = {
  27060. 2: { name: 'bottom', value: scrollablePixelsY }
  27061. };
  27062. }
  27063. }
  27064. if (corrections && !e.skipAxes) {
  27065. this.axes.forEach(function (axis) {
  27066. // For right and bottom axes, only fix the plot line length
  27067. if (corrections[axis.side]) {
  27068. // Get the plot lines right in getPlotLinePath,
  27069. // temporarily set it to the adjusted plot width.
  27070. axis.getPlotLinePath = function () {
  27071. var marginName = corrections[axis.side].name, correctionValue = corrections[axis.side].value,
  27072. // axis.right or axis.bottom
  27073. margin = this[marginName], path;
  27074. // Temporarily adjust
  27075. this[marginName] = margin - correctionValue;
  27076. path = H.Axis.prototype.getPlotLinePath.apply(this, arguments);
  27077. // Reset
  27078. this[marginName] = margin;
  27079. return path;
  27080. };
  27081. }
  27082. else {
  27083. // Apply the corrected plotWidth
  27084. axis.setAxisSize();
  27085. axis.setAxisTranslation();
  27086. }
  27087. });
  27088. }
  27089. }
  27090. });
  27091. addEvent(Chart, 'render', function () {
  27092. if (this.scrollablePixelsX || this.scrollablePixelsY) {
  27093. if (this.setUpScrolling) {
  27094. this.setUpScrolling();
  27095. }
  27096. this.applyFixed();
  27097. }
  27098. else if (this.fixedDiv) { // Has been in scrollable mode
  27099. this.applyFixed();
  27100. }
  27101. });
  27102. /**
  27103. * @private
  27104. * @function Highcharts.Chart#setUpScrolling
  27105. * @return {void}
  27106. */
  27107. Chart.prototype.setUpScrolling = function () {
  27108. var _this = this;
  27109. var attribs = {
  27110. WebkitOverflowScrolling: 'touch',
  27111. overflowX: 'hidden',
  27112. overflowY: 'hidden'
  27113. };
  27114. if (this.scrollablePixelsX) {
  27115. attribs.overflowX = 'auto';
  27116. }
  27117. if (this.scrollablePixelsY) {
  27118. attribs.overflowY = 'auto';
  27119. }
  27120. // Add the necessary divs to provide scrolling
  27121. this.scrollingContainer = createElement('div', {
  27122. 'className': 'highcharts-scrolling'
  27123. }, attribs, this.renderTo);
  27124. // On scroll, reset the chart position because it applies to the scrolled
  27125. // container
  27126. addEvent(this.scrollingContainer, 'scroll', function () {
  27127. if (_this.pointer) {
  27128. delete _this.pointer.chartPosition;
  27129. }
  27130. });
  27131. this.innerContainer = createElement('div', {
  27132. 'className': 'highcharts-inner-container'
  27133. }, null, this.scrollingContainer);
  27134. // Now move the container inside
  27135. this.innerContainer.appendChild(this.container);
  27136. // Don't run again
  27137. this.setUpScrolling = null;
  27138. };
  27139. /**
  27140. * These elements are moved over to the fixed renderer and stay fixed when the
  27141. * user scrolls the chart
  27142. * @private
  27143. */
  27144. Chart.prototype.moveFixedElements = function () {
  27145. var container = this.container, fixedRenderer = this.fixedRenderer, fixedSelectors = [
  27146. '.highcharts-contextbutton',
  27147. '.highcharts-credits',
  27148. '.highcharts-legend',
  27149. '.highcharts-legend-checkbox',
  27150. '.highcharts-navigator-series',
  27151. '.highcharts-navigator-xaxis',
  27152. '.highcharts-navigator-yaxis',
  27153. '.highcharts-navigator',
  27154. '.highcharts-reset-zoom',
  27155. '.highcharts-scrollbar',
  27156. '.highcharts-subtitle',
  27157. '.highcharts-title'
  27158. ], axisClass;
  27159. if (this.scrollablePixelsX && !this.inverted) {
  27160. axisClass = '.highcharts-yaxis';
  27161. }
  27162. else if (this.scrollablePixelsX && this.inverted) {
  27163. axisClass = '.highcharts-xaxis';
  27164. }
  27165. else if (this.scrollablePixelsY && !this.inverted) {
  27166. axisClass = '.highcharts-xaxis';
  27167. }
  27168. else if (this.scrollablePixelsY && this.inverted) {
  27169. axisClass = '.highcharts-yaxis';
  27170. }
  27171. fixedSelectors.push(axisClass, axisClass + '-labels');
  27172. fixedSelectors.forEach(function (className) {
  27173. [].forEach.call(container.querySelectorAll(className), function (elem) {
  27174. (elem.namespaceURI === fixedRenderer.SVG_NS ?
  27175. fixedRenderer.box :
  27176. fixedRenderer.box.parentNode).appendChild(elem);
  27177. elem.style.pointerEvents = 'auto';
  27178. });
  27179. });
  27180. };
  27181. /**
  27182. * @private
  27183. * @function Highcharts.Chart#applyFixed
  27184. * @return {void}
  27185. */
  27186. Chart.prototype.applyFixed = function () {
  27187. var _a;
  27188. var fixedRenderer, scrollableWidth, scrollableHeight, firstTime = !this.fixedDiv, scrollableOptions = this.options.chart.scrollablePlotArea;
  27189. // First render
  27190. if (firstTime) {
  27191. this.fixedDiv = createElement('div', {
  27192. className: 'highcharts-fixed'
  27193. }, {
  27194. position: 'absolute',
  27195. overflow: 'hidden',
  27196. pointerEvents: 'none',
  27197. zIndex: 2
  27198. }, null, true);
  27199. this.renderTo.insertBefore(this.fixedDiv, this.renderTo.firstChild);
  27200. this.renderTo.style.overflow = 'visible';
  27201. this.fixedRenderer = fixedRenderer = new H.Renderer(this.fixedDiv, this.chartWidth, this.chartHeight, (_a = this.options.chart) === null || _a === void 0 ? void 0 : _a.style);
  27202. // Mask
  27203. this.scrollableMask = fixedRenderer
  27204. .path()
  27205. .attr({
  27206. fill: this.options.chart.backgroundColor || '#fff',
  27207. 'fill-opacity': pick(scrollableOptions.opacity, 0.85),
  27208. zIndex: -1
  27209. })
  27210. .addClass('highcharts-scrollable-mask')
  27211. .add();
  27212. this.moveFixedElements();
  27213. addEvent(this, 'afterShowResetZoom', this.moveFixedElements);
  27214. addEvent(this, 'afterLayOutTitles', this.moveFixedElements);
  27215. }
  27216. else {
  27217. // Set the size of the fixed renderer to the visible width
  27218. this.fixedRenderer.setSize(this.chartWidth, this.chartHeight);
  27219. }
  27220. // Increase the size of the scrollable renderer and background
  27221. scrollableWidth = this.chartWidth + (this.scrollablePixelsX || 0);
  27222. scrollableHeight = this.chartHeight + (this.scrollablePixelsY || 0);
  27223. stop(this.container);
  27224. this.container.style.width = scrollableWidth + 'px';
  27225. this.container.style.height = scrollableHeight + 'px';
  27226. this.renderer.boxWrapper.attr({
  27227. width: scrollableWidth,
  27228. height: scrollableHeight,
  27229. viewBox: [0, 0, scrollableWidth, scrollableHeight].join(' ')
  27230. });
  27231. this.chartBackground.attr({
  27232. width: scrollableWidth,
  27233. height: scrollableHeight
  27234. });
  27235. this.scrollingContainer.style.height = this.chartHeight + 'px';
  27236. // Set scroll position
  27237. if (firstTime) {
  27238. if (scrollableOptions.scrollPositionX) {
  27239. this.scrollingContainer.scrollLeft =
  27240. this.scrollablePixelsX *
  27241. scrollableOptions.scrollPositionX;
  27242. }
  27243. if (scrollableOptions.scrollPositionY) {
  27244. this.scrollingContainer.scrollTop =
  27245. this.scrollablePixelsY *
  27246. scrollableOptions.scrollPositionY;
  27247. }
  27248. }
  27249. // Mask behind the left and right side
  27250. var 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 -
  27251. (this.scrollablePixelsX || 0), maskPlotBottom = this.plotTop + this.plotHeight -
  27252. (this.scrollablePixelsY || 0), d;
  27253. if (this.scrollablePixelsX) {
  27254. d = [
  27255. // Left side
  27256. ['M', 0, maskTop],
  27257. ['L', this.plotLeft - 1, maskTop],
  27258. ['L', this.plotLeft - 1, maskBottom],
  27259. ['L', 0, maskBottom],
  27260. ['Z'],
  27261. // Right side
  27262. ['M', maskPlotRight, maskTop],
  27263. ['L', this.chartWidth, maskTop],
  27264. ['L', this.chartWidth, maskBottom],
  27265. ['L', maskPlotRight, maskBottom],
  27266. ['Z']
  27267. ];
  27268. }
  27269. else if (this.scrollablePixelsY) {
  27270. d = [
  27271. // Top side
  27272. ['M', maskLeft, 0],
  27273. ['L', maskLeft, this.plotTop - 1],
  27274. ['L', maskRight, this.plotTop - 1],
  27275. ['L', maskRight, 0],
  27276. ['Z'],
  27277. // Bottom side
  27278. ['M', maskLeft, maskPlotBottom],
  27279. ['L', maskLeft, this.chartHeight],
  27280. ['L', maskRight, this.chartHeight],
  27281. ['L', maskRight, maskPlotBottom],
  27282. ['Z']
  27283. ];
  27284. }
  27285. else {
  27286. d = [['M', 0, 0]];
  27287. }
  27288. if (this.redrawTrigger !== 'adjustHeight') {
  27289. this.scrollableMask.attr({ d: d });
  27290. }
  27291. };
  27292. });
  27293. _registerModule(_modules, 'parts/StackingAxis.js', [_modules['parts/Utilities.js']], function (U) {
  27294. /* *
  27295. *
  27296. * (c) 2010-2020 Torstein Honsi
  27297. *
  27298. * License: www.highcharts.com/license
  27299. *
  27300. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  27301. *
  27302. * */
  27303. var addEvent = U.addEvent, destroyObjectProperties = U.destroyObjectProperties, fireEvent = U.fireEvent, objectEach = U.objectEach, pick = U.pick;
  27304. /* eslint-disable valid-jsdoc */
  27305. /**
  27306. * Adds stacking support to axes.
  27307. * @private
  27308. * @class
  27309. */
  27310. var StackingAxisAdditions = /** @class */ (function () {
  27311. /* *
  27312. *
  27313. * Constructors
  27314. *
  27315. * */
  27316. function StackingAxisAdditions(axis) {
  27317. this.oldStacks = {};
  27318. this.stacks = {};
  27319. this.stacksTouched = 0;
  27320. this.axis = axis;
  27321. }
  27322. /* *
  27323. *
  27324. * Functions
  27325. *
  27326. * */
  27327. /**
  27328. * Build the stacks from top down
  27329. * @private
  27330. */
  27331. StackingAxisAdditions.prototype.buildStacks = function () {
  27332. var stacking = this;
  27333. var axis = stacking.axis;
  27334. var axisSeries = axis.series;
  27335. var reversedStacks = pick(axis.options.reversedStacks, true);
  27336. var len = axisSeries.length;
  27337. var actualSeries, i;
  27338. if (!axis.isXAxis) {
  27339. stacking.usePercentage = false;
  27340. i = len;
  27341. while (i--) {
  27342. actualSeries = axisSeries[reversedStacks ? i : len - i - 1];
  27343. actualSeries.setStackedPoints();
  27344. actualSeries.setGroupedPoints();
  27345. }
  27346. // Loop up again to compute percent and stream stack
  27347. for (i = 0; i < len; i++) {
  27348. axisSeries[i].modifyStacks();
  27349. }
  27350. fireEvent(axis, 'afterBuildStacks');
  27351. }
  27352. };
  27353. /**
  27354. * @private
  27355. */
  27356. StackingAxisAdditions.prototype.cleanStacks = function () {
  27357. var stacking = this;
  27358. var axis = stacking.axis;
  27359. var stacks;
  27360. if (!axis.isXAxis) {
  27361. if (stacking.oldStacks) {
  27362. stacks = stacking.stacks = stacking.oldStacks;
  27363. }
  27364. // reset stacks
  27365. objectEach(stacks, function (type) {
  27366. objectEach(type, function (stack) {
  27367. stack.cumulative = stack.total;
  27368. });
  27369. });
  27370. }
  27371. };
  27372. /**
  27373. * Set all the stacks to initial states and destroy unused ones.
  27374. * @private
  27375. */
  27376. StackingAxisAdditions.prototype.resetStacks = function () {
  27377. var stacking = this;
  27378. var axis = stacking.axis;
  27379. var stacks = stacking.stacks;
  27380. if (!axis.isXAxis) {
  27381. objectEach(stacks, function (type) {
  27382. objectEach(type, function (stack, key) {
  27383. // Clean up memory after point deletion (#1044, #4320)
  27384. if (stack.touched < stacking.stacksTouched) {
  27385. stack.destroy();
  27386. delete type[key];
  27387. // Reset stacks
  27388. }
  27389. else {
  27390. stack.total = null;
  27391. stack.cumulative = null;
  27392. }
  27393. });
  27394. });
  27395. }
  27396. };
  27397. /**
  27398. * @private
  27399. */
  27400. StackingAxisAdditions.prototype.renderStackTotals = function () {
  27401. var stacking = this;
  27402. var axis = stacking.axis;
  27403. var chart = axis.chart;
  27404. var renderer = chart.renderer;
  27405. var stacks = stacking.stacks;
  27406. var stackTotalGroup = stacking.stackTotalGroup = (stacking.stackTotalGroup ||
  27407. renderer
  27408. .g('stack-labels')
  27409. .attr({
  27410. visibility: 'visible',
  27411. zIndex: 6
  27412. })
  27413. .add());
  27414. // plotLeft/Top will change when y axis gets wider so we need to
  27415. // translate the stackTotalGroup at every render call. See bug #506
  27416. // and #516
  27417. stackTotalGroup.translate(chart.plotLeft, chart.plotTop);
  27418. // Render each stack total
  27419. objectEach(stacks, function (type) {
  27420. objectEach(type, function (stack) {
  27421. stack.render(stackTotalGroup);
  27422. });
  27423. });
  27424. };
  27425. return StackingAxisAdditions;
  27426. }());
  27427. /**
  27428. * Axis with stacking support.
  27429. * @private
  27430. * @class
  27431. */
  27432. var StackingAxis = /** @class */ (function () {
  27433. function StackingAxis() {
  27434. }
  27435. /* *
  27436. *
  27437. * Static Functions
  27438. *
  27439. * */
  27440. /**
  27441. * Extends axis with stacking support.
  27442. * @private
  27443. */
  27444. StackingAxis.compose = function (AxisClass) {
  27445. var axisProto = AxisClass.prototype;
  27446. addEvent(AxisClass, 'init', StackingAxis.onInit);
  27447. addEvent(AxisClass, 'destroy', StackingAxis.onDestroy);
  27448. };
  27449. /**
  27450. * @private
  27451. */
  27452. StackingAxis.onDestroy = function () {
  27453. var stacking = this.stacking;
  27454. if (!stacking) {
  27455. return;
  27456. }
  27457. var stacks = stacking.stacks;
  27458. // Destroy each stack total
  27459. objectEach(stacks, function (stack, stackKey) {
  27460. destroyObjectProperties(stack);
  27461. stacks[stackKey] = null;
  27462. });
  27463. if (stacking &&
  27464. stacking.stackTotalGroup) {
  27465. stacking.stackTotalGroup.destroy();
  27466. }
  27467. };
  27468. /**
  27469. * @private
  27470. */
  27471. StackingAxis.onInit = function () {
  27472. var axis = this;
  27473. if (!axis.stacking) {
  27474. axis.stacking = new StackingAxisAdditions(axis);
  27475. }
  27476. };
  27477. return StackingAxis;
  27478. }());
  27479. return StackingAxis;
  27480. });
  27481. _registerModule(_modules, 'mixins/legend-symbol.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  27482. /* *
  27483. *
  27484. * (c) 2010-2020 Torstein Honsi
  27485. *
  27486. * License: www.highcharts.com/license
  27487. *
  27488. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  27489. *
  27490. * */
  27491. var merge = U.merge, pick = U.pick;
  27492. /* eslint-disable valid-jsdoc */
  27493. /**
  27494. * Legend symbol mixin.
  27495. *
  27496. * @private
  27497. * @mixin Highcharts.LegendSymbolMixin
  27498. */
  27499. H.LegendSymbolMixin = {
  27500. /**
  27501. * Get the series' symbol in the legend
  27502. *
  27503. * @private
  27504. * @function Highcharts.LegendSymbolMixin.drawRectangle
  27505. *
  27506. * @param {Highcharts.Legend} legend
  27507. * The legend object
  27508. *
  27509. * @param {Highcharts.Point|Highcharts.Series} item
  27510. * The series (this) or point
  27511. */
  27512. drawRectangle: function (legend, item) {
  27513. var options = legend.options, symbolHeight = legend.symbolHeight, square = options.squareSymbol, symbolWidth = square ? symbolHeight : legend.symbolWidth;
  27514. item.legendSymbol = this.chart.renderer.rect(square ? (legend.symbolWidth - symbolHeight) / 2 : 0, legend.baseline - symbolHeight + 1, // #3988
  27515. symbolWidth, symbolHeight, pick(legend.options.symbolRadius, symbolHeight / 2))
  27516. .addClass('highcharts-point')
  27517. .attr({
  27518. zIndex: 3
  27519. }).add(item.legendGroup);
  27520. },
  27521. /**
  27522. * Get the series' symbol in the legend. This method should be overridable
  27523. * to create custom symbols through
  27524. * Highcharts.seriesTypes[type].prototype.drawLegendSymbols.
  27525. *
  27526. * @private
  27527. * @function Highcharts.LegendSymbolMixin.drawLineMarker
  27528. *
  27529. * @param {Highcharts.Legend} legend
  27530. * The legend object.
  27531. */
  27532. drawLineMarker: function (legend) {
  27533. var options = this.options, markerOptions = options.marker, radius, legendSymbol, symbolWidth = legend.symbolWidth, symbolHeight = legend.symbolHeight, generalRadius = symbolHeight / 2, renderer = this.chart.renderer, legendItemGroup = this.legendGroup, verticalCenter = legend.baseline -
  27534. Math.round(legend.fontMetrics.b * 0.3), attr = {};
  27535. // Draw the line
  27536. if (!this.chart.styledMode) {
  27537. attr = {
  27538. 'stroke-width': options.lineWidth || 0
  27539. };
  27540. if (options.dashStyle) {
  27541. attr.dashstyle = options.dashStyle;
  27542. }
  27543. }
  27544. this.legendLine = renderer
  27545. .path([
  27546. 'M',
  27547. 0,
  27548. verticalCenter,
  27549. 'L',
  27550. symbolWidth,
  27551. verticalCenter
  27552. ])
  27553. .addClass('highcharts-graph')
  27554. .attr(attr)
  27555. .add(legendItemGroup);
  27556. // Draw the marker
  27557. if (markerOptions && markerOptions.enabled !== false && symbolWidth) {
  27558. // Do not allow the marker to be larger than the symbolHeight
  27559. radius = Math.min(pick(markerOptions.radius, generalRadius), generalRadius);
  27560. // Restrict symbol markers size
  27561. if (this.symbol.indexOf('url') === 0) {
  27562. markerOptions = merge(markerOptions, {
  27563. width: symbolHeight,
  27564. height: symbolHeight
  27565. });
  27566. radius = 0;
  27567. }
  27568. this.legendSymbol = legendSymbol = renderer.symbol(this.symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, 2 * radius, markerOptions)
  27569. .addClass('highcharts-point')
  27570. .add(legendItemGroup);
  27571. legendSymbol.isMarker = true;
  27572. }
  27573. }
  27574. };
  27575. return H.LegendSymbolMixin;
  27576. });
  27577. _registerModule(_modules, 'parts/Point.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  27578. /* *
  27579. *
  27580. * (c) 2010-2020 Torstein Honsi
  27581. *
  27582. * License: www.highcharts.com/license
  27583. *
  27584. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  27585. *
  27586. * */
  27587. var animObject = U.animObject, defined = U.defined, erase = U.erase, extend = U.extend, fireEvent = U.fireEvent, format = U.format, getNestedProperty = U.getNestedProperty, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, syncTimeout = U.syncTimeout, pick = U.pick, removeEvent = U.removeEvent, uniqueKey = U.uniqueKey;
  27588. /**
  27589. * Function callback when a series point is clicked. Return false to cancel the
  27590. * action.
  27591. *
  27592. * @callback Highcharts.PointClickCallbackFunction
  27593. *
  27594. * @param {Highcharts.Point} this
  27595. * The point where the event occured.
  27596. *
  27597. * @param {Highcharts.PointClickEventObject} event
  27598. * Event arguments.
  27599. */
  27600. /**
  27601. * Common information for a click event on a series point.
  27602. *
  27603. * @interface Highcharts.PointClickEventObject
  27604. * @extends Highcharts.PointerEventObject
  27605. */ /**
  27606. * Clicked point.
  27607. * @name Highcharts.PointClickEventObject#point
  27608. * @type {Highcharts.Point}
  27609. */
  27610. /**
  27611. * Configuration hash for the data label and tooltip formatters.
  27612. *
  27613. * @interface Highcharts.PointLabelObject
  27614. */ /**
  27615. * The point's current color.
  27616. * @name Highcharts.PointLabelObject#color
  27617. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  27618. */ /**
  27619. * The point's current color index, used in styled mode instead of `color`. The
  27620. * color index is inserted in class names used for styling.
  27621. * @name Highcharts.PointLabelObject#colorIndex
  27622. * @type {number}
  27623. */ /**
  27624. * The name of the related point.
  27625. * @name Highcharts.PointLabelObject#key
  27626. * @type {string|undefined}
  27627. */ /**
  27628. * The percentage for related points in a stacked series or pies.
  27629. * @name Highcharts.PointLabelObject#percentage
  27630. * @type {number}
  27631. */ /**
  27632. * The related point. The point name, if defined, is available through
  27633. * `this.point.name`.
  27634. * @name Highcharts.PointLabelObject#point
  27635. * @type {Highcharts.Point}
  27636. */ /**
  27637. * The related series. The series name is available through `this.series.name`.
  27638. * @name Highcharts.PointLabelObject#series
  27639. * @type {Highcharts.Series}
  27640. */ /**
  27641. * The total of values in either a stack for stacked series, or a pie in a pie
  27642. * series.
  27643. * @name Highcharts.PointLabelObject#total
  27644. * @type {number|undefined}
  27645. */ /**
  27646. * For categorized axes this property holds the category name for the point. For
  27647. * other axes it holds the X value.
  27648. * @name Highcharts.PointLabelObject#x
  27649. * @type {number|string|undefined}
  27650. */ /**
  27651. * The y value of the point.
  27652. * @name Highcharts.PointLabelObject#y
  27653. * @type {number|undefined}
  27654. */
  27655. /**
  27656. * Gets fired when the mouse leaves the area close to the point.
  27657. *
  27658. * @callback Highcharts.PointMouseOutCallbackFunction
  27659. *
  27660. * @param {Highcharts.Point} this
  27661. * Point where the event occured.
  27662. *
  27663. * @param {global.PointerEvent} event
  27664. * Event that occured.
  27665. */
  27666. /**
  27667. * Gets fired when the mouse enters the area close to the point.
  27668. *
  27669. * @callback Highcharts.PointMouseOverCallbackFunction
  27670. *
  27671. * @param {Highcharts.Point} this
  27672. * Point where the event occured.
  27673. *
  27674. * @param {global.Event} event
  27675. * Event that occured.
  27676. */
  27677. /**
  27678. * The generic point options for all series.
  27679. *
  27680. * In TypeScript you have to extend `PointOptionsObject` with an additional
  27681. * declaration to allow custom data options:
  27682. *
  27683. * ```
  27684. * declare interface PointOptionsObject {
  27685. * customProperty: string;
  27686. * }
  27687. * ```
  27688. *
  27689. * @interface Highcharts.PointOptionsObject
  27690. */
  27691. /**
  27692. * Possible option types for a data point.
  27693. *
  27694. * @typedef {number|string|Array<(number|string)>|Highcharts.PointOptionsObject|null} Highcharts.PointOptionsType
  27695. */
  27696. /**
  27697. * Gets fired when the point is removed using the `.remove()` method.
  27698. *
  27699. * @callback Highcharts.PointRemoveCallbackFunction
  27700. *
  27701. * @param {Highcharts.Point} this
  27702. * Point where the event occured.
  27703. *
  27704. * @param {global.Event} event
  27705. * Event that occured.
  27706. */
  27707. /**
  27708. * Possible key values for the point state options.
  27709. *
  27710. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.PointStateValue
  27711. */
  27712. /**
  27713. * Gets fired when the point is updated programmatically through the `.update()`
  27714. * method.
  27715. *
  27716. * @callback Highcharts.PointUpdateCallbackFunction
  27717. *
  27718. * @param {Highcharts.Point} this
  27719. * Point where the event occured.
  27720. *
  27721. * @param {Highcharts.PointUpdateEventObject} event
  27722. * Event that occured.
  27723. */
  27724. /**
  27725. * Information about the update event.
  27726. *
  27727. * @interface Highcharts.PointUpdateEventObject
  27728. * @extends global.Event
  27729. */ /**
  27730. * Options data of the update event.
  27731. * @name Highcharts.PointUpdateEventObject#options
  27732. * @type {Highcharts.PointOptionsType}
  27733. */
  27734. ''; // detach doclet above
  27735. /* eslint-disable no-invalid-this, valid-jsdoc */
  27736. /**
  27737. * The Point object. The point objects are generated from the `series.data`
  27738. * configuration objects or raw numbers. They can be accessed from the
  27739. * `Series.points` array. Other ways to instantiate points are through {@link
  27740. * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
  27741. *
  27742. * @class
  27743. * @name Highcharts.Point
  27744. */
  27745. var Point = /** @class */ (function () {
  27746. function Point() {
  27747. /* *
  27748. *
  27749. * Properties
  27750. *
  27751. * */
  27752. /**
  27753. * For categorized axes this property holds the category name for the
  27754. * point. For other axes it holds the X value.
  27755. *
  27756. * @name Highcharts.Point#category
  27757. * @type {string}
  27758. */
  27759. this.category = void 0;
  27760. /**
  27761. * The point's current color index, used in styled mode instead of
  27762. * `color`. The color index is inserted in class names used for styling.
  27763. *
  27764. * @name Highcharts.Point#colorIndex
  27765. * @type {number}
  27766. */
  27767. this.colorIndex = void 0;
  27768. this.formatPrefix = 'point';
  27769. this.id = void 0;
  27770. this.isNull = false;
  27771. /**
  27772. * The name of the point. The name can be given as the first position of the
  27773. * point configuration array, or as a `name` property in the configuration:
  27774. *
  27775. * @example
  27776. * // Array config
  27777. * data: [
  27778. * ['John', 1],
  27779. * ['Jane', 2]
  27780. * ]
  27781. *
  27782. * // Object config
  27783. * data: [{
  27784. * name: 'John',
  27785. * y: 1
  27786. * }, {
  27787. * name: 'Jane',
  27788. * y: 2
  27789. * }]
  27790. *
  27791. * @name Highcharts.Point#name
  27792. * @type {string}
  27793. */
  27794. this.name = void 0;
  27795. /**
  27796. * The point's options as applied in the initial configuration, or
  27797. * extended through `Point.update`.
  27798. *
  27799. * In TypeScript you have to extend `PointOptionsObject` via an
  27800. * additional interface to allow custom data options:
  27801. *
  27802. * ```
  27803. * declare interface PointOptionsObject {
  27804. * customProperty: string;
  27805. * }
  27806. * ```
  27807. *
  27808. * @name Highcharts.Point#options
  27809. * @type {Highcharts.PointOptionsObject}
  27810. */
  27811. this.options = void 0;
  27812. /**
  27813. * The percentage for points in a stacked series or pies.
  27814. *
  27815. * @name Highcharts.Point#percentage
  27816. * @type {number|undefined}
  27817. */
  27818. this.percentage = void 0;
  27819. this.selected = false;
  27820. /**
  27821. * The series object associated with the point.
  27822. *
  27823. * @name Highcharts.Point#series
  27824. * @type {Highcharts.Series}
  27825. */
  27826. this.series = void 0;
  27827. /**
  27828. * The total of values in either a stack for stacked series, or a pie in a
  27829. * pie series.
  27830. *
  27831. * @name Highcharts.Point#total
  27832. * @type {number|undefined}
  27833. */
  27834. this.total = void 0;
  27835. /**
  27836. * For certain series types, like pie charts, where individual points can
  27837. * be shown or hidden.
  27838. *
  27839. * @name Highcharts.Point#visible
  27840. * @type {boolean}
  27841. * @default true
  27842. */
  27843. this.visible = true;
  27844. this.x = void 0;
  27845. }
  27846. /* *
  27847. *
  27848. * Functions
  27849. *
  27850. * */
  27851. /**
  27852. * Animate SVG elements associated with the point.
  27853. *
  27854. * @private
  27855. * @function Highcharts.Point#animateBeforeDestroy
  27856. */
  27857. Point.prototype.animateBeforeDestroy = function () {
  27858. var point = this, animateParams = { x: point.startXPos, opacity: 0 }, isDataLabel, graphicalProps = point.getGraphicalProps();
  27859. graphicalProps.singular.forEach(function (prop) {
  27860. isDataLabel = prop === 'dataLabel';
  27861. point[prop] = point[prop].animate(isDataLabel ? {
  27862. x: point[prop].startXPos,
  27863. y: point[prop].startYPos,
  27864. opacity: 0
  27865. } : animateParams);
  27866. });
  27867. graphicalProps.plural.forEach(function (plural) {
  27868. point[plural].forEach(function (item) {
  27869. if (item.element) {
  27870. item.animate(extend({ x: point.startXPos }, (item.startYPos ? {
  27871. x: item.startXPos,
  27872. y: item.startYPos
  27873. } : {})));
  27874. }
  27875. });
  27876. });
  27877. };
  27878. /**
  27879. * Apply the options containing the x and y data and possible some extra
  27880. * properties. Called on point init or from point.update.
  27881. *
  27882. * @private
  27883. * @function Highcharts.Point#applyOptions
  27884. *
  27885. * @param {Highcharts.PointOptionsType} options
  27886. * The point options as defined in series.data.
  27887. *
  27888. * @param {number} [x]
  27889. * Optionally, the x value.
  27890. *
  27891. * @return {Highcharts.Point}
  27892. * The Point instance.
  27893. */
  27894. Point.prototype.applyOptions = function (options, x) {
  27895. var point = this, series = point.series, pointValKey = series.options.pointValKey || series.pointValKey;
  27896. options = Point.prototype.optionsToObject.call(this, options);
  27897. // copy options directly to point
  27898. extend(point, options);
  27899. point.options = point.options ? extend(point.options, options) : options;
  27900. // Since options are copied into the Point instance, some accidental
  27901. // options must be shielded (#5681)
  27902. if (options.group) {
  27903. delete point.group;
  27904. }
  27905. if (options.dataLabels) {
  27906. delete point.dataLabels;
  27907. }
  27908. /**
  27909. * The y value of the point.
  27910. * @name Highcharts.Point#y
  27911. * @type {number|undefined}
  27912. */
  27913. // For higher dimension series types. For instance, for ranges, point.y
  27914. // is mapped to point.low.
  27915. if (pointValKey) {
  27916. point.y = Point.prototype.getNestedProperty.call(point, pointValKey);
  27917. }
  27918. point.isNull = pick(point.isValid && !point.isValid(), point.x === null || !isNumber(point.y)); // #3571, check for NaN
  27919. point.formatPrefix = point.isNull ? 'null' : 'point'; // #9233, #10874
  27920. // The point is initially selected by options (#5777)
  27921. if (point.selected) {
  27922. point.state = 'select';
  27923. }
  27924. /**
  27925. * The x value of the point.
  27926. * @name Highcharts.Point#x
  27927. * @type {number}
  27928. */
  27929. // If no x is set by now, get auto incremented value. All points must
  27930. // have an x value, however the y value can be null to create a gap in
  27931. // the series
  27932. if ('name' in point &&
  27933. typeof x === 'undefined' &&
  27934. series.xAxis &&
  27935. series.xAxis.hasNames) {
  27936. point.x = series.xAxis.nameToX(point);
  27937. }
  27938. if (typeof point.x === 'undefined' && series) {
  27939. if (typeof x === 'undefined') {
  27940. point.x = series.autoIncrement(point);
  27941. }
  27942. else {
  27943. point.x = x;
  27944. }
  27945. }
  27946. return point;
  27947. };
  27948. /**
  27949. * Destroy a point to clear memory. Its reference still stays in
  27950. * `series.data`.
  27951. *
  27952. * @private
  27953. * @function Highcharts.Point#destroy
  27954. */
  27955. Point.prototype.destroy = function () {
  27956. var point = this, series = point.series, chart = series.chart, dataSorting = series.options.dataSorting, hoverPoints = chart.hoverPoints, globalAnimation = point.series.chart.renderer.globalAnimation, animation = animObject(globalAnimation), prop;
  27957. /**
  27958. * Allow to call after animation.
  27959. * @private
  27960. */
  27961. function destroyPoint() {
  27962. // Remove all events and elements
  27963. if (point.graphic || point.dataLabel || point.dataLabels) {
  27964. removeEvent(point);
  27965. point.destroyElements();
  27966. }
  27967. for (prop in point) { // eslint-disable-line guard-for-in
  27968. point[prop] = null;
  27969. }
  27970. }
  27971. if (point.legendItem) { // pies have legend items
  27972. chart.legend.destroyItem(point);
  27973. }
  27974. if (hoverPoints) {
  27975. point.setState();
  27976. erase(hoverPoints, point);
  27977. if (!hoverPoints.length) {
  27978. chart.hoverPoints = null;
  27979. }
  27980. }
  27981. if (point === chart.hoverPoint) {
  27982. point.onMouseOut();
  27983. }
  27984. // Remove properties after animation
  27985. if (!dataSorting || !dataSorting.enabled) {
  27986. destroyPoint();
  27987. }
  27988. else {
  27989. this.animateBeforeDestroy();
  27990. syncTimeout(destroyPoint, animation.duration);
  27991. }
  27992. chart.pointCount--;
  27993. };
  27994. /**
  27995. * Destroy SVG elements associated with the point.
  27996. *
  27997. * @private
  27998. * @function Highcharts.Point#destroyElements
  27999. * @param {Highcharts.Dictionary<number>} [kinds]
  28000. */
  28001. Point.prototype.destroyElements = function (kinds) {
  28002. var point = this, props = point.getGraphicalProps(kinds);
  28003. props.singular.forEach(function (prop) {
  28004. point[prop] = point[prop].destroy();
  28005. });
  28006. props.plural.forEach(function (plural) {
  28007. point[plural].forEach(function (item) {
  28008. if (item.element) {
  28009. item.destroy();
  28010. }
  28011. });
  28012. delete point[plural];
  28013. });
  28014. };
  28015. /**
  28016. * Fire an event on the Point object.
  28017. *
  28018. * @private
  28019. * @function Highcharts.Point#firePointEvent
  28020. *
  28021. * @param {string} eventType
  28022. * Type of the event.
  28023. *
  28024. * @param {Highcharts.Dictionary<any>|Event} [eventArgs]
  28025. * Additional event arguments.
  28026. *
  28027. * @param {Highcharts.EventCallbackFunction<Highcharts.Point>|Function} [defaultFunction]
  28028. * Default event handler.
  28029. *
  28030. * @fires Highcharts.Point#event:*
  28031. */
  28032. Point.prototype.firePointEvent = function (eventType, eventArgs, defaultFunction) {
  28033. var point = this, series = this.series, seriesOptions = series.options;
  28034. // load event handlers on demand to save time on mouseover/out
  28035. if (seriesOptions.point.events[eventType] ||
  28036. (point.options &&
  28037. point.options.events &&
  28038. point.options.events[eventType])) {
  28039. point.importEvents();
  28040. }
  28041. // add default handler if in selection mode
  28042. if (eventType === 'click' && seriesOptions.allowPointSelect) {
  28043. defaultFunction = function (event) {
  28044. // Control key is for Windows, meta (= Cmd key) for Mac, Shift
  28045. // for Opera.
  28046. if (point.select) { // #2911
  28047. point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
  28048. }
  28049. };
  28050. }
  28051. fireEvent(point, eventType, eventArgs, defaultFunction);
  28052. };
  28053. /**
  28054. * Get the CSS class names for individual points. Used internally where the
  28055. * returned value is set on every point.
  28056. *
  28057. * @function Highcharts.Point#getClassName
  28058. *
  28059. * @return {string}
  28060. * The class names.
  28061. */
  28062. Point.prototype.getClassName = function () {
  28063. var point = this;
  28064. return 'highcharts-point' +
  28065. (point.selected ? ' highcharts-point-select' : '') +
  28066. (point.negative ? ' highcharts-negative' : '') +
  28067. (point.isNull ? ' highcharts-null-point' : '') +
  28068. (typeof point.colorIndex !== 'undefined' ?
  28069. ' highcharts-color-' + point.colorIndex : '') +
  28070. (point.options.className ? ' ' + point.options.className : '') +
  28071. (point.zone && point.zone.className ? ' ' +
  28072. point.zone.className.replace('highcharts-negative', '') : '');
  28073. };
  28074. /**
  28075. * Get props of all existing graphical point elements.
  28076. *
  28077. * @private
  28078. * @function Highcharts.Point#getGraphicalProps
  28079. * @param {Highcharts.Dictionary<number>} [kinds]
  28080. * @return {Highcharts.PointGraphicalProps}
  28081. */
  28082. Point.prototype.getGraphicalProps = function (kinds) {
  28083. var point = this, props = [], prop, i, graphicalProps = { singular: [], plural: [] };
  28084. kinds = kinds || { graphic: 1, dataLabel: 1 };
  28085. if (kinds.graphic) {
  28086. props.push('graphic', 'shadowGroup');
  28087. }
  28088. if (kinds.dataLabel) {
  28089. props.push('dataLabel', 'dataLabelUpper', 'connector');
  28090. }
  28091. i = props.length;
  28092. while (i--) {
  28093. prop = props[i];
  28094. if (point[prop]) {
  28095. graphicalProps.singular.push(prop);
  28096. }
  28097. }
  28098. ['dataLabel', 'connector'].forEach(function (prop) {
  28099. var plural = prop + 's';
  28100. if (kinds[prop] && point[plural]) {
  28101. graphicalProps.plural.push(plural);
  28102. }
  28103. });
  28104. return graphicalProps;
  28105. };
  28106. /**
  28107. * Return the configuration hash needed for the data label and tooltip
  28108. * formatters.
  28109. *
  28110. * @function Highcharts.Point#getLabelConfig
  28111. *
  28112. * @return {Highcharts.PointLabelObject}
  28113. * Abstract object used in formatters and formats.
  28114. */
  28115. Point.prototype.getLabelConfig = function () {
  28116. return {
  28117. x: this.category,
  28118. y: this.y,
  28119. color: this.color,
  28120. colorIndex: this.colorIndex,
  28121. key: this.name || this.category,
  28122. series: this.series,
  28123. point: this,
  28124. percentage: this.percentage,
  28125. total: this.total || this.stackTotal
  28126. };
  28127. };
  28128. /**
  28129. * Returns the value of the point property for a given value.
  28130. * @private
  28131. */
  28132. Point.prototype.getNestedProperty = function (key) {
  28133. if (!key) {
  28134. return;
  28135. }
  28136. if (key.indexOf('custom.') === 0) {
  28137. return getNestedProperty(key, this.options);
  28138. }
  28139. return this[key];
  28140. };
  28141. /**
  28142. * In a series with `zones`, return the zone that the point belongs to.
  28143. *
  28144. * @function Highcharts.Point#getZone
  28145. *
  28146. * @return {Highcharts.SeriesZonesOptionsObject}
  28147. * The zone item.
  28148. */
  28149. Point.prototype.getZone = function () {
  28150. var series = this.series, zones = series.zones, zoneAxis = series.zoneAxis || 'y', i = 0, zone;
  28151. zone = zones[i];
  28152. while (this[zoneAxis] >= zone.value) {
  28153. zone = zones[++i];
  28154. }
  28155. // For resetting or reusing the point (#8100)
  28156. if (!this.nonZonedColor) {
  28157. this.nonZonedColor = this.color;
  28158. }
  28159. if (zone && zone.color && !this.options.color) {
  28160. this.color = zone.color;
  28161. }
  28162. else {
  28163. this.color = this.nonZonedColor;
  28164. }
  28165. return zone;
  28166. };
  28167. /**
  28168. * Utility to check if point has new shape type. Used in column series and
  28169. * all others that are based on column series.
  28170. *
  28171. * @return boolean|undefined
  28172. */
  28173. Point.prototype.hasNewShapeType = function () {
  28174. var point = this;
  28175. var oldShapeType = point.graphic &&
  28176. (point.graphic.symbolName || point.graphic.element.nodeName);
  28177. return oldShapeType !== this.shapeType;
  28178. };
  28179. /**
  28180. * Initialize the point. Called internally based on the `series.data`
  28181. * option.
  28182. *
  28183. * @function Highcharts.Point#init
  28184. *
  28185. * @param {Highcharts.Series} series
  28186. * The series object containing this point.
  28187. *
  28188. * @param {Highcharts.PointOptionsType} options
  28189. * The data in either number, array or object format.
  28190. *
  28191. * @param {number} [x]
  28192. * Optionally, the X value of the point.
  28193. *
  28194. * @return {Highcharts.Point}
  28195. * The Point instance.
  28196. *
  28197. * @fires Highcharts.Point#event:afterInit
  28198. */
  28199. Point.prototype.init = function (series, options, x) {
  28200. this.series = series;
  28201. this.applyOptions(options, x);
  28202. // Add a unique ID to the point if none is assigned
  28203. this.id = defined(this.id) ? this.id : uniqueKey();
  28204. this.resolveColor();
  28205. series.chart.pointCount++;
  28206. fireEvent(this, 'afterInit');
  28207. return this;
  28208. };
  28209. /**
  28210. * Transform number or array configs into objects. Also called for object
  28211. * configs. Used internally to unify the different configuration formats for
  28212. * points. For example, a simple number `10` in a line series will be
  28213. * transformed to `{ y: 10 }`, and an array config like `[1, 10]` in a
  28214. * scatter series will be transformed to `{ x: 1, y: 10 }`.
  28215. *
  28216. * @function Highcharts.Point#optionsToObject
  28217. *
  28218. * @param {Highcharts.PointOptionsType} options
  28219. * The input option.
  28220. *
  28221. * @return {Highcharts.Dictionary<*>}
  28222. * Transformed options.
  28223. */
  28224. Point.prototype.optionsToObject = function (options) {
  28225. var ret = {}, series = this.series, keys = series.options.keys, pointArrayMap = keys || series.pointArrayMap || ['y'], valueCount = pointArrayMap.length, firstItemType, i = 0, j = 0;
  28226. if (isNumber(options) || options === null) {
  28227. ret[pointArrayMap[0]] = options;
  28228. }
  28229. else if (isArray(options)) {
  28230. // with leading x value
  28231. if (!keys && options.length > valueCount) {
  28232. firstItemType = typeof options[0];
  28233. if (firstItemType === 'string') {
  28234. ret.name = options[0];
  28235. }
  28236. else if (firstItemType === 'number') {
  28237. ret.x = options[0];
  28238. }
  28239. i++;
  28240. }
  28241. while (j < valueCount) {
  28242. // Skip undefined positions for keys
  28243. if (!keys || typeof options[i] !== 'undefined') {
  28244. if (pointArrayMap[j].indexOf('.') > 0) {
  28245. // Handle nested keys, e.g. ['color.pattern.image']
  28246. // Avoid function call unless necessary.
  28247. Point.prototype.setNestedProperty(ret, options[i], pointArrayMap[j]);
  28248. }
  28249. else {
  28250. ret[pointArrayMap[j]] = options[i];
  28251. }
  28252. }
  28253. i++;
  28254. j++;
  28255. }
  28256. }
  28257. else if (typeof options === 'object') {
  28258. ret = options;
  28259. // This is the fastest way to detect if there are individual point
  28260. // dataLabels that need to be considered in drawDataLabels. These
  28261. // can only occur in object configs.
  28262. if (options.dataLabels) {
  28263. series._hasPointLabels = true;
  28264. }
  28265. // Same approach as above for markers
  28266. if (options.marker) {
  28267. series._hasPointMarkers = true;
  28268. }
  28269. }
  28270. return ret;
  28271. };
  28272. /**
  28273. * @private
  28274. * @function Highcharts.Point#resolveColor
  28275. * @return {void}
  28276. */
  28277. Point.prototype.resolveColor = function () {
  28278. var series = this.series, colors, optionsChart = series.chart.options.chart, colorCount = optionsChart.colorCount, styledMode = series.chart.styledMode, colorIndex;
  28279. // remove points nonZonedColor for later recalculation
  28280. delete this.nonZonedColor;
  28281. /**
  28282. * The point's current color.
  28283. *
  28284. * @name Highcharts.Point#color
  28285. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  28286. */
  28287. if (!styledMode && !this.options.color) {
  28288. this.color = series.color; // #3445
  28289. }
  28290. if (series.options.colorByPoint) {
  28291. if (!styledMode) {
  28292. colors = series.options.colors || series.chart.options.colors;
  28293. this.color = this.color || colors[series.colorCounter];
  28294. colorCount = colors.length;
  28295. }
  28296. colorIndex = series.colorCounter;
  28297. series.colorCounter++;
  28298. // loop back to zero
  28299. if (series.colorCounter === colorCount) {
  28300. series.colorCounter = 0;
  28301. }
  28302. }
  28303. else {
  28304. colorIndex = series.colorIndex;
  28305. }
  28306. this.colorIndex = pick(this.colorIndex, colorIndex);
  28307. };
  28308. /**
  28309. * Set a value in an object, on the property defined by key. The key
  28310. * supports nested properties using dot notation. The function modifies the
  28311. * input object and does not make a copy.
  28312. *
  28313. * @function Highcharts.Point#setNestedProperty<T>
  28314. *
  28315. * @param {T} object
  28316. * The object to set the value on.
  28317. *
  28318. * @param {*} value
  28319. * The value to set.
  28320. *
  28321. * @param {string} key
  28322. * Key to the property to set.
  28323. *
  28324. * @return {T}
  28325. * The modified object.
  28326. */
  28327. Point.prototype.setNestedProperty = function (object, value, key) {
  28328. var nestedKeys = key.split('.');
  28329. nestedKeys.reduce(function (result, key, i, arr) {
  28330. var isLastKey = arr.length - 1 === i;
  28331. result[key] = (isLastKey ?
  28332. value :
  28333. isObject(result[key], true) ?
  28334. result[key] :
  28335. {});
  28336. return result[key];
  28337. }, object);
  28338. return object;
  28339. };
  28340. /**
  28341. * Extendable method for formatting each point's tooltip line.
  28342. *
  28343. * @function Highcharts.Point#tooltipFormatter
  28344. *
  28345. * @param {string} pointFormat
  28346. * The point format.
  28347. *
  28348. * @return {string}
  28349. * A string to be concatenated in to the common tooltip text.
  28350. */
  28351. Point.prototype.tooltipFormatter = function (pointFormat) {
  28352. // Insert options for valueDecimals, valuePrefix, and valueSuffix
  28353. var series = this.series, seriesTooltipOptions = series.tooltipOptions, valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), valuePrefix = seriesTooltipOptions.valuePrefix || '', valueSuffix = seriesTooltipOptions.valueSuffix || '';
  28354. // Replace default point style with class name
  28355. if (series.chart.styledMode) {
  28356. pointFormat =
  28357. series.chart.tooltip.styledModeFormat(pointFormat);
  28358. }
  28359. // Loop over the point array map and replace unformatted values with
  28360. // sprintf formatting markup
  28361. (series.pointArrayMap || ['y']).forEach(function (key) {
  28362. key = '{point.' + key; // without the closing bracket
  28363. if (valuePrefix || valueSuffix) {
  28364. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), valuePrefix + key + '}' + valueSuffix);
  28365. }
  28366. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), key + ':,.' + valueDecimals + 'f}');
  28367. });
  28368. return format(pointFormat, {
  28369. point: this,
  28370. series: this.series
  28371. }, series.chart);
  28372. };
  28373. return Point;
  28374. }());
  28375. H.Point = Point;
  28376. return Point;
  28377. });
  28378. _registerModule(_modules, 'parts/Series.js', [_modules['parts/Globals.js'], _modules['mixins/legend-symbol.js'], _modules['parts/Options.js'], _modules['parts/Point.js'], _modules['parts/SVGElement.js'], _modules['parts/Utilities.js']], function (H, LegendSymbolMixin, O, Point, SVGElement, U) {
  28379. /* *
  28380. *
  28381. * (c) 2010-2020 Torstein Honsi
  28382. *
  28383. * License: www.highcharts.com/license
  28384. *
  28385. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  28386. *
  28387. * */
  28388. var defaultOptions = O.defaultOptions;
  28389. var addEvent = U.addEvent, animObject = U.animObject, arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, erase = U.erase, error = U.error, extend = U.extend, find = U.find, fireEvent = U.fireEvent, getNestedProperty = U.getNestedProperty, isArray = U.isArray, isFunction = U.isFunction, isNumber = U.isNumber, isString = U.isString, merge = U.merge, objectEach = U.objectEach, pick = U.pick, removeEvent = U.removeEvent, seriesType = U.seriesType, splat = U.splat, syncTimeout = U.syncTimeout;
  28390. /**
  28391. * This is a placeholder type of the possible series options for
  28392. * [Highcharts](../highcharts/series), [Highstock](../highstock/series),
  28393. * [Highmaps](../highmaps/series), and [Gantt](../gantt/series).
  28394. *
  28395. * In TypeScript is this dynamically generated to reference all possible types
  28396. * of series options.
  28397. *
  28398. * @ignore-declaration
  28399. * @typedef {Highcharts.SeriesOptions|Highcharts.Dictionary<*>} Highcharts.SeriesOptionsType
  28400. */
  28401. /**
  28402. * Options for `dataSorting`.
  28403. *
  28404. * @interface Highcharts.DataSortingOptionsObject
  28405. * @since 8.0.0
  28406. */ /**
  28407. * Enable or disable data sorting for the series.
  28408. * @name Highcharts.DataSortingOptionsObject#enabled
  28409. * @type {boolean|undefined}
  28410. */ /**
  28411. * Whether to allow matching points by name in an update.
  28412. * @name Highcharts.DataSortingOptionsObject#matchByName
  28413. * @type {boolean|undefined}
  28414. */ /**
  28415. * Determines what data value should be used to sort by.
  28416. * @name Highcharts.DataSortingOptionsObject#sortKey
  28417. * @type {string|undefined}
  28418. */
  28419. /**
  28420. * Function callback when a series has been animated.
  28421. *
  28422. * @callback Highcharts.SeriesAfterAnimateCallbackFunction
  28423. *
  28424. * @param {Highcharts.Series} this
  28425. * The series where the event occured.
  28426. *
  28427. * @param {Highcharts.SeriesAfterAnimateEventObject} event
  28428. * Event arguments.
  28429. */
  28430. /**
  28431. * Event information regarding completed animation of a series.
  28432. *
  28433. * @interface Highcharts.SeriesAfterAnimateEventObject
  28434. */ /**
  28435. * Animated series.
  28436. * @name Highcharts.SeriesAfterAnimateEventObject#target
  28437. * @type {Highcharts.Series}
  28438. */ /**
  28439. * Event type.
  28440. * @name Highcharts.SeriesAfterAnimateEventObject#type
  28441. * @type {"afterAnimate"}
  28442. */
  28443. /**
  28444. * Function callback when the checkbox next to the series' name in the legend is
  28445. * clicked.
  28446. *
  28447. * @callback Highcharts.SeriesCheckboxClickCallbackFunction
  28448. *
  28449. * @param {Highcharts.Series} this
  28450. * The series where the event occured.
  28451. *
  28452. * @param {Highcharts.SeriesCheckboxClickEventObject} event
  28453. * Event arguments.
  28454. */
  28455. /**
  28456. * Event information regarding check of a series box.
  28457. *
  28458. * @interface Highcharts.SeriesCheckboxClickEventObject
  28459. */ /**
  28460. * Whether the box has been checked.
  28461. * @name Highcharts.SeriesCheckboxClickEventObject#checked
  28462. * @type {boolean}
  28463. */ /**
  28464. * Related series.
  28465. * @name Highcharts.SeriesCheckboxClickEventObject#item
  28466. * @type {Highcharts.Series}
  28467. */ /**
  28468. * Related series.
  28469. * @name Highcharts.SeriesCheckboxClickEventObject#target
  28470. * @type {Highcharts.Series}
  28471. */ /**
  28472. * Event type.
  28473. * @name Highcharts.SeriesCheckboxClickEventObject#type
  28474. * @type {"checkboxClick"}
  28475. */
  28476. /**
  28477. * Function callback when a series is clicked. Return false to cancel toogle
  28478. * actions.
  28479. *
  28480. * @callback Highcharts.SeriesClickCallbackFunction
  28481. *
  28482. * @param {Highcharts.Series} this
  28483. * The series where the event occured.
  28484. *
  28485. * @param {Highcharts.SeriesClickEventObject} event
  28486. * Event arguments.
  28487. */
  28488. /**
  28489. * Common information for a click event on a series.
  28490. *
  28491. * @interface Highcharts.SeriesClickEventObject
  28492. * @extends global.Event
  28493. */ /**
  28494. * Nearest point on the graph.
  28495. * @name Highcharts.SeriesClickEventObject#point
  28496. * @type {Highcharts.Point}
  28497. */
  28498. /**
  28499. * Gets fired when the series is hidden after chart generation time, either by
  28500. * clicking the legend item or by calling `.hide()`.
  28501. *
  28502. * @callback Highcharts.SeriesHideCallbackFunction
  28503. *
  28504. * @param {Highcharts.Series} this
  28505. * The series where the event occured.
  28506. *
  28507. * @param {global.Event} event
  28508. * The event that occured.
  28509. */
  28510. /**
  28511. * The SVG value used for the `stroke-linecap` and `stroke-linejoin` of a line
  28512. * graph.
  28513. *
  28514. * @typedef {"butt"|"round"|"square"|string} Highcharts.SeriesLinecapValue
  28515. */
  28516. /**
  28517. * Gets fired when the legend item belonging to the series is clicked. The
  28518. * default action is to toggle the visibility of the series. This can be
  28519. * prevented by returning `false` or calling `event.preventDefault()`.
  28520. *
  28521. * @callback Highcharts.SeriesLegendItemClickCallbackFunction
  28522. *
  28523. * @param {Highcharts.Series} this
  28524. * The series where the event occured.
  28525. *
  28526. * @param {Highcharts.SeriesLegendItemClickEventObject} event
  28527. * The event that occured.
  28528. */
  28529. /**
  28530. * Information about the event.
  28531. *
  28532. * @interface Highcharts.SeriesLegendItemClickEventObject
  28533. */ /**
  28534. * Related browser event.
  28535. * @name Highcharts.SeriesLegendItemClickEventObject#browserEvent
  28536. * @type {global.PointerEvent}
  28537. */ /**
  28538. * Prevent the default action of toggle the visibility of the series.
  28539. * @name Highcharts.SeriesLegendItemClickEventObject#preventDefault
  28540. * @type {Function}
  28541. */ /**
  28542. * Related series.
  28543. * @name Highcharts.SeriesCheckboxClickEventObject#target
  28544. * @type {Highcharts.Series}
  28545. */ /**
  28546. * Event type.
  28547. * @name Highcharts.SeriesCheckboxClickEventObject#type
  28548. * @type {"checkboxClick"}
  28549. */
  28550. /**
  28551. * Gets fired when the mouse leaves the graph.
  28552. *
  28553. * @callback Highcharts.SeriesMouseOutCallbackFunction
  28554. *
  28555. * @param {Highcharts.Series} this
  28556. * Series where the event occured.
  28557. *
  28558. * @param {global.PointerEvent} event
  28559. * Event that occured.
  28560. */
  28561. /**
  28562. * Gets fired when the mouse enters the graph.
  28563. *
  28564. * @callback Highcharts.SeriesMouseOverCallbackFunction
  28565. *
  28566. * @param {Highcharts.Series} this
  28567. * Series where the event occured.
  28568. *
  28569. * @param {global.PointerEvent} event
  28570. * Event that occured.
  28571. */
  28572. /**
  28573. * Translation and scale for the plot area of a series.
  28574. *
  28575. * @interface Highcharts.SeriesPlotBoxObject
  28576. */ /**
  28577. * @name Highcharts.SeriesPlotBoxObject#scaleX
  28578. * @type {number}
  28579. */ /**
  28580. * @name Highcharts.SeriesPlotBoxObject#scaleY
  28581. * @type {number}
  28582. */ /**
  28583. * @name Highcharts.SeriesPlotBoxObject#translateX
  28584. * @type {number}
  28585. */ /**
  28586. * @name Highcharts.SeriesPlotBoxObject#translateY
  28587. * @type {number}
  28588. */
  28589. /**
  28590. * Gets fired when the series is shown after chart generation time, either by
  28591. * clicking the legend item or by calling `.show()`.
  28592. *
  28593. * @callback Highcharts.SeriesShowCallbackFunction
  28594. *
  28595. * @param {Highcharts.Series} this
  28596. * Series where the event occured.
  28597. *
  28598. * @param {global.Event} event
  28599. * Event that occured.
  28600. */
  28601. /**
  28602. * Possible key values for the series state options.
  28603. *
  28604. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.SeriesStateValue
  28605. */
  28606. ''; // detach doclets above
  28607. var seriesTypes = H.seriesTypes, win = H.win;
  28608. /**
  28609. * This is the base series prototype that all other series types inherit from.
  28610. * A new series is initialized either through the
  28611. * [series](https://api.highcharts.com/highcharts/series)
  28612. * option structure, or after the chart is initialized, through
  28613. * {@link Highcharts.Chart#addSeries}.
  28614. *
  28615. * The object can be accessed in a number of ways. All series and point event
  28616. * handlers give a reference to the `series` object. The chart object has a
  28617. * {@link Highcharts.Chart#series|series} property that is a collection of all
  28618. * the chart's series. The point objects and axis objects also have the same
  28619. * reference.
  28620. *
  28621. * Another way to reference the series programmatically is by `id`. Add an id
  28622. * in the series configuration options, and get the series object by
  28623. * {@link Highcharts.Chart#get}.
  28624. *
  28625. * Configuration options for the series are given in three levels. Options for
  28626. * all series in a chart are given in the
  28627. * [plotOptions.series](https://api.highcharts.com/highcharts/plotOptions.series)
  28628. * object. Then options for all series of a specific type
  28629. * are given in the plotOptions of that type, for example `plotOptions.line`.
  28630. * Next, options for one single series are given in the series array, or as
  28631. * arguments to `chart.addSeries`.
  28632. *
  28633. * The data in the series is stored in various arrays.
  28634. *
  28635. * - First, `series.options.data` contains all the original config options for
  28636. * each point whether added by options or methods like `series.addPoint`.
  28637. *
  28638. * - Next, `series.data` contains those values converted to points, but in case
  28639. * the series data length exceeds the `cropThreshold`, or if the data is
  28640. * grouped, `series.data` doesn't contain all the points. It only contains the
  28641. * points that have been created on demand.
  28642. *
  28643. * - Then there's `series.points` that contains all currently visible point
  28644. * objects. In case of cropping, the cropped-away points are not part of this
  28645. * array. The `series.points` array starts at `series.cropStart` compared to
  28646. * `series.data` and `series.options.data`. If however the series data is
  28647. * grouped, these can't be correlated one to one.
  28648. *
  28649. * - `series.xData` and `series.processedXData` contain clean x values,
  28650. * equivalent to `series.data` and `series.points`.
  28651. *
  28652. * - `series.yData` and `series.processedYData` contain clean y values,
  28653. * equivalent to `series.data` and `series.points`.
  28654. *
  28655. * @class
  28656. * @name Highcharts.Series
  28657. *
  28658. * @param {Highcharts.Chart} chart
  28659. * The chart instance.
  28660. *
  28661. * @param {Highcharts.SeriesOptionsType|object} options
  28662. * The series options.
  28663. */ /**
  28664. * The line series is the base type and is therefor the series base prototype.
  28665. *
  28666. * @private
  28667. * @class
  28668. * @name Highcharts.seriesTypes.line
  28669. *
  28670. * @augments Highcharts.Series
  28671. */
  28672. H.Series = seriesType('line',
  28673. /**
  28674. * Series options for specific data and the data itself. In TypeScript you
  28675. * have to cast the series options to specific series types, to get all
  28676. * possible options for a series.
  28677. *
  28678. * @example
  28679. * // TypeScript example
  28680. * Highcharts.chart('container', {
  28681. * series: [{
  28682. * color: '#06C',
  28683. * data: [[0, 1], [2, 3]]
  28684. * } as Highcharts.SeriesLineOptions ]
  28685. * });
  28686. *
  28687. * @type {Array<*>}
  28688. * @apioption series
  28689. */
  28690. /**
  28691. * An id for the series. This can be used after render time to get a pointer
  28692. * to the series object through `chart.get()`.
  28693. *
  28694. * @sample {highcharts} highcharts/plotoptions/series-id/
  28695. * Get series by id
  28696. *
  28697. * @type {string}
  28698. * @since 1.2.0
  28699. * @apioption series.id
  28700. */
  28701. /**
  28702. * The index of the series in the chart, affecting the internal index in the
  28703. * `chart.series` array, the visible Z index as well as the order in the
  28704. * legend.
  28705. *
  28706. * @type {number}
  28707. * @since 2.3.0
  28708. * @apioption series.index
  28709. */
  28710. /**
  28711. * The sequential index of the series in the legend.
  28712. *
  28713. * @see [legend.reversed](#legend.reversed),
  28714. * [yAxis.reversedStacks](#yAxis.reversedStacks)
  28715. *
  28716. * @sample {highcharts|highstock} highcharts/series/legendindex/
  28717. * Legend in opposite order
  28718. *
  28719. * @type {number}
  28720. * @apioption series.legendIndex
  28721. */
  28722. /**
  28723. * The name of the series as shown in the legend, tooltip etc.
  28724. *
  28725. * @sample {highcharts} highcharts/series/name/
  28726. * Series name
  28727. * @sample {highmaps} maps/demo/category-map/
  28728. * Series name
  28729. *
  28730. * @type {string}
  28731. * @apioption series.name
  28732. */
  28733. /**
  28734. * This option allows grouping series in a stacked chart. The stack option
  28735. * can be a string or anything else, as long as the grouped series' stack
  28736. * options match each other after conversion into a string.
  28737. *
  28738. * @sample {highcharts} highcharts/series/stack/
  28739. * Stacked and grouped columns
  28740. *
  28741. * @type {number|string}
  28742. * @since 2.1
  28743. * @product highcharts highstock
  28744. * @apioption series.stack
  28745. */
  28746. /**
  28747. * The type of series, for example `line` or `column`. By default, the
  28748. * series type is inherited from [chart.type](#chart.type), so unless the
  28749. * chart is a combination of series types, there is no need to set it on the
  28750. * series level.
  28751. *
  28752. * @sample {highcharts} highcharts/series/type/
  28753. * Line and column in the same chart
  28754. * @sample highcharts/series/type-dynamic/
  28755. * Dynamic types with button selector
  28756. * @sample {highmaps} maps/demo/mapline-mappoint/
  28757. * Multiple types in the same map
  28758. *
  28759. * @type {string}
  28760. * @apioption series.type
  28761. */
  28762. /**
  28763. * When using dual or multiple x axes, this number defines which xAxis the
  28764. * particular series is connected to. It refers to either the
  28765. * {@link #xAxis.id|axis id}
  28766. * or the index of the axis in the xAxis array, with 0 being the first.
  28767. *
  28768. * @type {number|string}
  28769. * @default 0
  28770. * @product highcharts highstock
  28771. * @apioption series.xAxis
  28772. */
  28773. /**
  28774. * When using dual or multiple y axes, this number defines which yAxis the
  28775. * particular series is connected to. It refers to either the
  28776. * {@link #yAxis.id|axis id}
  28777. * or the index of the axis in the yAxis array, with 0 being the first.
  28778. *
  28779. * @sample {highcharts} highcharts/series/yaxis/
  28780. * Apply the column series to the secondary Y axis
  28781. *
  28782. * @type {number|string}
  28783. * @default 0
  28784. * @product highcharts highstock
  28785. * @apioption series.yAxis
  28786. */
  28787. /**
  28788. * Define the visual z index of the series.
  28789. *
  28790. * @sample {highcharts} highcharts/plotoptions/series-zindex-default/
  28791. * With no z index, the series defined last are on top
  28792. * @sample {highcharts} highcharts/plotoptions/series-zindex/
  28793. * With a z index, the series with the highest z index is on top
  28794. * @sample {highstock} highcharts/plotoptions/series-zindex-default/
  28795. * With no z index, the series defined last are on top
  28796. * @sample {highstock} highcharts/plotoptions/series-zindex/
  28797. * With a z index, the series with the highest z index is on top
  28798. *
  28799. * @type {number}
  28800. * @product highcharts highstock
  28801. * @apioption series.zIndex
  28802. */
  28803. null,
  28804. /**
  28805. * General options for all series types.
  28806. *
  28807. * @optionparent plotOptions.series
  28808. */
  28809. {
  28810. /**
  28811. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  28812. * of a line graph. Round means that lines are rounded in the ends and
  28813. * bends.
  28814. *
  28815. * @type {Highcharts.SeriesLinecapValue}
  28816. * @default round
  28817. * @since 3.0.7
  28818. * @apioption plotOptions.line.linecap
  28819. */
  28820. /**
  28821. * Pixel width of the graph line.
  28822. *
  28823. * @see In styled mode, the line stroke-width can be set with the
  28824. * `.highcharts-graph` class name.
  28825. *
  28826. * @sample {highcharts} highcharts/plotoptions/series-linewidth-general/
  28827. * On all series
  28828. * @sample {highcharts} highcharts/plotoptions/series-linewidth-specific/
  28829. * On one single series
  28830. *
  28831. * @product highcharts highstock
  28832. *
  28833. * @private
  28834. */
  28835. lineWidth: 2,
  28836. /**
  28837. * For some series, there is a limit that shuts down initial animation
  28838. * by default when the total number of points in the chart is too high.
  28839. * For example, for a column chart and its derivatives, animation does
  28840. * not run if there is more than 250 points totally. To disable this
  28841. * cap, set `animationLimit` to `Infinity`.
  28842. *
  28843. * @type {number}
  28844. * @apioption plotOptions.series.animationLimit
  28845. */
  28846. /**
  28847. * Allow this series' points to be selected by clicking on the graphic
  28848. * (columns, point markers, pie slices, map areas etc).
  28849. *
  28850. * The selected points can be handled by point select and unselect
  28851. * events, or collectively by the [getSelectedPoints
  28852. * ](/class-reference/Highcharts.Chart#getSelectedPoints) function.
  28853. *
  28854. * And alternative way of selecting points is through dragging.
  28855. *
  28856. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-line/
  28857. * Line
  28858. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-column/
  28859. * Column
  28860. * @sample {highcharts} highcharts/plotoptions/series-allowpointselect-pie/
  28861. * Pie
  28862. * @sample {highcharts} highcharts/chart/events-selection-points/
  28863. * Select a range of points through a drag selection
  28864. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  28865. * Map area
  28866. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  28867. * Map bubble
  28868. *
  28869. * @since 1.2.0
  28870. *
  28871. * @private
  28872. */
  28873. allowPointSelect: false,
  28874. /**
  28875. * When true, each point or column edge is rounded to its nearest pixel
  28876. * in order to render sharp on screen. In some cases, when there are a
  28877. * lot of densely packed columns, this leads to visible difference
  28878. * in column widths or distance between columns. In these cases,
  28879. * setting `crisp` to `false` may look better, even though each column
  28880. * is rendered blurry.
  28881. *
  28882. * @sample {highcharts} highcharts/plotoptions/column-crisp-false/
  28883. * Crisp is false
  28884. *
  28885. * @since 5.0.10
  28886. * @product highcharts highstock gantt
  28887. *
  28888. * @private
  28889. */
  28890. crisp: true,
  28891. /**
  28892. * If true, a checkbox is displayed next to the legend item to allow
  28893. * selecting the series. The state of the checkbox is determined by
  28894. * the `selected` option.
  28895. *
  28896. * @productdesc {highmaps}
  28897. * Note that if a `colorAxis` is defined, the color axis is represented
  28898. * in the legend, not the series.
  28899. *
  28900. * @sample {highcharts} highcharts/plotoptions/series-showcheckbox-true/
  28901. * Show select box
  28902. *
  28903. * @since 1.2.0
  28904. *
  28905. * @private
  28906. */
  28907. showCheckbox: false,
  28908. /**
  28909. * Enable or disable the initial animation when a series is displayed.
  28910. * The animation can also be set as a configuration object. Please
  28911. * note that this option only applies to the initial animation of the
  28912. * series itself. For other animations, see [chart.animation](
  28913. * #chart.animation) and the animation parameter under the API methods.
  28914. * The following properties are supported:
  28915. *
  28916. * - `duration`: The duration of the animation in milliseconds.
  28917. *
  28918. * - `easing`: Can be a string reference to an easing function set on
  28919. * the `Math` object or a function. See the _Custom easing function_
  28920. * demo below.
  28921. *
  28922. * Due to poor performance, animation is disabled in old IE browsers
  28923. * for several chart types.
  28924. *
  28925. * @sample {highcharts} highcharts/plotoptions/series-animation-disabled/
  28926. * Animation disabled
  28927. * @sample {highcharts} highcharts/plotoptions/series-animation-slower/
  28928. * Slower animation
  28929. * @sample {highcharts} highcharts/plotoptions/series-animation-easing/
  28930. * Custom easing function
  28931. * @sample {highstock} stock/plotoptions/animation-slower/
  28932. * Slower animation
  28933. * @sample {highstock} stock/plotoptions/animation-easing/
  28934. * Custom easing function
  28935. * @sample {highmaps} maps/plotoptions/series-animation-true/
  28936. * Animation enabled on map series
  28937. * @sample {highmaps} maps/plotoptions/mapbubble-animation-false/
  28938. * Disabled on mapbubble series
  28939. *
  28940. * @type {boolean|Highcharts.AnimationOptionsObject}
  28941. * @default {highcharts} true
  28942. * @default {highstock} true
  28943. * @default {highmaps} false
  28944. *
  28945. * @private
  28946. */
  28947. animation: {
  28948. /** @internal */
  28949. duration: 1000
  28950. },
  28951. /**
  28952. * An additional class name to apply to the series' graphical elements.
  28953. * This option does not replace default class names of the graphical
  28954. * element.
  28955. *
  28956. * @type {string}
  28957. * @since 5.0.0
  28958. * @apioption plotOptions.series.className
  28959. */
  28960. /**
  28961. * Disable this option to allow series rendering in the whole plotting
  28962. * area.
  28963. *
  28964. * **Note:** Clipping should be always enabled when
  28965. * [chart.zoomType](#chart.zoomType) is set
  28966. *
  28967. * @sample {highcharts} highcharts/plotoptions/series-clip/
  28968. * Disabled clipping
  28969. *
  28970. * @default true
  28971. * @type {boolean}
  28972. * @since 3.0.0
  28973. * @apioption plotOptions.series.clip
  28974. */
  28975. /**
  28976. * The main color of the series. In line type series it applies to the
  28977. * line and the point markers unless otherwise specified. In bar type
  28978. * series it applies to the bars unless a color is specified per point.
  28979. * The default value is pulled from the `options.colors` array.
  28980. *
  28981. * In styled mode, the color can be defined by the
  28982. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  28983. * color can be set with the `.highcharts-series`,
  28984. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  28985. * `.highcharts-series-{n}` class, or individual classes given by the
  28986. * `className` option.
  28987. *
  28988. * @productdesc {highmaps}
  28989. * In maps, the series color is rarely used, as most choropleth maps use
  28990. * the color to denote the value of each point. The series color can
  28991. * however be used in a map with multiple series holding categorized
  28992. * data.
  28993. *
  28994. * @sample {highcharts} highcharts/plotoptions/series-color-general/
  28995. * General plot option
  28996. * @sample {highcharts} highcharts/plotoptions/series-color-specific/
  28997. * One specific series
  28998. * @sample {highcharts} highcharts/plotoptions/series-color-area/
  28999. * Area color
  29000. * @sample {highcharts} highcharts/series/infographic/
  29001. * Pattern fill
  29002. * @sample {highmaps} maps/demo/category-map/
  29003. * Category map by multiple series
  29004. *
  29005. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29006. * @apioption plotOptions.series.color
  29007. */
  29008. /**
  29009. * Styled mode only. A specific color index to use for the series, so
  29010. * its graphic representations are given the class name
  29011. * `highcharts-color-{n}`.
  29012. *
  29013. * @type {number}
  29014. * @since 5.0.0
  29015. * @apioption plotOptions.series.colorIndex
  29016. */
  29017. /**
  29018. * Whether to connect a graph line across null points, or render a gap
  29019. * between the two points on either side of the null.
  29020. *
  29021. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-false/
  29022. * False by default
  29023. * @sample {highcharts} highcharts/plotoptions/series-connectnulls-true/
  29024. * True
  29025. *
  29026. * @type {boolean}
  29027. * @default false
  29028. * @product highcharts highstock
  29029. * @apioption plotOptions.series.connectNulls
  29030. */
  29031. /**
  29032. * You can set the cursor to "pointer" if you have click events attached
  29033. * to the series, to signal to the user that the points and lines can
  29034. * be clicked.
  29035. *
  29036. * In styled mode, the series cursor can be set with the same classes
  29037. * as listed under [series.color](#plotOptions.series.color).
  29038. *
  29039. * @sample {highcharts} highcharts/plotoptions/series-cursor-line/
  29040. * On line graph
  29041. * @sample {highcharts} highcharts/plotoptions/series-cursor-column/
  29042. * On columns
  29043. * @sample {highcharts} highcharts/plotoptions/series-cursor-scatter/
  29044. * On scatter markers
  29045. * @sample {highstock} stock/plotoptions/cursor/
  29046. * Pointer on a line graph
  29047. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  29048. * Map area
  29049. * @sample {highmaps} maps/plotoptions/mapbubble-allowpointselect/
  29050. * Map bubble
  29051. *
  29052. * @type {string|Highcharts.CursorValue}
  29053. * @apioption plotOptions.series.cursor
  29054. */
  29055. /**
  29056. * A reserved subspace to store options and values for customized
  29057. * functionality. Here you can add additional data for your own event
  29058. * callbacks and formatter callbacks.
  29059. *
  29060. * @sample {highcharts} highcharts/point/custom/
  29061. * Point and series with custom data
  29062. *
  29063. * @type {Highcharts.Dictionary<*>}
  29064. * @apioption plotOptions.series.custom
  29065. */
  29066. /**
  29067. * Name of the dash style to use for the graph, or for some series types
  29068. * the outline of each shape.
  29069. *
  29070. * In styled mode, the
  29071. * [stroke dash-array](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/series-dashstyle/)
  29072. * can be set with the same classes as listed under
  29073. * [series.color](#plotOptions.series.color).
  29074. *
  29075. * @sample {highcharts} highcharts/plotoptions/series-dashstyle-all/
  29076. * Possible values demonstrated
  29077. * @sample {highcharts} highcharts/plotoptions/series-dashstyle/
  29078. * Chart suitable for printing in black and white
  29079. * @sample {highstock} highcharts/plotoptions/series-dashstyle-all/
  29080. * Possible values demonstrated
  29081. * @sample {highmaps} highcharts/plotoptions/series-dashstyle-all/
  29082. * Possible values demonstrated
  29083. * @sample {highmaps} maps/plotoptions/series-dashstyle/
  29084. * Dotted borders on a map
  29085. *
  29086. * @type {Highcharts.DashStyleValue}
  29087. * @default Solid
  29088. * @since 2.1
  29089. * @apioption plotOptions.series.dashStyle
  29090. */
  29091. /**
  29092. * A description of the series to add to the screen reader information
  29093. * about the series.
  29094. *
  29095. * @type {string}
  29096. * @since 5.0.0
  29097. * @requires modules/accessibility
  29098. * @apioption plotOptions.series.description
  29099. */
  29100. /**
  29101. * Options for the series data sorting.
  29102. *
  29103. * @type {Highcharts.DataSortingOptionsObject}
  29104. * @since 8.0.0
  29105. * @product highcharts highstock
  29106. * @apioption plotOptions.series.dataSorting
  29107. */
  29108. /**
  29109. * Enable or disable data sorting for the series. Use [xAxis.reversed](
  29110. * #xAxis.reversed) to change the sorting order.
  29111. *
  29112. * @sample {highcharts} highcharts/datasorting/animation/
  29113. * Data sorting in scatter-3d
  29114. * @sample {highcharts} highcharts/datasorting/labels-animation/
  29115. * Axis labels animation
  29116. * @sample {highcharts} highcharts/datasorting/dependent-sorting/
  29117. * Dependent series sorting
  29118. * @sample {highcharts} highcharts/datasorting/independent-sorting/
  29119. * Independent series sorting
  29120. *
  29121. * @type {boolean}
  29122. * @since 8.0.0
  29123. * @apioption plotOptions.series.dataSorting.enabled
  29124. */
  29125. /**
  29126. * Whether to allow matching points by name in an update. If this option
  29127. * is disabled, points will be matched by order.
  29128. *
  29129. * @sample {highcharts} highcharts/datasorting/match-by-name/
  29130. * Enabled match by name
  29131. *
  29132. * @type {boolean}
  29133. * @since 8.0.0
  29134. * @apioption plotOptions.series.dataSorting.matchByName
  29135. */
  29136. /**
  29137. * Determines what data value should be used to sort by.
  29138. *
  29139. * @sample {highcharts} highcharts/datasorting/sort-key/
  29140. * Sort key as `z` value
  29141. *
  29142. * @type {string}
  29143. * @since 8.0.0
  29144. * @default y
  29145. * @apioption plotOptions.series.dataSorting.sortKey
  29146. */
  29147. /**
  29148. * Enable or disable the mouse tracking for a specific series. This
  29149. * includes point tooltips and click events on graphs and points. For
  29150. * large datasets it improves performance.
  29151. *
  29152. * @sample {highcharts} highcharts/plotoptions/series-enablemousetracking-false/
  29153. * No mouse tracking
  29154. * @sample {highmaps} maps/plotoptions/series-enablemousetracking-false/
  29155. * No mouse tracking
  29156. *
  29157. * @type {boolean}
  29158. * @default true
  29159. * @apioption plotOptions.series.enableMouseTracking
  29160. */
  29161. /**
  29162. * Whether to use the Y extremes of the total chart width or only the
  29163. * zoomed area when zooming in on parts of the X axis. By default, the
  29164. * Y axis adjusts to the min and max of the visible data. Cartesian
  29165. * series only.
  29166. *
  29167. * @type {boolean}
  29168. * @default false
  29169. * @since 4.1.6
  29170. * @product highcharts highstock gantt
  29171. * @apioption plotOptions.series.getExtremesFromAll
  29172. */
  29173. /**
  29174. * An array specifying which option maps to which key in the data point
  29175. * array. This makes it convenient to work with unstructured data arrays
  29176. * from different sources.
  29177. *
  29178. * @see [series.data](#series.line.data)
  29179. *
  29180. * @sample {highcharts|highstock} highcharts/series/data-keys/
  29181. * An extended data array with keys
  29182. * @sample {highcharts|highstock} highcharts/series/data-nested-keys/
  29183. * Nested keys used to access object properties
  29184. *
  29185. * @type {Array<string>}
  29186. * @since 4.1.6
  29187. * @apioption plotOptions.series.keys
  29188. */
  29189. /**
  29190. * The line cap used for line ends and line joins on the graph.
  29191. *
  29192. * @type {Highcharts.SeriesLinecapValue}
  29193. * @default round
  29194. * @product highcharts highstock
  29195. * @apioption plotOptions.series.linecap
  29196. */
  29197. /**
  29198. * The [id](#series.id) of another series to link to. Additionally,
  29199. * the value can be ":previous" to link to the previous series. When
  29200. * two series are linked, only the first one appears in the legend.
  29201. * Toggling the visibility of this also toggles the linked series.
  29202. *
  29203. * If master series uses data sorting and linked series does not have
  29204. * its own sorting definition, the linked series will be sorted in the
  29205. * same order as the master one.
  29206. *
  29207. * @sample {highcharts|highstock} highcharts/demo/arearange-line/
  29208. * Linked series
  29209. *
  29210. * @type {string}
  29211. * @since 3.0
  29212. * @product highcharts highstock gantt
  29213. * @apioption plotOptions.series.linkedTo
  29214. */
  29215. /**
  29216. * Options for the corresponding navigator series if `showInNavigator`
  29217. * is `true` for this series. Available options are the same as any
  29218. * series, documented at [plotOptions](#plotOptions.series) and
  29219. * [series](#series).
  29220. *
  29221. * These options are merged with options in [navigator.series](
  29222. * #navigator.series), and will take precedence if the same option is
  29223. * defined both places.
  29224. *
  29225. * @see [navigator.series](#navigator.series)
  29226. *
  29227. * @type {Highcharts.PlotSeriesOptions}
  29228. * @since 5.0.0
  29229. * @product highstock
  29230. * @apioption plotOptions.series.navigatorOptions
  29231. */
  29232. /**
  29233. * The color for the parts of the graph or points that are below the
  29234. * [threshold](#plotOptions.series.threshold). Note that `zones` takes
  29235. * precedence over the negative color. Using `negativeColor` is
  29236. * equivalent to applying a zone with value of 0.
  29237. *
  29238. * @see In styled mode, a negative color is applied by setting this option
  29239. * to `true` combined with the `.highcharts-negative` class name.
  29240. *
  29241. * @sample {highcharts} highcharts/plotoptions/series-negative-color/
  29242. * Spline, area and column
  29243. * @sample {highcharts} highcharts/plotoptions/arearange-negativecolor/
  29244. * Arearange
  29245. * @sample {highcharts} highcharts/css/series-negative-color/
  29246. * Styled mode
  29247. * @sample {highstock} highcharts/plotoptions/series-negative-color/
  29248. * Spline, area and column
  29249. * @sample {highstock} highcharts/plotoptions/arearange-negativecolor/
  29250. * Arearange
  29251. * @sample {highmaps} highcharts/plotoptions/series-negative-color/
  29252. * Spline, area and column
  29253. * @sample {highmaps} highcharts/plotoptions/arearange-negativecolor/
  29254. * Arearange
  29255. *
  29256. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29257. * @since 3.0
  29258. * @apioption plotOptions.series.negativeColor
  29259. */
  29260. /**
  29261. * Same as
  29262. * [accessibility.pointDescriptionFormatter](#accessibility.pointDescriptionFormatter),
  29263. * but for an individual series. Overrides the chart wide configuration.
  29264. *
  29265. * @type {Function}
  29266. * @since 5.0.12
  29267. * @apioption plotOptions.series.pointDescriptionFormatter
  29268. */
  29269. /**
  29270. * If no x values are given for the points in a series, `pointInterval`
  29271. * defines the interval of the x values. For example, if a series
  29272. * contains one value every decade starting from year 0, set
  29273. * `pointInterval` to `10`. In true `datetime` axes, the `pointInterval`
  29274. * is set in milliseconds.
  29275. *
  29276. * It can be also be combined with `pointIntervalUnit` to draw irregular
  29277. * time intervals.
  29278. *
  29279. * Please note that this options applies to the _series data_, not the
  29280. * interval of the axis ticks, which is independent.
  29281. *
  29282. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29283. * Datetime X axis
  29284. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29285. * Using pointStart and pointInterval
  29286. *
  29287. * @type {number}
  29288. * @default 1
  29289. * @product highcharts highstock gantt
  29290. * @apioption plotOptions.series.pointInterval
  29291. */
  29292. /**
  29293. * On datetime series, this allows for setting the
  29294. * [pointInterval](#plotOptions.series.pointInterval) to irregular time
  29295. * units, `day`, `month` and `year`. A day is usually the same as 24
  29296. * hours, but `pointIntervalUnit` also takes the DST crossover into
  29297. * consideration when dealing with local time. Combine this option with
  29298. * `pointInterval` to draw weeks, quarters, 6 months, 10 years etc.
  29299. *
  29300. * Please note that this options applies to the _series data_, not the
  29301. * interval of the axis ticks, which is independent.
  29302. *
  29303. * @sample {highcharts} highcharts/plotoptions/series-pointintervalunit/
  29304. * One point a month
  29305. * @sample {highstock} highcharts/plotoptions/series-pointintervalunit/
  29306. * One point a month
  29307. *
  29308. * @type {string}
  29309. * @since 4.1.0
  29310. * @product highcharts highstock gantt
  29311. * @validvalue ["day", "month", "year"]
  29312. * @apioption plotOptions.series.pointIntervalUnit
  29313. */
  29314. /**
  29315. * Possible values: `"on"`, `"between"`, `number`.
  29316. *
  29317. * In a column chart, when pointPlacement is `"on"`, the point will not
  29318. * create any padding of the X axis. In a polar column chart this means
  29319. * that the first column points directly north. If the pointPlacement is
  29320. * `"between"`, the columns will be laid out between ticks. This is
  29321. * useful for example for visualising an amount between two points in
  29322. * time or in a certain sector of a polar chart.
  29323. *
  29324. * Since Highcharts 3.0.2, the point placement can also be numeric,
  29325. * where 0 is on the axis value, -0.5 is between this value and the
  29326. * previous, and 0.5 is between this value and the next. Unlike the
  29327. * textual options, numeric point placement options won't affect axis
  29328. * padding.
  29329. *
  29330. * Note that pointPlacement needs a [pointRange](
  29331. * #plotOptions.series.pointRange) to work. For column series this is
  29332. * computed, but for line-type series it needs to be set.
  29333. *
  29334. * For the `xrange` series type and gantt charts, if the Y axis is a
  29335. * category axis, the `pointPlacement` applies to the Y axis rather than
  29336. * the (typically datetime) X axis.
  29337. *
  29338. * Defaults to `undefined` in cartesian charts, `"between"` in polar
  29339. * charts.
  29340. *
  29341. * @see [xAxis.tickmarkPlacement](#xAxis.tickmarkPlacement)
  29342. *
  29343. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-between/
  29344. * Between in a column chart
  29345. * @sample {highcharts|highstock} highcharts/plotoptions/series-pointplacement-numeric/
  29346. * Numeric placement for custom layout
  29347. * @sample {highcharts|highstock} maps/plotoptions/heatmap-pointplacement/
  29348. * Placement in heatmap
  29349. *
  29350. * @type {string|number}
  29351. * @since 2.3.0
  29352. * @product highcharts highstock gantt
  29353. * @apioption plotOptions.series.pointPlacement
  29354. */
  29355. /**
  29356. * If no x values are given for the points in a series, pointStart
  29357. * defines on what value to start. For example, if a series contains one
  29358. * yearly value starting from 1945, set pointStart to 1945.
  29359. *
  29360. * @sample {highcharts} highcharts/plotoptions/series-pointstart-linear/
  29361. * Linear
  29362. * @sample {highcharts} highcharts/plotoptions/series-pointstart-datetime/
  29363. * Datetime
  29364. * @sample {highstock} stock/plotoptions/pointinterval-pointstart/
  29365. * Using pointStart and pointInterval
  29366. *
  29367. * @type {number}
  29368. * @default 0
  29369. * @product highcharts highstock gantt
  29370. * @apioption plotOptions.series.pointStart
  29371. */
  29372. /**
  29373. * Whether to select the series initially. If `showCheckbox` is true,
  29374. * the checkbox next to the series name in the legend will be checked
  29375. * for a selected series.
  29376. *
  29377. * @sample {highcharts} highcharts/plotoptions/series-selected/
  29378. * One out of two series selected
  29379. *
  29380. * @type {boolean}
  29381. * @default false
  29382. * @since 1.2.0
  29383. * @apioption plotOptions.series.selected
  29384. */
  29385. /**
  29386. * Whether to apply a drop shadow to the graph line. Since 2.3 the
  29387. * shadow can be an object configuration containing `color`, `offsetX`,
  29388. * `offsetY`, `opacity` and `width`.
  29389. *
  29390. * @sample {highcharts} highcharts/plotoptions/series-shadow/
  29391. * Shadow enabled
  29392. *
  29393. * @type {boolean|Highcharts.ShadowOptionsObject}
  29394. * @default false
  29395. * @apioption plotOptions.series.shadow
  29396. */
  29397. /**
  29398. * Whether to display this particular series or series type in the
  29399. * legend. Standalone series are shown in legend by default, and linked
  29400. * series are not. Since v7.2.0 it is possible to show series that use
  29401. * colorAxis by setting this option to `true`.
  29402. *
  29403. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  29404. * One series in the legend, one hidden
  29405. *
  29406. * @type {boolean}
  29407. * @apioption plotOptions.series.showInLegend
  29408. */
  29409. /**
  29410. * Whether or not to show the series in the navigator. Takes precedence
  29411. * over [navigator.baseSeries](#navigator.baseSeries) if defined.
  29412. *
  29413. * @type {boolean}
  29414. * @since 5.0.0
  29415. * @product highstock
  29416. * @apioption plotOptions.series.showInNavigator
  29417. */
  29418. /**
  29419. * If set to `true`, the accessibility module will skip past the points
  29420. * in this series for keyboard navigation.
  29421. *
  29422. * @type {boolean}
  29423. * @since 5.0.12
  29424. * @apioption plotOptions.series.skipKeyboardNavigation
  29425. */
  29426. /**
  29427. * Whether to stack the values of each series on top of each other.
  29428. * Possible values are `undefined` to disable, `"normal"` to stack by
  29429. * value or `"percent"`.
  29430. *
  29431. * When stacking is enabled, data must be sorted
  29432. * in ascending X order.
  29433. *
  29434. * Some stacking options are related to specific series types. In the
  29435. * streamgraph series type, the stacking option is set to `"stream"`.
  29436. * The second one is `"overlap"`, which only applies to waterfall
  29437. * series.
  29438. *
  29439. * @see [yAxis.reversedStacks](#yAxis.reversedStacks)
  29440. *
  29441. * @sample {highcharts} highcharts/plotoptions/series-stacking-line/
  29442. * Line
  29443. * @sample {highcharts} highcharts/plotoptions/series-stacking-column/
  29444. * Column
  29445. * @sample {highcharts} highcharts/plotoptions/series-stacking-bar/
  29446. * Bar
  29447. * @sample {highcharts} highcharts/plotoptions/series-stacking-area/
  29448. * Area
  29449. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-line/
  29450. * Line
  29451. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-column/
  29452. * Column
  29453. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-bar/
  29454. * Bar
  29455. * @sample {highcharts} highcharts/plotoptions/series-stacking-percent-area/
  29456. * Area
  29457. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-normal-stacking
  29458. * Waterfall with normal stacking
  29459. * @sample {highcharts} highcharts/plotoptions/series-waterfall-with-overlap-stacking
  29460. * Waterfall with overlap stacking
  29461. * @sample {highstock} stock/plotoptions/stacking/
  29462. * Area
  29463. *
  29464. * @type {string}
  29465. * @product highcharts highstock
  29466. * @validvalue ["normal", "overlap", "percent", "stream"]
  29467. * @apioption plotOptions.series.stacking
  29468. */
  29469. /**
  29470. * Whether to apply steps to the line. Possible values are `left`,
  29471. * `center` and `right`.
  29472. *
  29473. * @sample {highcharts} highcharts/plotoptions/line-step/
  29474. * Different step line options
  29475. * @sample {highcharts} highcharts/plotoptions/area-step/
  29476. * Stepped, stacked area
  29477. * @sample {highstock} stock/plotoptions/line-step/
  29478. * Step line
  29479. *
  29480. * @type {string}
  29481. * @since 1.2.5
  29482. * @product highcharts highstock
  29483. * @validvalue ["left", "center", "right"]
  29484. * @apioption plotOptions.series.step
  29485. */
  29486. /**
  29487. * The threshold, also called zero level or base level. For line type
  29488. * series this is only used in conjunction with
  29489. * [negativeColor](#plotOptions.series.negativeColor).
  29490. *
  29491. * @see [softThreshold](#plotOptions.series.softThreshold).
  29492. *
  29493. * @type {number}
  29494. * @default 0
  29495. * @since 3.0
  29496. * @product highcharts highstock
  29497. * @apioption plotOptions.series.threshold
  29498. */
  29499. /**
  29500. * Set the initial visibility of the series.
  29501. *
  29502. * @sample {highcharts} highcharts/plotoptions/series-visible/
  29503. * Two series, one hidden and one visible
  29504. * @sample {highstock} stock/plotoptions/series-visibility/
  29505. * Hidden series
  29506. *
  29507. * @type {boolean}
  29508. * @default true
  29509. * @apioption plotOptions.series.visible
  29510. */
  29511. /**
  29512. * Defines the Axis on which the zones are applied.
  29513. *
  29514. * @see [zones](#plotOptions.series.zones)
  29515. *
  29516. * @sample {highcharts} highcharts/series/color-zones-zoneaxis-x/
  29517. * Zones on the X-Axis
  29518. * @sample {highstock} highcharts/series/color-zones-zoneaxis-x/
  29519. * Zones on the X-Axis
  29520. *
  29521. * @type {string}
  29522. * @default y
  29523. * @since 4.1.0
  29524. * @product highcharts highstock
  29525. * @apioption plotOptions.series.zoneAxis
  29526. */
  29527. /**
  29528. * General event handlers for the series items. These event hooks can
  29529. * also be attached to the series at run time using the
  29530. * `Highcharts.addEvent` function.
  29531. *
  29532. * @declare Highcharts.SeriesEventsOptionsObject
  29533. *
  29534. * @private
  29535. */
  29536. events: {},
  29537. /**
  29538. * Fires after the series has finished its initial animation, or in case
  29539. * animation is disabled, immediately as the series is displayed.
  29540. *
  29541. * @sample {highcharts} highcharts/plotoptions/series-events-afteranimate/
  29542. * Show label after animate
  29543. * @sample {highstock} highcharts/plotoptions/series-events-afteranimate/
  29544. * Show label after animate
  29545. *
  29546. * @type {Highcharts.SeriesAfterAnimateCallbackFunction}
  29547. * @since 4.0
  29548. * @product highcharts highstock gantt
  29549. * @context Highcharts.Series
  29550. * @apioption plotOptions.series.events.afterAnimate
  29551. */
  29552. /**
  29553. * Fires when the checkbox next to the series' name in the legend is
  29554. * clicked. One parameter, `event`, is passed to the function. The state
  29555. * of the checkbox is found by `event.checked`. The checked item is
  29556. * found by `event.item`. Return `false` to prevent the default action
  29557. * which is to toggle the select state of the series.
  29558. *
  29559. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  29560. * Alert checkbox status
  29561. *
  29562. * @type {Highcharts.SeriesCheckboxClickCallbackFunction}
  29563. * @since 1.2.0
  29564. * @context Highcharts.Series
  29565. * @apioption plotOptions.series.events.checkboxClick
  29566. */
  29567. /**
  29568. * Fires when the series is clicked. One parameter, `event`, is passed
  29569. * to the function, containing common event information. Additionally,
  29570. * `event.point` holds a pointer to the nearest point on the graph.
  29571. *
  29572. * @sample {highcharts} highcharts/plotoptions/series-events-click/
  29573. * Alert click info
  29574. * @sample {highstock} stock/plotoptions/series-events-click/
  29575. * Alert click info
  29576. * @sample {highmaps} maps/plotoptions/series-events-click/
  29577. * Display click info in subtitle
  29578. *
  29579. * @type {Highcharts.SeriesClickCallbackFunction}
  29580. * @context Highcharts.Series
  29581. * @apioption plotOptions.series.events.click
  29582. */
  29583. /**
  29584. * Fires when the series is hidden after chart generation time, either
  29585. * by clicking the legend item or by calling `.hide()`.
  29586. *
  29587. * @sample {highcharts} highcharts/plotoptions/series-events-hide/
  29588. * Alert when the series is hidden by clicking the legend item
  29589. *
  29590. * @type {Highcharts.SeriesHideCallbackFunction}
  29591. * @since 1.2.0
  29592. * @context Highcharts.Series
  29593. * @apioption plotOptions.series.events.hide
  29594. */
  29595. /**
  29596. * Fires when the legend item belonging to the series is clicked. One
  29597. * parameter, `event`, is passed to the function. The default action
  29598. * is to toggle the visibility of the series. This can be prevented
  29599. * by returning `false` or calling `event.preventDefault()`.
  29600. *
  29601. * @sample {highcharts} highcharts/plotoptions/series-events-legenditemclick/
  29602. * Confirm hiding and showing
  29603. *
  29604. * @type {Highcharts.SeriesLegendItemClickCallbackFunction}
  29605. * @context Highcharts.Series
  29606. * @apioption plotOptions.series.events.legendItemClick
  29607. */
  29608. /**
  29609. * Fires when the mouse leaves the graph. One parameter, `event`, is
  29610. * passed to the function, containing common event information. If the
  29611. * [stickyTracking](#plotOptions.series) option is true, `mouseOut`
  29612. * doesn't happen before the mouse enters another graph or leaves the
  29613. * plot area.
  29614. *
  29615. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29616. * With sticky tracking by default
  29617. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29618. * Without sticky tracking
  29619. *
  29620. * @type {Highcharts.SeriesMouseOutCallbackFunction}
  29621. * @context Highcharts.Series
  29622. * @apioption plotOptions.series.events.mouseOut
  29623. */
  29624. /**
  29625. * Fires when the mouse enters the graph. One parameter, `event`, is
  29626. * passed to the function, containing common event information.
  29627. *
  29628. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-sticky/
  29629. * With sticky tracking by default
  29630. * @sample {highcharts} highcharts/plotoptions/series-events-mouseover-no-sticky/
  29631. * Without sticky tracking
  29632. *
  29633. * @type {Highcharts.SeriesMouseOverCallbackFunction}
  29634. * @context Highcharts.Series
  29635. * @apioption plotOptions.series.events.mouseOver
  29636. */
  29637. /**
  29638. * Fires when the series is shown after chart generation time, either
  29639. * by clicking the legend item or by calling `.show()`.
  29640. *
  29641. * @sample {highcharts} highcharts/plotoptions/series-events-show/
  29642. * Alert when the series is shown by clicking the legend item.
  29643. *
  29644. * @type {Highcharts.SeriesShowCallbackFunction}
  29645. * @since 1.2.0
  29646. * @context Highcharts.Series
  29647. * @apioption plotOptions.series.events.show
  29648. */
  29649. /**
  29650. * Options for the point markers of line-like series. Properties like
  29651. * `fillColor`, `lineColor` and `lineWidth` define the visual appearance
  29652. * of the markers. Other series types, like column series, don't have
  29653. * markers, but have visual options on the series level instead.
  29654. *
  29655. * In styled mode, the markers can be styled with the
  29656. * `.highcharts-point`, `.highcharts-point-hover` and
  29657. * `.highcharts-point-select` class names.
  29658. *
  29659. * @declare Highcharts.PointMarkerOptionsObject
  29660. *
  29661. * @private
  29662. */
  29663. marker: {
  29664. /**
  29665. * Enable or disable the point marker. If `undefined`, the markers
  29666. * are hidden when the data is dense, and shown for more widespread
  29667. * data points.
  29668. *
  29669. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled/
  29670. * Disabled markers
  29671. * @sample {highcharts} highcharts/plotoptions/series-marker-enabled-false/
  29672. * Disabled in normal state but enabled on hover
  29673. * @sample {highstock} stock/plotoptions/series-marker/
  29674. * Enabled markers
  29675. *
  29676. * @type {boolean}
  29677. * @default {highcharts} undefined
  29678. * @default {highstock} false
  29679. * @apioption plotOptions.series.marker.enabled
  29680. */
  29681. /**
  29682. * The threshold for how dense the point markers should be before
  29683. * they are hidden, given that `enabled` is not defined. The number
  29684. * indicates the horizontal distance between the two closest points
  29685. * in the series, as multiples of the `marker.radius`. In other
  29686. * words, the default value of 2 means points are hidden if
  29687. * overlapping horizontally.
  29688. *
  29689. * @sample highcharts/plotoptions/series-marker-enabledthreshold
  29690. * A higher threshold
  29691. *
  29692. * @since 6.0.5
  29693. */
  29694. enabledThreshold: 2,
  29695. /**
  29696. * The fill color of the point marker. When `undefined`, the series'
  29697. * or point's color is used.
  29698. *
  29699. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29700. * White fill
  29701. *
  29702. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29703. * @apioption plotOptions.series.marker.fillColor
  29704. */
  29705. /**
  29706. * Image markers only. Set the image width explicitly. When using
  29707. * this option, a `width` must also be set.
  29708. *
  29709. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29710. * Fixed width and height
  29711. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29712. * Fixed width and height
  29713. *
  29714. * @type {number}
  29715. * @since 4.0.4
  29716. * @apioption plotOptions.series.marker.height
  29717. */
  29718. /**
  29719. * The color of the point marker's outline. When `undefined`, the
  29720. * series' or point's color is used.
  29721. *
  29722. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29723. * Inherit from series color (undefined)
  29724. *
  29725. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29726. */
  29727. lineColor: '#ffffff',
  29728. /**
  29729. * The width of the point marker's outline.
  29730. *
  29731. * @sample {highcharts} highcharts/plotoptions/series-marker-fillcolor/
  29732. * 2px blue marker
  29733. */
  29734. lineWidth: 0,
  29735. /**
  29736. * The radius of the point marker.
  29737. *
  29738. * @sample {highcharts} highcharts/plotoptions/series-marker-radius/
  29739. * Bigger markers
  29740. *
  29741. * @default {highstock} 2
  29742. */
  29743. radius: 4,
  29744. /**
  29745. * A predefined shape or symbol for the marker. When undefined, the
  29746. * symbol is pulled from options.symbols. Other possible values are
  29747. * `'circle'`, `'square'`,`'diamond'`, `'triangle'` and
  29748. * `'triangle-down'`.
  29749. *
  29750. * Additionally, the URL to a graphic can be given on this form:
  29751. * `'url(graphic.png)'`. Note that for the image to be applied to
  29752. * exported charts, its URL needs to be accessible by the export
  29753. * server.
  29754. *
  29755. * Custom callbacks for symbol path generation can also be added to
  29756. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  29757. * used by its method name, as shown in the demo.
  29758. *
  29759. * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
  29760. * Predefined, graphic and custom markers
  29761. * @sample {highstock} highcharts/plotoptions/series-marker-symbol/
  29762. * Predefined, graphic and custom markers
  29763. *
  29764. * @type {string}
  29765. * @apioption plotOptions.series.marker.symbol
  29766. */
  29767. /**
  29768. * Image markers only. Set the image width explicitly. When using
  29769. * this option, a `height` must also be set.
  29770. *
  29771. * @sample {highcharts} highcharts/plotoptions/series-marker-width-height/
  29772. * Fixed width and height
  29773. * @sample {highstock} highcharts/plotoptions/series-marker-width-height/
  29774. * Fixed width and height
  29775. *
  29776. * @type {number}
  29777. * @since 4.0.4
  29778. * @apioption plotOptions.series.marker.width
  29779. */
  29780. /**
  29781. * States for a single point marker.
  29782. *
  29783. * @declare Highcharts.PointStatesOptionsObject
  29784. */
  29785. states: {
  29786. /**
  29787. * The normal state of a single point marker. Currently only
  29788. * used for setting animation when returning to normal state
  29789. * from hover.
  29790. *
  29791. * @declare Highcharts.PointStatesNormalOptionsObject
  29792. */
  29793. normal: {
  29794. /**
  29795. * Animation when returning to normal state after hovering.
  29796. *
  29797. * @type {boolean|Highcharts.AnimationOptionsObject}
  29798. */
  29799. animation: true
  29800. },
  29801. /**
  29802. * The hover state for a single point marker.
  29803. *
  29804. * @declare Highcharts.PointStatesHoverOptionsObject
  29805. */
  29806. hover: {
  29807. /**
  29808. * Animation when hovering over the marker.
  29809. *
  29810. * @type {boolean|Highcharts.AnimationOptionsObject}
  29811. */
  29812. animation: {
  29813. /** @internal */
  29814. duration: 50
  29815. },
  29816. /**
  29817. * Enable or disable the point marker.
  29818. *
  29819. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-enabled/
  29820. * Disabled hover state
  29821. */
  29822. enabled: true,
  29823. /**
  29824. * The fill color of the marker in hover state. When
  29825. * `undefined`, the series' or point's fillColor for normal
  29826. * state is used.
  29827. *
  29828. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29829. * @apioption plotOptions.series.marker.states.hover.fillColor
  29830. */
  29831. /**
  29832. * The color of the point marker's outline. When
  29833. * `undefined`, the series' or point's lineColor for normal
  29834. * state is used.
  29835. *
  29836. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linecolor/
  29837. * White fill color, black line color
  29838. *
  29839. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29840. * @apioption plotOptions.series.marker.states.hover.lineColor
  29841. */
  29842. /**
  29843. * The width of the point marker's outline. When
  29844. * `undefined`, the series' or point's lineWidth for normal
  29845. * state is used.
  29846. *
  29847. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-linewidth/
  29848. * 3px line width
  29849. *
  29850. * @type {number}
  29851. * @apioption plotOptions.series.marker.states.hover.lineWidth
  29852. */
  29853. /**
  29854. * The radius of the point marker. In hover state, it
  29855. * defaults to the normal state's radius + 2 as per the
  29856. * [radiusPlus](#plotOptions.series.marker.states.hover.radiusPlus)
  29857. * option.
  29858. *
  29859. * @sample {highcharts} highcharts/plotoptions/series-marker-states-hover-radius/
  29860. * 10px radius
  29861. *
  29862. * @type {number}
  29863. * @apioption plotOptions.series.marker.states.hover.radius
  29864. */
  29865. /**
  29866. * The number of pixels to increase the radius of the
  29867. * hovered point.
  29868. *
  29869. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29870. * 5 pixels greater radius on hover
  29871. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29872. * 5 pixels greater radius on hover
  29873. *
  29874. * @since 4.0.3
  29875. */
  29876. radiusPlus: 2,
  29877. /**
  29878. * The additional line width for a hovered point.
  29879. *
  29880. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  29881. * 2 pixels wider on hover
  29882. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  29883. * 2 pixels wider on hover
  29884. *
  29885. * @since 4.0.3
  29886. */
  29887. lineWidthPlus: 1
  29888. },
  29889. /**
  29890. * The appearance of the point marker when selected. In order to
  29891. * allow a point to be selected, set the
  29892. * `series.allowPointSelect` option to true.
  29893. *
  29894. * @declare Highcharts.PointStatesSelectOptionsObject
  29895. */
  29896. select: {
  29897. /**
  29898. * Enable or disable visible feedback for selection.
  29899. *
  29900. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-enabled/
  29901. * Disabled select state
  29902. *
  29903. * @type {boolean}
  29904. * @default true
  29905. * @apioption plotOptions.series.marker.states.select.enabled
  29906. */
  29907. /**
  29908. * The radius of the point marker. In hover state, it
  29909. * defaults to the normal state's radius + 2.
  29910. *
  29911. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-radius/
  29912. * 10px radius for selected points
  29913. *
  29914. * @type {number}
  29915. * @apioption plotOptions.series.marker.states.select.radius
  29916. */
  29917. /**
  29918. * The fill color of the point marker.
  29919. *
  29920. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-fillcolor/
  29921. * Solid red discs for selected points
  29922. *
  29923. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29924. */
  29925. fillColor: '#cccccc',
  29926. /**
  29927. * The color of the point marker's outline. When
  29928. * `undefined`, the series' or point's color is used.
  29929. *
  29930. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linecolor/
  29931. * Red line color for selected points
  29932. *
  29933. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  29934. */
  29935. lineColor: '#000000',
  29936. /**
  29937. * The width of the point marker's outline.
  29938. *
  29939. * @sample {highcharts} highcharts/plotoptions/series-marker-states-select-linewidth/
  29940. * 3px line width for selected points
  29941. */
  29942. lineWidth: 2
  29943. }
  29944. }
  29945. },
  29946. /**
  29947. * Properties for each single point.
  29948. *
  29949. * @declare Highcharts.PlotSeriesPointOptions
  29950. *
  29951. * @private
  29952. */
  29953. point: {
  29954. /**
  29955. * Fires when a point is clicked. One parameter, `event`, is passed
  29956. * to the function, containing common event information.
  29957. *
  29958. * If the `series.allowPointSelect` option is true, the default
  29959. * action for the point's click event is to toggle the point's
  29960. * select state. Returning `false` cancels this action.
  29961. *
  29962. * @sample {highcharts} highcharts/plotoptions/series-point-events-click/
  29963. * Click marker to alert values
  29964. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-column/
  29965. * Click column
  29966. * @sample {highcharts} highcharts/plotoptions/series-point-events-click-url/
  29967. * Go to URL
  29968. * @sample {highmaps} maps/plotoptions/series-point-events-click/
  29969. * Click marker to display values
  29970. * @sample {highmaps} maps/plotoptions/series-point-events-click-url/
  29971. * Go to URL
  29972. *
  29973. * @type {Highcharts.PointClickCallbackFunction}
  29974. * @context Highcharts.Point
  29975. * @apioption plotOptions.series.point.events.click
  29976. */
  29977. /**
  29978. * Fires when the mouse leaves the area close to the point. One
  29979. * parameter, `event`, is passed to the function, containing common
  29980. * event information.
  29981. *
  29982. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  29983. * Show values in the chart's corner on mouse over
  29984. *
  29985. * @type {Highcharts.PointMouseOutCallbackFunction}
  29986. * @context Highcharts.Point
  29987. * @apioption plotOptions.series.point.events.mouseOut
  29988. */
  29989. /**
  29990. * Fires when the mouse enters the area close to the point. One
  29991. * parameter, `event`, is passed to the function, containing common
  29992. * event information.
  29993. *
  29994. * @sample {highcharts} highcharts/plotoptions/series-point-events-mouseover/
  29995. * Show values in the chart's corner on mouse over
  29996. *
  29997. * @type {Highcharts.PointMouseOverCallbackFunction}
  29998. * @context Highcharts.Point
  29999. * @apioption plotOptions.series.point.events.mouseOver
  30000. */
  30001. /**
  30002. * Fires when the point is removed using the `.remove()` method. One
  30003. * parameter, `event`, is passed to the function. Returning `false`
  30004. * cancels the operation.
  30005. *
  30006. * @sample {highcharts} highcharts/plotoptions/series-point-events-remove/
  30007. * Remove point and confirm
  30008. *
  30009. * @type {Highcharts.PointRemoveCallbackFunction}
  30010. * @since 1.2.0
  30011. * @context Highcharts.Point
  30012. * @apioption plotOptions.series.point.events.remove
  30013. */
  30014. /**
  30015. * Fires when the point is selected either programmatically or
  30016. * following a click on the point. One parameter, `event`, is passed
  30017. * to the function. Returning `false` cancels the operation.
  30018. *
  30019. * @sample {highcharts} highcharts/plotoptions/series-point-events-select/
  30020. * Report the last selected point
  30021. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30022. * Report select and unselect
  30023. *
  30024. * @type {Highcharts.PointSelectCallbackFunction}
  30025. * @since 1.2.0
  30026. * @context Highcharts.Point
  30027. * @apioption plotOptions.series.point.events.select
  30028. */
  30029. /**
  30030. * Fires when the point is unselected either programmatically or
  30031. * following a click on the point. One parameter, `event`, is passed
  30032. * to the function.
  30033. * Returning `false` cancels the operation.
  30034. *
  30035. * @sample {highcharts} highcharts/plotoptions/series-point-events-unselect/
  30036. * Report the last unselected point
  30037. * @sample {highmaps} maps/plotoptions/series-allowpointselect/
  30038. * Report select and unselect
  30039. *
  30040. * @type {Highcharts.PointUnselectCallbackFunction}
  30041. * @since 1.2.0
  30042. * @context Highcharts.Point
  30043. * @apioption plotOptions.series.point.events.unselect
  30044. */
  30045. /**
  30046. * Fires when the point is updated programmatically through the
  30047. * `.update()` method. One parameter, `event`, is passed to the
  30048. * function. The new point options can be accessed through
  30049. * `event.options`. Returning `false` cancels the operation.
  30050. *
  30051. * @sample {highcharts} highcharts/plotoptions/series-point-events-update/
  30052. * Confirm point updating
  30053. *
  30054. * @type {Highcharts.PointUpdateCallbackFunction}
  30055. * @since 1.2.0
  30056. * @context Highcharts.Point
  30057. * @apioption plotOptions.series.point.events.update
  30058. */
  30059. /**
  30060. * Events for each single point.
  30061. *
  30062. * @declare Highcharts.PointEventsOptionsObject
  30063. */
  30064. events: {}
  30065. },
  30066. /**
  30067. * Options for the series data labels, appearing next to each data
  30068. * point.
  30069. *
  30070. * Since v6.2.0, multiple data labels can be applied to each single
  30071. * point by defining them as an array of configs.
  30072. *
  30073. * In styled mode, the data labels can be styled with the
  30074. * `.highcharts-data-label-box` and `.highcharts-data-label` class names
  30075. * ([see example](https://www.highcharts.com/samples/highcharts/css/series-datalabels)).
  30076. *
  30077. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled
  30078. * Data labels enabled
  30079. * @sample {highcharts} highcharts/plotoptions/series-datalabels-multiple
  30080. * Multiple data labels on a bar series
  30081. * @sample {highcharts} highcharts/css/series-datalabels
  30082. * Style mode example
  30083. *
  30084. * @type {*|Array<*>}
  30085. * @product highcharts highstock highmaps gantt
  30086. *
  30087. * @private
  30088. */
  30089. dataLabels: {
  30090. /**
  30091. * The alignment of the data label compared to the point. If
  30092. * `right`, the right side of the label should be touching the
  30093. * point. For points with an extent, like columns, the alignments
  30094. * also dictates how to align it inside the box, as given with the
  30095. * [inside](#plotOptions.column.dataLabels.inside)
  30096. * option. Can be one of `left`, `center` or `right`.
  30097. *
  30098. * @sample {highcharts} highcharts/plotoptions/series-datalabels-align-left/
  30099. * Left aligned
  30100. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  30101. * Data labels inside the bar
  30102. *
  30103. * @type {Highcharts.AlignValue|null}
  30104. */
  30105. align: 'center',
  30106. /**
  30107. * Whether to allow data labels to overlap. To make the labels less
  30108. * sensitive for overlapping, the
  30109. * [dataLabels.padding](#plotOptions.series.dataLabels.padding)
  30110. * can be set to 0.
  30111. *
  30112. * @sample {highcharts} highcharts/plotoptions/series-datalabels-allowoverlap-false/
  30113. * Don't allow overlap
  30114. *
  30115. * @type {boolean}
  30116. * @default false
  30117. * @since 4.1.0
  30118. * @apioption plotOptions.series.dataLabels.allowOverlap
  30119. */
  30120. /**
  30121. * The background color or gradient for the data label.
  30122. *
  30123. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30124. * Data labels box options
  30125. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30126. * Data labels box options
  30127. *
  30128. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30129. * @since 2.2.1
  30130. * @apioption plotOptions.series.dataLabels.backgroundColor
  30131. */
  30132. /**
  30133. * The border color for the data label. Defaults to `undefined`.
  30134. *
  30135. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30136. * Data labels box options
  30137. *
  30138. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30139. * @since 2.2.1
  30140. * @apioption plotOptions.series.dataLabels.borderColor
  30141. */
  30142. /**
  30143. * The border radius in pixels for the data label.
  30144. *
  30145. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30146. * Data labels box options
  30147. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30148. * Data labels box options
  30149. *
  30150. * @type {number}
  30151. * @default 0
  30152. * @since 2.2.1
  30153. * @apioption plotOptions.series.dataLabels.borderRadius
  30154. */
  30155. /**
  30156. * The border width in pixels for the data label.
  30157. *
  30158. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30159. * Data labels box options
  30160. *
  30161. * @type {number}
  30162. * @default 0
  30163. * @since 2.2.1
  30164. * @apioption plotOptions.series.dataLabels.borderWidth
  30165. */
  30166. /**
  30167. * A class name for the data label. Particularly in styled mode,
  30168. * this can be used to give each series' or point's data label
  30169. * unique styling. In addition to this option, a default color class
  30170. * name is added so that we can give the labels a contrast text
  30171. * shadow.
  30172. *
  30173. * @sample {highcharts} highcharts/css/data-label-contrast/
  30174. * Contrast text shadow
  30175. * @sample {highcharts} highcharts/css/series-datalabels/
  30176. * Styling by CSS
  30177. *
  30178. * @type {string}
  30179. * @since 5.0.0
  30180. * @apioption plotOptions.series.dataLabels.className
  30181. */
  30182. /**
  30183. * The text color for the data labels. Defaults to `undefined`. For
  30184. * certain series types, like column or map, the data labels can be
  30185. * drawn inside the points. In this case the data label will be
  30186. * drawn with maximum contrast by default. Additionally, it will be
  30187. * given a `text-outline` style with the opposite color, to further
  30188. * increase the contrast. This can be overridden by setting the
  30189. * `text-outline` style to `none` in the `dataLabels.style` option.
  30190. *
  30191. * @sample {highcharts} highcharts/plotoptions/series-datalabels-color/
  30192. * Red data labels
  30193. * @sample {highmaps} maps/demo/color-axis/
  30194. * White data labels
  30195. *
  30196. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30197. * @apioption plotOptions.series.dataLabels.color
  30198. */
  30199. /**
  30200. * Whether to hide data labels that are outside the plot area. By
  30201. * default, the data label is moved inside the plot area according
  30202. * to the
  30203. * [overflow](#plotOptions.series.dataLabels.overflow)
  30204. * option.
  30205. *
  30206. * @type {boolean}
  30207. * @default true
  30208. * @since 2.3.3
  30209. * @apioption plotOptions.series.dataLabels.crop
  30210. */
  30211. /**
  30212. * Whether to defer displaying the data labels until the initial
  30213. * series animation has finished.
  30214. *
  30215. * @type {boolean}
  30216. * @default true
  30217. * @since 4.0.0
  30218. * @product highcharts highstock gantt
  30219. * @apioption plotOptions.series.dataLabels.defer
  30220. */
  30221. /**
  30222. * Enable or disable the data labels.
  30223. *
  30224. * @sample {highcharts} highcharts/plotoptions/series-datalabels-enabled/
  30225. * Data labels enabled
  30226. * @sample {highmaps} maps/demo/color-axis/
  30227. * Data labels enabled
  30228. *
  30229. * @type {boolean}
  30230. * @default false
  30231. * @apioption plotOptions.series.dataLabels.enabled
  30232. */
  30233. /**
  30234. * A declarative filter to control of which data labels to display.
  30235. * The declarative filter is designed for use when callback
  30236. * functions are not available, like when the chart options require
  30237. * a pure JSON structure or for use with graphical editors. For
  30238. * programmatic control, use the `formatter` instead, and return
  30239. * `undefined` to disable a single data label.
  30240. *
  30241. * @example
  30242. * filter: {
  30243. * property: 'percentage',
  30244. * operator: '>',
  30245. * value: 4
  30246. * }
  30247. *
  30248. * @sample {highcharts} highcharts/demo/pie-monochrome
  30249. * Data labels filtered by percentage
  30250. *
  30251. * @declare Highcharts.DataLabelsFilterOptionsObject
  30252. * @since 6.0.3
  30253. * @apioption plotOptions.series.dataLabels.filter
  30254. */
  30255. /**
  30256. * The operator to compare by. Can be one of `>`, `<`, `>=`, `<=`,
  30257. * `==`, and `===`.
  30258. *
  30259. * @type {string}
  30260. * @validvalue [">", "<", ">=", "<=", "==", "==="]
  30261. * @apioption plotOptions.series.dataLabels.filter.operator
  30262. */
  30263. /**
  30264. * The point property to filter by. Point options are passed
  30265. * directly to properties, additionally there are `y` value,
  30266. * `percentage` and others listed under {@link Highcharts.Point}
  30267. * members.
  30268. *
  30269. * @type {string}
  30270. * @apioption plotOptions.series.dataLabels.filter.property
  30271. */
  30272. /**
  30273. * The value to compare against.
  30274. *
  30275. * @type {number}
  30276. * @apioption plotOptions.series.dataLabels.filter.value
  30277. */
  30278. /**
  30279. * A
  30280. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  30281. * for the data label. Available variables are the same as for
  30282. * `formatter`.
  30283. *
  30284. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  30285. * Add a unit
  30286. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30287. * Formatted value in the data label
  30288. *
  30289. * @type {string}
  30290. * @default y
  30291. * @default point.value
  30292. * @since 3.0
  30293. * @apioption plotOptions.series.dataLabels.format
  30294. */
  30295. // eslint-disable-next-line valid-jsdoc
  30296. /**
  30297. * Callback JavaScript function to format the data label. Note that
  30298. * if a `format` is defined, the format takes precedence and the
  30299. * formatter is ignored.
  30300. *
  30301. * @sample {highmaps} maps/plotoptions/series-datalabels-format/
  30302. * Formatted value
  30303. *
  30304. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30305. */
  30306. formatter: function () {
  30307. var numberFormatter = this.series.chart.numberFormatter;
  30308. return typeof this.y !== 'number' ? '' : numberFormatter(this.y, -1);
  30309. },
  30310. /**
  30311. * For points with an extent, like columns or map areas, whether to
  30312. * align the data label inside the box or to the actual value point.
  30313. * Defaults to `false` in most cases, `true` in stacked columns.
  30314. *
  30315. * @type {boolean}
  30316. * @since 3.0
  30317. * @apioption plotOptions.series.dataLabels.inside
  30318. */
  30319. /**
  30320. * Format for points with the value of null. Works analogously to
  30321. * [format](#plotOptions.series.dataLabels.format). `nullFormat` can
  30322. * be applied only to series which support displaying null points.
  30323. *
  30324. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  30325. * Format data label and tooltip for null point.
  30326. *
  30327. * @type {boolean|string}
  30328. * @since 7.1.0
  30329. * @apioption plotOptions.series.dataLabels.nullFormat
  30330. */
  30331. /**
  30332. * Callback JavaScript function that defines formatting for points
  30333. * with the value of null. Works analogously to
  30334. * [formatter](#plotOptions.series.dataLabels.formatter).
  30335. * `nullPointFormatter` can be applied only to series which support
  30336. * displaying null points.
  30337. *
  30338. * @sample {highcharts} highcharts/plotoptions/series-datalabels-format/
  30339. * Format data label and tooltip for null point.
  30340. *
  30341. * @type {Highcharts.DataLabelsFormatterCallbackFunction}
  30342. * @since 7.1.0
  30343. * @apioption plotOptions.series.dataLabels.nullFormatter
  30344. */
  30345. /**
  30346. * How to handle data labels that flow outside the plot area. The
  30347. * default is `"justify"`, which aligns them inside the plot area.
  30348. * For columns and bars, this means it will be moved inside the bar.
  30349. * To display data labels outside the plot area, set `crop` to
  30350. * `false` and `overflow` to `"allow"`.
  30351. *
  30352. * @type {Highcharts.DataLabelsOverflowValue}
  30353. * @default justify
  30354. * @since 3.0.6
  30355. * @apioption plotOptions.series.dataLabels.overflow
  30356. */
  30357. /**
  30358. * When either the `borderWidth` or the `backgroundColor` is set,
  30359. * this is the padding within the box.
  30360. *
  30361. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30362. * Data labels box options
  30363. * @sample {highmaps} maps/plotoptions/series-datalabels-box/
  30364. * Data labels box options
  30365. *
  30366. * @since 2.2.1
  30367. */
  30368. padding: 5,
  30369. /**
  30370. * Aligns data labels relative to points. If `center` alignment is
  30371. * not possible, it defaults to `right`.
  30372. *
  30373. * @type {Highcharts.AlignValue}
  30374. * @default center
  30375. * @apioption plotOptions.series.dataLabels.position
  30376. */
  30377. /**
  30378. * Text rotation in degrees. Note that due to a more complex
  30379. * structure, backgrounds, borders and padding will be lost on a
  30380. * rotated data label.
  30381. *
  30382. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30383. * Vertical labels
  30384. *
  30385. * @type {number}
  30386. * @default 0
  30387. * @apioption plotOptions.series.dataLabels.rotation
  30388. */
  30389. /**
  30390. * The shadow of the box. Works best with `borderWidth` or
  30391. * `backgroundColor`. Since 2.3 the shadow can be an object
  30392. * configuration containing `color`, `offsetX`, `offsetY`, `opacity`
  30393. * and `width`.
  30394. *
  30395. * @sample {highcharts} highcharts/plotoptions/series-datalabels-box/
  30396. * Data labels box options
  30397. *
  30398. * @type {boolean|Highcharts.ShadowOptionsObject}
  30399. * @default false
  30400. * @since 2.2.1
  30401. * @apioption plotOptions.series.dataLabels.shadow
  30402. */
  30403. /**
  30404. * The name of a symbol to use for the border around the label.
  30405. * Symbols are predefined functions on the Renderer object.
  30406. *
  30407. * @sample {highcharts} highcharts/plotoptions/series-datalabels-shape/
  30408. * A callout for annotations
  30409. *
  30410. * @type {string}
  30411. * @default square
  30412. * @since 4.1.2
  30413. * @apioption plotOptions.series.dataLabels.shape
  30414. */
  30415. /**
  30416. * Styles for the label. The default `color` setting is
  30417. * `"contrast"`, which is a pseudo color that Highcharts picks up
  30418. * and applies the maximum contrast to the underlying point item,
  30419. * for example the bar in a bar chart.
  30420. *
  30421. * The `textOutline` is a pseudo property that applies an outline of
  30422. * the given width with the given color, which by default is the
  30423. * maximum contrast to the text. So a bright text color will result
  30424. * in a black text outline for maximum readability on a mixed
  30425. * background. In some cases, especially with grayscale text, the
  30426. * text outline doesn't work well, in which cases it can be disabled
  30427. * by setting it to `"none"`. When `useHTML` is true, the
  30428. * `textOutline` will not be picked up. In this, case, the same
  30429. * effect can be acheived through the `text-shadow` CSS property.
  30430. *
  30431. * For some series types, where each point has an extent, like for
  30432. * example tree maps, the data label may overflow the point. There
  30433. * are two strategies for handling overflow. By default, the text
  30434. * will wrap to multiple lines. The other strategy is to set
  30435. * `style.textOverflow` to `ellipsis`, which will keep the text on
  30436. * one line plus it will break inside long words.
  30437. *
  30438. * @sample {highcharts} highcharts/plotoptions/series-datalabels-style/
  30439. * Bold labels
  30440. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow/
  30441. * Long labels truncated with an ellipsis in a pie
  30442. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap/
  30443. * Long labels are wrapped in a pie
  30444. * @sample {highmaps} maps/demo/color-axis/
  30445. * Bold labels
  30446. *
  30447. * @type {Highcharts.CSSObject}
  30448. * @since 4.1.0
  30449. * @apioption plotOptions.series.dataLabels.style
  30450. */
  30451. style: {
  30452. /** @internal */
  30453. fontSize: '11px',
  30454. /** @internal */
  30455. fontWeight: 'bold',
  30456. /** @internal */
  30457. color: 'contrast',
  30458. /** @internal */
  30459. textOutline: '1px contrast'
  30460. },
  30461. /**
  30462. * Options for a label text which should follow marker's shape.
  30463. * Border and background are disabled for a label that follows a
  30464. * path.
  30465. *
  30466. * **Note:** Only SVG-based renderer supports this option. Setting
  30467. * `useHTML` to true will disable this option.
  30468. *
  30469. * @declare Highcharts.DataLabelsTextPathOptionsObject
  30470. * @since 7.1.0
  30471. * @apioption plotOptions.series.dataLabels.textPath
  30472. */
  30473. /**
  30474. * Presentation attributes for the text path.
  30475. *
  30476. * @type {Highcharts.SVGAttributes}
  30477. * @since 7.1.0
  30478. * @apioption plotOptions.series.dataLabels.textPath.attributes
  30479. */
  30480. /**
  30481. * Enable or disable `textPath` option for link's or marker's data
  30482. * labels.
  30483. *
  30484. * @type {boolean}
  30485. * @since 7.1.0
  30486. * @apioption plotOptions.series.dataLabels.textPath.enabled
  30487. */
  30488. /**
  30489. * Whether to
  30490. * [use HTML](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting#html)
  30491. * to render the labels.
  30492. *
  30493. * @type {boolean}
  30494. * @default false
  30495. * @apioption plotOptions.series.dataLabels.useHTML
  30496. */
  30497. /**
  30498. * The vertical alignment of a data label. Can be one of `top`,
  30499. * `middle` or `bottom`. The default value depends on the data, for
  30500. * instance in a column chart, the label is above positive values
  30501. * and below negative values.
  30502. *
  30503. * @type {Highcharts.VerticalAlignValue|null}
  30504. * @since 2.3.3
  30505. */
  30506. verticalAlign: 'bottom',
  30507. /**
  30508. * The x position offset of the label relative to the point in
  30509. * pixels.
  30510. *
  30511. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30512. * Vertical and positioned
  30513. * @sample {highcharts} highcharts/plotoptions/bar-datalabels-align-inside-bar/
  30514. * Data labels inside the bar
  30515. */
  30516. x: 0,
  30517. /**
  30518. * The Z index of the data labels. The default Z index puts it above
  30519. * the series. Use a Z index of 2 to display it behind the series.
  30520. *
  30521. * @type {number}
  30522. * @default 6
  30523. * @since 2.3.5
  30524. * @apioption plotOptions.series.dataLabels.z
  30525. */
  30526. /**
  30527. * The y position offset of the label relative to the point in
  30528. * pixels.
  30529. *
  30530. * @sample {highcharts} highcharts/plotoptions/series-datalabels-rotation/
  30531. * Vertical and positioned
  30532. */
  30533. y: 0
  30534. },
  30535. /**
  30536. * When the series contains less points than the crop threshold, all
  30537. * points are drawn, even if the points fall outside the visible plot
  30538. * area at the current zoom. The advantage of drawing all points
  30539. * (including markers and columns), is that animation is performed on
  30540. * updates. On the other hand, when the series contains more points than
  30541. * the crop threshold, the series data is cropped to only contain points
  30542. * that fall within the plot area. The advantage of cropping away
  30543. * invisible points is to increase performance on large series.
  30544. *
  30545. * @since 2.2
  30546. * @product highcharts highstock
  30547. *
  30548. * @private
  30549. */
  30550. cropThreshold: 300,
  30551. /**
  30552. * Opacity of a series parts: line, fill (e.g. area) and dataLabels.
  30553. *
  30554. * @see [states.inactive.opacity](#plotOptions.series.states.inactive.opacity)
  30555. *
  30556. * @since 7.1.0
  30557. *
  30558. * @private
  30559. */
  30560. opacity: 1,
  30561. /**
  30562. * The width of each point on the x axis. For example in a column chart
  30563. * with one value each day, the pointRange would be 1 day (= 24 * 3600
  30564. * * 1000 milliseconds). This is normally computed automatically, but
  30565. * this option can be used to override the automatic value.
  30566. *
  30567. * @product highstock
  30568. *
  30569. * @private
  30570. */
  30571. pointRange: 0,
  30572. /**
  30573. * When this is true, the series will not cause the Y axis to cross
  30574. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  30575. * unless the data actually crosses the plane.
  30576. *
  30577. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  30578. * 3 will make the Y axis show negative values according to the
  30579. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  30580. * at 0.
  30581. *
  30582. * @since 4.1.9
  30583. * @product highcharts highstock
  30584. *
  30585. * @private
  30586. */
  30587. softThreshold: true,
  30588. /**
  30589. * @declare Highcharts.SeriesStatesOptionsObject
  30590. *
  30591. * @private
  30592. */
  30593. states: {
  30594. /**
  30595. * The normal state of a series, or for point items in column, pie
  30596. * and similar series. Currently only used for setting animation
  30597. * when returning to normal state from hover.
  30598. *
  30599. * @declare Highcharts.SeriesStatesNormalOptionsObject
  30600. */
  30601. normal: {
  30602. /**
  30603. * Animation when returning to normal state after hovering.
  30604. *
  30605. * @type {boolean|Highcharts.AnimationOptionsObject}
  30606. */
  30607. animation: true
  30608. },
  30609. /**
  30610. * Options for the hovered series. These settings override the
  30611. * normal state options when a series is moused over or touched.
  30612. *
  30613. * @declare Highcharts.SeriesStatesHoverOptionsObject
  30614. */
  30615. hover: {
  30616. /**
  30617. * Enable separate styles for the hovered series to visualize
  30618. * that the user hovers either the series itself or the legend.
  30619. *
  30620. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled/
  30621. * Line
  30622. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-column/
  30623. * Column
  30624. * @sample {highcharts} highcharts/plotoptions/series-states-hover-enabled-pie/
  30625. * Pie
  30626. *
  30627. * @type {boolean}
  30628. * @default true
  30629. * @since 1.2
  30630. * @apioption plotOptions.series.states.hover.enabled
  30631. */
  30632. /**
  30633. * Animation setting for hovering the graph in line-type series.
  30634. *
  30635. * @type {boolean|Highcharts.AnimationOptionsObject}
  30636. * @since 5.0.8
  30637. * @product highcharts highstock
  30638. */
  30639. animation: {
  30640. /**
  30641. * The duration of the hover animation in milliseconds. By
  30642. * default the hover state animates quickly in, and slowly
  30643. * back to normal.
  30644. *
  30645. * @internal
  30646. */
  30647. duration: 50
  30648. },
  30649. /**
  30650. * Pixel width of the graph line. By default this property is
  30651. * undefined, and the `lineWidthPlus` property dictates how much
  30652. * to increase the linewidth from normal state.
  30653. *
  30654. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidth/
  30655. * 5px line on hover
  30656. *
  30657. * @type {number}
  30658. * @product highcharts highstock
  30659. * @apioption plotOptions.series.states.hover.lineWidth
  30660. */
  30661. /**
  30662. * The additional line width for the graph of a hovered series.
  30663. *
  30664. * @sample {highcharts} highcharts/plotoptions/series-states-hover-linewidthplus/
  30665. * 5 pixels wider
  30666. * @sample {highstock} highcharts/plotoptions/series-states-hover-linewidthplus/
  30667. * 5 pixels wider
  30668. *
  30669. * @since 4.0.3
  30670. * @product highcharts highstock
  30671. */
  30672. lineWidthPlus: 1,
  30673. /**
  30674. * In Highcharts 1.0, the appearance of all markers belonging
  30675. * to the hovered series. For settings on the hover state of the
  30676. * individual point, see
  30677. * [marker.states.hover](#plotOptions.series.marker.states.hover).
  30678. *
  30679. * @deprecated
  30680. *
  30681. * @extends plotOptions.series.marker
  30682. * @excluding states
  30683. * @product highcharts highstock
  30684. */
  30685. marker: {
  30686. // lineWidth: base + 1,
  30687. // radius: base + 1
  30688. },
  30689. /**
  30690. * Options for the halo appearing around the hovered point in
  30691. * line-type series as well as outside the hovered slice in pie
  30692. * charts. By default the halo is filled by the current point or
  30693. * series color with an opacity of 0.25\. The halo can be
  30694. * disabled by setting the `halo` option to `null`.
  30695. *
  30696. * In styled mode, the halo is styled with the
  30697. * `.highcharts-halo` class, with colors inherited from
  30698. * `.highcharts-color-{n}`.
  30699. *
  30700. * @sample {highcharts} highcharts/plotoptions/halo/
  30701. * Halo options
  30702. * @sample {highstock} highcharts/plotoptions/halo/
  30703. * Halo options
  30704. *
  30705. * @declare Highcharts.SeriesStatesHoverHaloOptionsObject
  30706. * @type {null|*}
  30707. * @since 4.0
  30708. * @product highcharts highstock
  30709. */
  30710. halo: {
  30711. /**
  30712. * A collection of SVG attributes to override the appearance
  30713. * of the halo, for example `fill`, `stroke` and
  30714. * `stroke-width`.
  30715. *
  30716. * @type {Highcharts.SVGAttributes}
  30717. * @since 4.0
  30718. * @product highcharts highstock
  30719. * @apioption plotOptions.series.states.hover.halo.attributes
  30720. */
  30721. /**
  30722. * The pixel size of the halo. For point markers this is the
  30723. * radius of the halo. For pie slices it is the width of the
  30724. * halo outside the slice. For bubbles it defaults to 5 and
  30725. * is the width of the halo outside the bubble.
  30726. *
  30727. * @since 4.0
  30728. * @product highcharts highstock
  30729. */
  30730. size: 10,
  30731. /**
  30732. * Opacity for the halo unless a specific fill is overridden
  30733. * using the `attributes` setting. Note that Highcharts is
  30734. * only able to apply opacity to colors of hex or rgb(a)
  30735. * formats.
  30736. *
  30737. * @since 4.0
  30738. * @product highcharts highstock
  30739. */
  30740. opacity: 0.25
  30741. }
  30742. },
  30743. /**
  30744. * Specific options for point in selected states, after being
  30745. * selected by
  30746. * [allowPointSelect](#plotOptions.series.allowPointSelect)
  30747. * or programmatically.
  30748. *
  30749. * @sample maps/plotoptions/series-allowpointselect/
  30750. * Allow point select demo
  30751. *
  30752. * @declare Highcharts.SeriesStatesSelectOptionsObject
  30753. * @extends plotOptions.series.states.hover
  30754. * @excluding brightness
  30755. */
  30756. select: {
  30757. animation: {
  30758. /** @internal */
  30759. duration: 0
  30760. }
  30761. },
  30762. /**
  30763. * The opposite state of a hover for series.
  30764. *
  30765. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30766. * Disabled inactive state
  30767. *
  30768. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  30769. */
  30770. inactive: {
  30771. /**
  30772. * Enable or disable the inactive state for a series
  30773. *
  30774. * @sample highcharts/plotoptions/series-states-inactive-disabled
  30775. * Disabled inactive state
  30776. *
  30777. * @type {boolean}
  30778. * @default true
  30779. * @apioption plotOptions.series.states.inactive.enabled
  30780. */
  30781. /**
  30782. * The animation for entering the inactive state.
  30783. *
  30784. * @type {boolean|Highcharts.AnimationOptionsObject}
  30785. */
  30786. animation: {
  30787. /** @internal */
  30788. duration: 50
  30789. },
  30790. /**
  30791. * Opacity of series elements (dataLabels, line, area).
  30792. *
  30793. * @type {number}
  30794. */
  30795. opacity: 0.2
  30796. }
  30797. },
  30798. /**
  30799. * Sticky tracking of mouse events. When true, the `mouseOut` event on a
  30800. * series isn't triggered until the mouse moves over another series, or
  30801. * out of the plot area. When false, the `mouseOut` event on a series is
  30802. * triggered when the mouse leaves the area around the series' graph or
  30803. * markers. This also implies the tooltip when not shared. When
  30804. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  30805. * will be hidden when moving the mouse between series. Defaults to true
  30806. * for line and area type series, but to false for columns, pies etc.
  30807. *
  30808. * **Note:** The boost module will force this option because of
  30809. * technical limitations.
  30810. *
  30811. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-true/
  30812. * True by default
  30813. * @sample {highcharts} highcharts/plotoptions/series-stickytracking-false/
  30814. * False
  30815. *
  30816. * @default {highcharts} true
  30817. * @default {highstock} true
  30818. * @default {highmaps} false
  30819. * @since 2.0
  30820. *
  30821. * @private
  30822. */
  30823. stickyTracking: true,
  30824. /**
  30825. * A configuration object for the tooltip rendering of each single
  30826. * series. Properties are inherited from [tooltip](#tooltip), but only
  30827. * the following properties can be defined on a series level.
  30828. *
  30829. * @declare Highcharts.SeriesTooltipOptionsObject
  30830. * @since 2.3
  30831. * @extends tooltip
  30832. * @excluding animation, backgroundColor, borderColor, borderRadius,
  30833. * borderWidth, className, crosshairs, enabled, formatter,
  30834. * headerShape, hideDelay, outside, padding, positioner,
  30835. * shadow, shape, shared, snap, split, style, useHTML
  30836. * @apioption plotOptions.series.tooltip
  30837. */
  30838. /**
  30839. * When a series contains a data array that is longer than this, only
  30840. * one dimensional arrays of numbers, or two dimensional arrays with
  30841. * x and y values are allowed. Also, only the first point is tested,
  30842. * and the rest are assumed to be the same format. This saves expensive
  30843. * data checking and indexing in long series. Set it to `0` disable.
  30844. *
  30845. * Note:
  30846. * In boost mode turbo threshold is forced. Only array of numbers or
  30847. * two dimensional arrays are allowed.
  30848. *
  30849. * @since 2.2
  30850. * @product highcharts highstock gantt
  30851. *
  30852. * @private
  30853. */
  30854. turboThreshold: 1000,
  30855. /**
  30856. * An array defining zones within a series. Zones can be applied to the
  30857. * X axis, Y axis or Z axis for bubbles, according to the `zoneAxis`
  30858. * option. The zone definitions have to be in ascending order regarding
  30859. * to the value.
  30860. *
  30861. * In styled mode, the color zones are styled with the
  30862. * `.highcharts-zone-{n}` class, or custom classed from the `className`
  30863. * option
  30864. * ([view live demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/color-zones/)).
  30865. *
  30866. * @see [zoneAxis](#plotOptions.series.zoneAxis)
  30867. *
  30868. * @sample {highcharts} highcharts/series/color-zones-simple/
  30869. * Color zones
  30870. * @sample {highstock} highcharts/series/color-zones-simple/
  30871. * Color zones
  30872. *
  30873. * @declare Highcharts.SeriesZonesOptionsObject
  30874. * @type {Array<*>}
  30875. * @since 4.1.0
  30876. * @product highcharts highstock
  30877. * @apioption plotOptions.series.zones
  30878. */
  30879. /**
  30880. * Styled mode only. A custom class name for the zone.
  30881. *
  30882. * @sample highcharts/css/color-zones/
  30883. * Zones styled by class name
  30884. *
  30885. * @type {string}
  30886. * @since 5.0.0
  30887. * @apioption plotOptions.series.zones.className
  30888. */
  30889. /**
  30890. * Defines the color of the series.
  30891. *
  30892. * @see [series color](#plotOptions.series.color)
  30893. *
  30894. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30895. * @since 4.1.0
  30896. * @product highcharts highstock
  30897. * @apioption plotOptions.series.zones.color
  30898. */
  30899. /**
  30900. * A name for the dash style to use for the graph.
  30901. *
  30902. * @see [plotOptions.series.dashStyle](#plotOptions.series.dashStyle)
  30903. *
  30904. * @sample {highcharts|highstock} highcharts/series/color-zones-dashstyle-dot/
  30905. * Dashed line indicates prognosis
  30906. *
  30907. * @type {Highcharts.DashStyleValue}
  30908. * @since 4.1.0
  30909. * @product highcharts highstock
  30910. * @apioption plotOptions.series.zones.dashStyle
  30911. */
  30912. /**
  30913. * Defines the fill color for the series (in area type series)
  30914. *
  30915. * @see [fillColor](#plotOptions.area.fillColor)
  30916. *
  30917. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  30918. * @since 4.1.0
  30919. * @product highcharts highstock
  30920. * @apioption plotOptions.series.zones.fillColor
  30921. */
  30922. /**
  30923. * The value up to where the zone extends, if undefined the zones
  30924. * stretches to the last value in the series.
  30925. *
  30926. * @type {number}
  30927. * @since 4.1.0
  30928. * @product highcharts highstock
  30929. * @apioption plotOptions.series.zones.value
  30930. */
  30931. /**
  30932. * When using dual or multiple color axes, this number defines which
  30933. * colorAxis the particular series is connected to. It refers to
  30934. * either the
  30935. * {@link #colorAxis.id|axis id}
  30936. * or the index of the axis in the colorAxis array, with 0 being the
  30937. * first. Set this option to false to prevent a series from connecting
  30938. * to the default color axis.
  30939. *
  30940. * Since v7.2.0 the option can also be an axis id or an axis index
  30941. * instead of a boolean flag.
  30942. *
  30943. * @sample highcharts/coloraxis/coloraxis-with-pie/
  30944. * Color axis with pie series
  30945. * @sample highcharts/coloraxis/multiple-coloraxis/
  30946. * Multiple color axis
  30947. *
  30948. * @type {number|string|boolean}
  30949. * @default 0
  30950. * @product highcharts highstock highmaps
  30951. * @apioption plotOptions.series.colorAxis
  30952. */
  30953. /**
  30954. * Determines what data value should be used to calculate point color
  30955. * if `colorAxis` is used. Requires to set `min` and `max` if some
  30956. * custom point property is used or if approximation for data grouping
  30957. * is set to `'sum'`.
  30958. *
  30959. * @sample highcharts/coloraxis/custom-color-key/
  30960. * Custom color key
  30961. * @sample highcharts/coloraxis/changed-default-color-key/
  30962. * Changed default color key
  30963. *
  30964. * @type {string}
  30965. * @default y
  30966. * @since 7.2.0
  30967. * @product highcharts highstock highmaps
  30968. * @apioption plotOptions.series.colorKey
  30969. */
  30970. /**
  30971. * Determines whether the series should look for the nearest point
  30972. * in both dimensions or just the x-dimension when hovering the series.
  30973. * Defaults to `'xy'` for scatter series and `'x'` for most other
  30974. * series. If the data has duplicate x-values, it is recommended to
  30975. * set this to `'xy'` to allow hovering over all points.
  30976. *
  30977. * Applies only to series types using nearest neighbor search (not
  30978. * direct hover) for tooltip.
  30979. *
  30980. * @sample {highcharts} highcharts/series/findnearestpointby/
  30981. * Different hover behaviors
  30982. * @sample {highstock} highcharts/series/findnearestpointby/
  30983. * Different hover behaviors
  30984. * @sample {highmaps} highcharts/series/findnearestpointby/
  30985. * Different hover behaviors
  30986. *
  30987. * @since 5.0.10
  30988. * @validvalue ["x", "xy"]
  30989. *
  30990. * @private
  30991. */
  30992. findNearestPointBy: 'x'
  30993. },
  30994. /* eslint-disable no-invalid-this, valid-jsdoc */
  30995. /** @lends Highcharts.Series.prototype */
  30996. {
  30997. axisTypes: ['xAxis', 'yAxis'],
  30998. coll: 'series',
  30999. colorCounter: 0,
  31000. cropShoulder: 1,
  31001. directTouch: false,
  31002. eventsToUnbind: [],
  31003. isCartesian: true,
  31004. // each point's x and y values are stored in this.xData and this.yData
  31005. parallelArrays: ['x', 'y'],
  31006. pointClass: Point,
  31007. requireSorting: true,
  31008. sorted: true,
  31009. init: function (chart, options) {
  31010. fireEvent(this, 'init', { options: options });
  31011. var series = this, events, chartSeries = chart.series, lastSeries;
  31012. // A lookup over those events that are added by _options_ (not
  31013. // programmatically). These are updated through Series.update()
  31014. // (#10861).
  31015. this.eventOptions = this.eventOptions || {};
  31016. /**
  31017. * Read only. The chart that the series belongs to.
  31018. *
  31019. * @name Highcharts.Series#chart
  31020. * @type {Highcharts.Chart}
  31021. */
  31022. series.chart = chart;
  31023. /**
  31024. * Read only. The series' type, like "line", "area", "column" etc.
  31025. * The type in the series options anc can be altered using
  31026. * {@link Series#update}.
  31027. *
  31028. * @name Highcharts.Series#type
  31029. * @type {string}
  31030. */
  31031. /**
  31032. * Read only. The series' current options. To update, use
  31033. * {@link Series#update}.
  31034. *
  31035. * @name Highcharts.Series#options
  31036. * @type {Highcharts.SeriesOptionsType}
  31037. */
  31038. series.options = options = series.setOptions(options);
  31039. series.linkedSeries = [];
  31040. // bind the axes
  31041. series.bindAxes();
  31042. // set some variables
  31043. extend(series, {
  31044. /**
  31045. * The series name as given in the options. Defaults to
  31046. * "Series {n}".
  31047. *
  31048. * @name Highcharts.Series#name
  31049. * @type {string}
  31050. */
  31051. name: options.name,
  31052. state: '',
  31053. /**
  31054. * Read only. The series' visibility state as set by {@link
  31055. * Series#show}, {@link Series#hide}, or in the initial
  31056. * configuration.
  31057. *
  31058. * @name Highcharts.Series#visible
  31059. * @type {boolean}
  31060. */
  31061. visible: options.visible !== false,
  31062. /**
  31063. * Read only. The series' selected state as set by {@link
  31064. * Highcharts.Series#select}.
  31065. *
  31066. * @name Highcharts.Series#selected
  31067. * @type {boolean}
  31068. */
  31069. selected: options.selected === true // false by default
  31070. });
  31071. // Register event listeners
  31072. events = options.events;
  31073. objectEach(events, function (event, eventType) {
  31074. if (isFunction(event)) {
  31075. // If event does not exist, or is changed by Series.update
  31076. if (series.eventOptions[eventType] !== event) {
  31077. // Remove existing if set by option
  31078. if (isFunction(series.eventOptions[eventType])) {
  31079. removeEvent(series, eventType, series.eventOptions[eventType]);
  31080. }
  31081. series.eventOptions[eventType] = event;
  31082. addEvent(series, eventType, event);
  31083. }
  31084. }
  31085. });
  31086. if ((events && events.click) ||
  31087. (options.point &&
  31088. options.point.events &&
  31089. options.point.events.click) ||
  31090. options.allowPointSelect) {
  31091. chart.runTrackerClick = true;
  31092. }
  31093. series.getColor();
  31094. series.getSymbol();
  31095. // Initialize the parallel data arrays
  31096. series.parallelArrays.forEach(function (key) {
  31097. if (!series[key + 'Data']) {
  31098. series[key + 'Data'] = [];
  31099. }
  31100. });
  31101. // Mark cartesian
  31102. if (series.isCartesian) {
  31103. chart.hasCartesianSeries = true;
  31104. }
  31105. // Get the index and register the series in the chart. The index is
  31106. // one more than the current latest series index (#5960).
  31107. if (chartSeries.length) {
  31108. lastSeries = chartSeries[chartSeries.length - 1];
  31109. }
  31110. series._i = pick(lastSeries && lastSeries._i, -1) + 1;
  31111. series.opacity = series.options.opacity;
  31112. // Insert the series and re-order all series above the insertion
  31113. // point.
  31114. chart.orderSeries(this.insert(chartSeries));
  31115. // Set options for series with sorting and set data later.
  31116. if (options.dataSorting && options.dataSorting.enabled) {
  31117. series.setDataSortingOptions();
  31118. }
  31119. else if (!series.points && !series.data) {
  31120. series.setData(options.data, false);
  31121. }
  31122. fireEvent(this, 'afterInit');
  31123. },
  31124. /**
  31125. * Check whether the series item is itself or inherits from a certain
  31126. * series type.
  31127. *
  31128. * @function Highcharts.Series#is
  31129. * @param {string} type The type of series to check for, can be either
  31130. * featured or custom series types. For example `column`, `pie`,
  31131. * `ohlc` etc.
  31132. *
  31133. * @return {boolean}
  31134. * True if this item is or inherits from the given type.
  31135. */
  31136. is: function (type) {
  31137. return seriesTypes[type] && this instanceof seriesTypes[type];
  31138. },
  31139. /**
  31140. * Insert the series in a collection with other series, either the chart
  31141. * series or yAxis series, in the correct order according to the index
  31142. * option. Used internally when adding series.
  31143. *
  31144. * @private
  31145. * @function Highcharts.Series#insert
  31146. * @param {Array<Highcharts.Series>} collection
  31147. * A collection of series, like `chart.series` or `xAxis.series`.
  31148. * @return {number}
  31149. * The index of the series in the collection.
  31150. */
  31151. insert: function (collection) {
  31152. var indexOption = this.options.index, i;
  31153. // Insert by index option
  31154. if (isNumber(indexOption)) {
  31155. i = collection.length;
  31156. while (i--) {
  31157. // Loop down until the interted element has higher index
  31158. if (indexOption >=
  31159. pick(collection[i].options.index, collection[i]._i)) {
  31160. collection.splice(i + 1, 0, this);
  31161. break;
  31162. }
  31163. }
  31164. if (i === -1) {
  31165. collection.unshift(this);
  31166. }
  31167. i = i + 1;
  31168. // Or just push it to the end
  31169. }
  31170. else {
  31171. collection.push(this);
  31172. }
  31173. return pick(i, collection.length - 1);
  31174. },
  31175. /**
  31176. * Set the xAxis and yAxis properties of cartesian series, and register
  31177. * the series in the `axis.series` array.
  31178. *
  31179. * @private
  31180. * @function Highcharts.Series#bindAxes
  31181. * @return {void}
  31182. * @exception 18
  31183. */
  31184. bindAxes: function () {
  31185. var series = this, seriesOptions = series.options, chart = series.chart, axisOptions;
  31186. fireEvent(this, 'bindAxes', null, function () {
  31187. // repeat for xAxis and yAxis
  31188. (series.axisTypes || []).forEach(function (AXIS) {
  31189. // loop through the chart's axis objects
  31190. chart[AXIS].forEach(function (axis) {
  31191. axisOptions = axis.options;
  31192. // apply if the series xAxis or yAxis option mathches
  31193. // the number of the axis, or if undefined, use the
  31194. // first axis
  31195. if (seriesOptions[AXIS] ===
  31196. axisOptions.index ||
  31197. (typeof seriesOptions[AXIS] !==
  31198. 'undefined' &&
  31199. seriesOptions[AXIS] === axisOptions.id) ||
  31200. (typeof seriesOptions[AXIS] ===
  31201. 'undefined' &&
  31202. axisOptions.index === 0)) {
  31203. // register this series in the axis.series lookup
  31204. series.insert(axis.series);
  31205. // set this series.xAxis or series.yAxis reference
  31206. /**
  31207. * Read only. The unique xAxis object associated
  31208. * with the series.
  31209. *
  31210. * @name Highcharts.Series#xAxis
  31211. * @type {Highcharts.Axis}
  31212. */
  31213. /**
  31214. * Read only. The unique yAxis object associated
  31215. * with the series.
  31216. *
  31217. * @name Highcharts.Series#yAxis
  31218. * @type {Highcharts.Axis}
  31219. */
  31220. series[AXIS] = axis;
  31221. // mark dirty for redraw
  31222. axis.isDirty = true;
  31223. }
  31224. });
  31225. // The series needs an X and an Y axis
  31226. if (!series[AXIS] &&
  31227. series.optionalAxis !== AXIS) {
  31228. error(18, true, chart);
  31229. }
  31230. });
  31231. });
  31232. fireEvent(this, 'afterBindAxes');
  31233. },
  31234. /**
  31235. * For simple series types like line and column, the data values are
  31236. * held in arrays like xData and yData for quick lookup to find extremes
  31237. * and more. For multidimensional series like bubble and map, this can
  31238. * be extended with arrays like zData and valueData by adding to the
  31239. * `series.parallelArrays` array.
  31240. *
  31241. * @private
  31242. * @function Highcharts.Series#updateParallelArrays
  31243. * @param {Highcharts.Point} point
  31244. * @param {number|string} i
  31245. * @return {void}
  31246. */
  31247. updateParallelArrays: function (point, i) {
  31248. var series = point.series, args = arguments, fn = isNumber(i) ?
  31249. // Insert the value in the given position
  31250. function (key) {
  31251. var val = key === 'y' && series.toYData ?
  31252. series.toYData(point) :
  31253. point[key];
  31254. series[key + 'Data'][i] = val;
  31255. } :
  31256. // Apply the method specified in i with the following
  31257. // arguments as arguments
  31258. function (key) {
  31259. Array.prototype[i].apply(series[key + 'Data'], Array.prototype.slice.call(args, 2));
  31260. };
  31261. series.parallelArrays.forEach(fn);
  31262. },
  31263. /**
  31264. * Define hasData functions for series. These return true if there
  31265. * are data points on this series within the plot area.
  31266. *
  31267. * @private
  31268. * @function Highcharts.Series#hasData
  31269. * @return {boolean}
  31270. */
  31271. hasData: function () {
  31272. return ((this.visible &&
  31273. typeof this.dataMax !== 'undefined' &&
  31274. typeof this.dataMin !== 'undefined') || ( // #3703
  31275. this.visible &&
  31276. this.yData &&
  31277. this.yData.length > 0) // #9758
  31278. );
  31279. },
  31280. /**
  31281. * Return an auto incremented x value based on the pointStart and
  31282. * pointInterval options. This is only used if an x value is not given
  31283. * for the point that calls autoIncrement.
  31284. *
  31285. * @private
  31286. * @function Highcharts.Series#autoIncrement
  31287. * @return {number}
  31288. */
  31289. autoIncrement: function () {
  31290. var options = this.options, xIncrement = this.xIncrement, date, pointInterval, pointIntervalUnit = options.pointIntervalUnit, time = this.chart.time;
  31291. xIncrement = pick(xIncrement, options.pointStart, 0);
  31292. this.pointInterval = pointInterval = pick(this.pointInterval, options.pointInterval, 1);
  31293. // Added code for pointInterval strings
  31294. if (pointIntervalUnit) {
  31295. date = new time.Date(xIncrement);
  31296. if (pointIntervalUnit === 'day') {
  31297. time.set('Date', date, time.get('Date', date) + pointInterval);
  31298. }
  31299. else if (pointIntervalUnit === 'month') {
  31300. time.set('Month', date, time.get('Month', date) + pointInterval);
  31301. }
  31302. else if (pointIntervalUnit === 'year') {
  31303. time.set('FullYear', date, time.get('FullYear', date) + pointInterval);
  31304. }
  31305. pointInterval = date.getTime() - xIncrement;
  31306. }
  31307. this.xIncrement = xIncrement + pointInterval;
  31308. return xIncrement;
  31309. },
  31310. /**
  31311. * Internal function to set properties for series if data sorting is
  31312. * enabled.
  31313. *
  31314. * @private
  31315. * @function Highcharts.Series#setDataSortingOptions
  31316. * @return {void}
  31317. */
  31318. setDataSortingOptions: function () {
  31319. var options = this.options;
  31320. extend(this, {
  31321. requireSorting: false,
  31322. sorted: false,
  31323. enabledDataSorting: true,
  31324. allowDG: false
  31325. });
  31326. // To allow unsorted data for column series.
  31327. if (!defined(options.pointRange)) {
  31328. options.pointRange = 1;
  31329. }
  31330. },
  31331. /**
  31332. * Set the series options by merging from the options tree. Called
  31333. * internally on initializing and updating series. This function will
  31334. * not redraw the series. For API usage, use {@link Series#update}.
  31335. * @private
  31336. * @function Highcharts.Series#setOptions
  31337. * @param {Highcharts.SeriesOptionsType} itemOptions
  31338. * The series options.
  31339. * @return {Highcharts.SeriesOptionsType}
  31340. * @fires Highcharts.Series#event:afterSetOptions
  31341. */
  31342. setOptions: function (itemOptions) {
  31343. var chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, userOptions = chart.userOptions || {}, seriesUserOptions = merge(itemOptions), options, zones, zone, styledMode = chart.styledMode, e = {
  31344. plotOptions: plotOptions,
  31345. userOptions: seriesUserOptions
  31346. };
  31347. fireEvent(this, 'setOptions', e);
  31348. // These may be modified by the event
  31349. var typeOptions = e.plotOptions[this.type], userPlotOptions = (userOptions.plotOptions || {});
  31350. // use copy to prevent undetected changes (#9762)
  31351. /**
  31352. * Contains series options by the user without defaults.
  31353. * @name Highcharts.Series#userOptions
  31354. * @type {Highcharts.SeriesOptionsType}
  31355. */
  31356. this.userOptions = e.userOptions;
  31357. options = merge(typeOptions, plotOptions.series,
  31358. // #3881, chart instance plotOptions[type] should trump
  31359. // plotOptions.series
  31360. userOptions.plotOptions &&
  31361. userOptions.plotOptions[this.type], seriesUserOptions);
  31362. // The tooltip options are merged between global and series specific
  31363. // options. Importance order asscendingly:
  31364. // globals: (1)tooltip, (2)plotOptions.series,
  31365. // (3)plotOptions[this.type]
  31366. // init userOptions with possible later updates: 4-6 like 1-3 and
  31367. // (7)this series options
  31368. this.tooltipOptions = merge(defaultOptions.tooltip, // 1
  31369. defaultOptions.plotOptions.series &&
  31370. defaultOptions.plotOptions.series.tooltip, // 2
  31371. defaultOptions.plotOptions[this.type].tooltip, // 3
  31372. chartOptions.tooltip.userOptions, // 4
  31373. plotOptions.series &&
  31374. plotOptions.series.tooltip, // 5
  31375. plotOptions[this.type].tooltip, // 6
  31376. seriesUserOptions.tooltip // 7
  31377. );
  31378. // When shared tooltip, stickyTracking is true by default,
  31379. // unless user says otherwise.
  31380. this.stickyTracking = pick(seriesUserOptions.stickyTracking, userPlotOptions[this.type] &&
  31381. userPlotOptions[this.type].stickyTracking, userPlotOptions.series && userPlotOptions.series.stickyTracking, (this.tooltipOptions.shared && !this.noSharedTooltip ?
  31382. true :
  31383. options.stickyTracking));
  31384. // Delete marker object if not allowed (#1125)
  31385. if (typeOptions.marker === null) {
  31386. delete options.marker;
  31387. }
  31388. // Handle color zones
  31389. this.zoneAxis = options.zoneAxis;
  31390. zones = this.zones = (options.zones || []).slice();
  31391. if ((options.negativeColor || options.negativeFillColor) &&
  31392. !options.zones) {
  31393. zone = {
  31394. value: options[this.zoneAxis + 'Threshold'] ||
  31395. options.threshold ||
  31396. 0,
  31397. className: 'highcharts-negative'
  31398. };
  31399. if (!styledMode) {
  31400. zone.color = options.negativeColor;
  31401. zone.fillColor = options.negativeFillColor;
  31402. }
  31403. zones.push(zone);
  31404. }
  31405. if (zones.length) { // Push one extra zone for the rest
  31406. if (defined(zones[zones.length - 1].value)) {
  31407. zones.push(styledMode ? {} : {
  31408. color: this.color,
  31409. fillColor: this.fillColor
  31410. });
  31411. }
  31412. }
  31413. fireEvent(this, 'afterSetOptions', { options: options });
  31414. return options;
  31415. },
  31416. /**
  31417. * Return series name in "Series {Number}" format or the one defined by
  31418. * a user. This method can be simply overridden as series name format
  31419. * can vary (e.g. technical indicators).
  31420. *
  31421. * @function Highcharts.Series#getName
  31422. * @return {string}
  31423. * The series name.
  31424. */
  31425. getName: function () {
  31426. // #4119
  31427. return pick(this.options.name, 'Series ' + (this.index + 1));
  31428. },
  31429. /**
  31430. * @private
  31431. * @function Highcharts.Series#getCyclic
  31432. * @param {string} prop
  31433. * @param {*} [value]
  31434. * @param {Highcharts.Dictionary<any>} [defaults]
  31435. * @return {void}
  31436. */
  31437. getCyclic: function (prop, value, defaults) {
  31438. var i, chart = this.chart, userOptions = this.userOptions, indexName = prop + 'Index', counterName = prop + 'Counter', len = defaults ? defaults.length : pick(chart.options.chart[prop + 'Count'], chart[prop + 'Count']), setting;
  31439. if (!value) {
  31440. // Pick up either the colorIndex option, or the _colorIndex
  31441. // after Series.update()
  31442. setting = pick(userOptions[indexName], userOptions['_' + indexName]);
  31443. if (defined(setting)) { // after Series.update()
  31444. i = setting;
  31445. }
  31446. else {
  31447. // #6138
  31448. if (!chart.series.length) {
  31449. chart[counterName] = 0;
  31450. }
  31451. userOptions['_' + indexName] = i =
  31452. chart[counterName] % len;
  31453. chart[counterName] += 1;
  31454. }
  31455. if (defaults) {
  31456. value = defaults[i];
  31457. }
  31458. }
  31459. // Set the colorIndex
  31460. if (typeof i !== 'undefined') {
  31461. this[indexName] = i;
  31462. }
  31463. this[prop] = value;
  31464. },
  31465. /**
  31466. * Get the series' color based on either the options or pulled from
  31467. * global options.
  31468. *
  31469. * @private
  31470. * @function Highcharts.Series#getColor
  31471. * @return {void}
  31472. */
  31473. getColor: function () {
  31474. if (this.chart.styledMode) {
  31475. this.getCyclic('color');
  31476. }
  31477. else if (this.options.colorByPoint) {
  31478. // #4359, selected slice got series.color even when colorByPoint
  31479. // was set.
  31480. this.options.color = null;
  31481. }
  31482. else {
  31483. this.getCyclic('color', this.options.color ||
  31484. defaultOptions.plotOptions[this.type].color, this.chart.options.colors);
  31485. }
  31486. },
  31487. /**
  31488. * Get all points' instances created for this series.
  31489. *
  31490. * @private
  31491. * @function Highcharts.Series#getPointsCollection
  31492. * @return {Array<Highcharts.Point>}
  31493. */
  31494. getPointsCollection: function () {
  31495. return (this.hasGroupedData ? this.points : this.data) || [];
  31496. },
  31497. /**
  31498. * Get the series' symbol based on either the options or pulled from
  31499. * global options.
  31500. *
  31501. * @private
  31502. * @function Highcharts.Series#getSymbol
  31503. * @return {void}
  31504. */
  31505. getSymbol: function () {
  31506. var seriesMarkerOption = this.options.marker;
  31507. this.getCyclic('symbol', seriesMarkerOption.symbol, this.chart.options.symbols);
  31508. },
  31509. /**
  31510. * Finds the index of an existing point that matches the given point
  31511. * options.
  31512. *
  31513. * @private
  31514. * @function Highcharts.Series#findPointIndex
  31515. * @param {Highcharts.PointOptionsObject} optionsObject
  31516. * The options of the point.
  31517. * @param {number} fromIndex
  31518. * The index to start searching from, used for optimizing
  31519. * series with required sorting.
  31520. * @returns {number|undefined}
  31521. * Returns the index of a matching point, or undefined if no
  31522. * match is found.
  31523. */
  31524. findPointIndex: function (optionsObject, fromIndex) {
  31525. var id = optionsObject.id, x = optionsObject.x, oldData = this.points, matchingPoint, matchedById, pointIndex, matchKey, dataSorting = this.options.dataSorting;
  31526. if (id) {
  31527. matchingPoint = this.chart.get(id);
  31528. }
  31529. else if (this.linkedParent || this.enabledDataSorting) {
  31530. matchKey = (dataSorting && dataSorting.matchByName) ?
  31531. 'name' : 'index';
  31532. matchingPoint = find(oldData, function (oldPoint) {
  31533. return !oldPoint.touched && oldPoint[matchKey] ===
  31534. optionsObject[matchKey];
  31535. });
  31536. // Add unmatched point as a new point
  31537. if (!matchingPoint) {
  31538. return void 0;
  31539. }
  31540. }
  31541. if (matchingPoint) {
  31542. pointIndex = matchingPoint && matchingPoint.index;
  31543. if (typeof pointIndex !== 'undefined') {
  31544. matchedById = true;
  31545. }
  31546. }
  31547. // Search for the same X in the existing data set
  31548. if (typeof pointIndex === 'undefined' && isNumber(x)) {
  31549. pointIndex = this.xData.indexOf(x, fromIndex);
  31550. }
  31551. // Reduce pointIndex if data is cropped
  31552. if (pointIndex !== -1 &&
  31553. typeof pointIndex !== 'undefined' &&
  31554. this.cropped) {
  31555. pointIndex = (pointIndex >= this.cropStart) ?
  31556. pointIndex - this.cropStart : pointIndex;
  31557. }
  31558. if (!matchedById &&
  31559. oldData[pointIndex] && oldData[pointIndex].touched) {
  31560. pointIndex = void 0;
  31561. }
  31562. return pointIndex;
  31563. },
  31564. /**
  31565. * @private
  31566. * @borrows LegendSymbolMixin.drawLineMarker as Highcharts.Series#drawLegendSymbol
  31567. */
  31568. drawLegendSymbol: LegendSymbolMixin.drawLineMarker,
  31569. /**
  31570. * Internal function called from setData. If the point count is the same
  31571. * as is was, or if there are overlapping X values, just run
  31572. * Point.update which is cheaper, allows animation, and keeps references
  31573. * to points. This also allows adding or removing points if the X-es
  31574. * don't match.
  31575. *
  31576. * @private
  31577. * @function Highcharts.Series#updateData
  31578. *
  31579. * @param {Array<Highcharts.PointOptionsType>} data
  31580. *
  31581. * @return {boolean}
  31582. */
  31583. updateData: function (data, animation) {
  31584. var options = this.options, dataSorting = options.dataSorting, oldData = this.points, pointsToAdd = [], hasUpdatedByKey, i, point, lastIndex, requireSorting = this.requireSorting, equalLength = data.length === oldData.length, succeeded = true;
  31585. this.xIncrement = null;
  31586. // Iterate the new data
  31587. data.forEach(function (pointOptions, i) {
  31588. var id, x, pointIndex, optionsObject = (defined(pointOptions) &&
  31589. this.pointClass.prototype.optionsToObject.call({ series: this }, pointOptions)) || {};
  31590. // Get the x of the new data point
  31591. x = optionsObject.x;
  31592. id = optionsObject.id;
  31593. if (id || isNumber(x)) {
  31594. pointIndex = this.findPointIndex(optionsObject, lastIndex);
  31595. // Matching X not found
  31596. // or used already due to ununique x values (#8995),
  31597. // add point (but later)
  31598. if (pointIndex === -1 ||
  31599. typeof pointIndex === 'undefined') {
  31600. pointsToAdd.push(pointOptions);
  31601. // Matching X found, update
  31602. }
  31603. else if (oldData[pointIndex] &&
  31604. pointOptions !== options.data[pointIndex]) {
  31605. oldData[pointIndex].update(pointOptions, false, null, false);
  31606. // Mark it touched, below we will remove all points that
  31607. // are not touched.
  31608. oldData[pointIndex].touched = true;
  31609. // Speed optimize by only searching after last known
  31610. // index. Performs ~20% bettor on large data sets.
  31611. if (requireSorting) {
  31612. lastIndex = pointIndex + 1;
  31613. }
  31614. // Point exists, no changes, don't remove it
  31615. }
  31616. else if (oldData[pointIndex]) {
  31617. oldData[pointIndex].touched = true;
  31618. }
  31619. // If the length is equal and some of the nodes had a
  31620. // match in the same position, we don't want to remove
  31621. // non-matches.
  31622. if (!equalLength ||
  31623. i !== pointIndex ||
  31624. (dataSorting && dataSorting.enabled) ||
  31625. this.hasDerivedData) {
  31626. hasUpdatedByKey = true;
  31627. }
  31628. }
  31629. else {
  31630. // Gather all points that are not matched
  31631. pointsToAdd.push(pointOptions);
  31632. }
  31633. }, this);
  31634. // Remove points that don't exist in the updated data set
  31635. if (hasUpdatedByKey) {
  31636. i = oldData.length;
  31637. while (i--) {
  31638. point = oldData[i];
  31639. if (point && !point.touched && point.remove) {
  31640. point.remove(false, animation);
  31641. }
  31642. }
  31643. // If we did not find keys (ids or x-values), and the length is the
  31644. // same, update one-to-one
  31645. }
  31646. else if (equalLength && (!dataSorting || !dataSorting.enabled)) {
  31647. data.forEach(function (point, i) {
  31648. // .update doesn't exist on a linked, hidden series (#3709)
  31649. // (#10187)
  31650. if (oldData[i].update && point !== oldData[i].y) {
  31651. oldData[i].update(point, false, null, false);
  31652. }
  31653. });
  31654. // Don't add new points since those configs are used above
  31655. pointsToAdd.length = 0;
  31656. // Did not succeed in updating data
  31657. }
  31658. else {
  31659. succeeded = false;
  31660. }
  31661. oldData.forEach(function (point) {
  31662. if (point) {
  31663. point.touched = false;
  31664. }
  31665. });
  31666. if (!succeeded) {
  31667. return false;
  31668. }
  31669. // Add new points
  31670. pointsToAdd.forEach(function (point) {
  31671. this.addPoint(point, false, null, null, false);
  31672. }, this);
  31673. if (this.xIncrement === null &&
  31674. this.xData &&
  31675. this.xData.length) {
  31676. this.xIncrement = arrayMax(this.xData);
  31677. this.autoIncrement();
  31678. }
  31679. return true;
  31680. },
  31681. /**
  31682. * Apply a new set of data to the series and optionally redraw it. The
  31683. * new data array is passed by reference (except in case of
  31684. * `updatePoints`), and may later be mutated when updating the chart
  31685. * data.
  31686. *
  31687. * Note the difference in behaviour when setting the same amount of
  31688. * points, or a different amount of points, as handled by the
  31689. * `updatePoints` parameter.
  31690. *
  31691. * @sample highcharts/members/series-setdata/
  31692. * Set new data from a button
  31693. * @sample highcharts/members/series-setdata-pie/
  31694. * Set data in a pie
  31695. * @sample stock/members/series-setdata/
  31696. * Set new data in Highstock
  31697. * @sample maps/members/series-setdata/
  31698. * Set new data in Highmaps
  31699. *
  31700. * @function Highcharts.Series#setData
  31701. *
  31702. * @param {Array<Highcharts.PointOptionsType>} data
  31703. * Takes an array of data in the same format as described under
  31704. * `series.{type}.data` for the given series type, for example a
  31705. * line series would take data in the form described under
  31706. * [series.line.data](https://api.highcharts.com/highcharts/series.line.data).
  31707. *
  31708. * @param {boolean} [redraw=true]
  31709. * Whether to redraw the chart after the series is altered. If
  31710. * doing more operations on the chart, it is a good idea to set
  31711. * redraw to false and call {@link Chart#redraw} after.
  31712. *
  31713. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  31714. * When the updated data is the same length as the existing data,
  31715. * points will be updated by default, and animation visualizes
  31716. * how the points are changed. Set false to disable animation, or
  31717. * a configuration object to set duration or easing.
  31718. *
  31719. * @param {boolean} [updatePoints=true]
  31720. * When this is true, points will be updated instead of replaced
  31721. * whenever possible. This occurs a) when the updated data is the
  31722. * same length as the existing data, b) when points are matched
  31723. * by their id's, or c) when points can be matched by X values.
  31724. * This allows updating with animation and performs better. In
  31725. * this case, the original array is not passed by reference. Set
  31726. * `false` to prevent.
  31727. *
  31728. * @return {void}
  31729. */
  31730. setData: function (data, redraw, animation, updatePoints) {
  31731. var series = this, oldData = series.points, oldDataLength = (oldData && oldData.length) || 0, dataLength, options = series.options, chart = series.chart, dataSorting = options.dataSorting, firstPoint = null, xAxis = series.xAxis, i, turboThreshold = options.turboThreshold, pt, xData = this.xData, yData = this.yData, pointArrayMap = series.pointArrayMap, valueCount = pointArrayMap && pointArrayMap.length, keys = options.keys, indexOfX = 0, indexOfY = 1, updatedData;
  31732. data = data || [];
  31733. dataLength = data.length;
  31734. redraw = pick(redraw, true);
  31735. if (dataSorting && dataSorting.enabled) {
  31736. data = this.sortData(data);
  31737. }
  31738. // First try to run Point.update which is cheaper, allows animation,
  31739. // and keeps references to points.
  31740. if (updatePoints !== false &&
  31741. dataLength &&
  31742. oldDataLength &&
  31743. !series.cropped &&
  31744. !series.hasGroupedData &&
  31745. series.visible &&
  31746. // Soft updating has no benefit in boost, and causes JS error
  31747. // (#8355)
  31748. !series.isSeriesBoosting) {
  31749. updatedData = this.updateData(data, animation);
  31750. }
  31751. if (!updatedData) {
  31752. // Reset properties
  31753. series.xIncrement = null;
  31754. series.colorCounter = 0; // for series with colorByPoint (#1547)
  31755. // Update parallel arrays
  31756. this.parallelArrays.forEach(function (key) {
  31757. series[key + 'Data'].length = 0;
  31758. });
  31759. // In turbo mode, only one- or twodimensional arrays of numbers
  31760. // are allowed. The first value is tested, and we assume that
  31761. // all the rest are defined the same way. Although the 'for'
  31762. // loops are similar, they are repeated inside each if-else
  31763. // conditional for max performance.
  31764. if (turboThreshold && dataLength > turboThreshold) {
  31765. firstPoint = series.getFirstValidPoint(data);
  31766. if (isNumber(firstPoint)) { // assume all points are numbers
  31767. for (i = 0; i < dataLength; i++) {
  31768. xData[i] = this.autoIncrement();
  31769. yData[i] = data[i];
  31770. }
  31771. // Assume all points are arrays when first point is
  31772. }
  31773. else if (isArray(firstPoint)) {
  31774. if (valueCount) { // [x, low, high] or [x, o, h, l, c]
  31775. for (i = 0; i < dataLength; i++) {
  31776. pt = data[i];
  31777. xData[i] = pt[0];
  31778. yData[i] =
  31779. pt.slice(1, valueCount + 1);
  31780. }
  31781. }
  31782. else { // [x, y]
  31783. if (keys) {
  31784. indexOfX = keys.indexOf('x');
  31785. indexOfY = keys.indexOf('y');
  31786. indexOfX = indexOfX >= 0 ? indexOfX : 0;
  31787. indexOfY = indexOfY >= 0 ? indexOfY : 1;
  31788. }
  31789. for (i = 0; i < dataLength; i++) {
  31790. pt = data[i];
  31791. xData[i] = pt[indexOfX];
  31792. yData[i] = pt[indexOfY];
  31793. }
  31794. }
  31795. }
  31796. else {
  31797. // Highcharts expects configs to be numbers or arrays in
  31798. // turbo mode
  31799. error(12, false, chart);
  31800. }
  31801. }
  31802. else {
  31803. for (i = 0; i < dataLength; i++) {
  31804. // stray commas in oldIE:
  31805. if (typeof data[i] !== 'undefined') {
  31806. pt = { series: series };
  31807. series.pointClass.prototype.applyOptions.apply(pt, [data[i]]);
  31808. series.updateParallelArrays(pt, i);
  31809. }
  31810. }
  31811. }
  31812. // Forgetting to cast strings to numbers is a common caveat when
  31813. // handling CSV or JSON
  31814. if (yData && isString(yData[0])) {
  31815. error(14, true, chart);
  31816. }
  31817. series.data = [];
  31818. series.options.data = series.userOptions.data = data;
  31819. // destroy old points
  31820. i = oldDataLength;
  31821. while (i--) {
  31822. if (oldData[i] && oldData[i].destroy) {
  31823. oldData[i].destroy();
  31824. }
  31825. }
  31826. // reset minRange (#878)
  31827. if (xAxis) {
  31828. xAxis.minRange = xAxis.userMinRange;
  31829. }
  31830. // redraw
  31831. series.isDirty = chart.isDirtyBox = true;
  31832. series.isDirtyData = !!oldData;
  31833. animation = false;
  31834. }
  31835. // Typically for pie series, points need to be processed and
  31836. // generated prior to rendering the legend
  31837. if (options.legendType === 'point') {
  31838. this.processData();
  31839. this.generatePoints();
  31840. }
  31841. if (redraw) {
  31842. chart.redraw(animation);
  31843. }
  31844. },
  31845. /**
  31846. * Internal function to sort series data
  31847. *
  31848. * @private
  31849. * @function Highcharts.Series#sortData
  31850. * @param {Array<Highcharts.PointOptionsType>} data
  31851. * Force data grouping.
  31852. * @return {Array<Highcharts.PointOptionsObject>}
  31853. */
  31854. sortData: function (data) {
  31855. var series = this, options = series.options, dataSorting = options.dataSorting, sortKey = dataSorting.sortKey || 'y', sortedData, getPointOptionsObject = function (series, pointOptions) {
  31856. return (defined(pointOptions) &&
  31857. series.pointClass.prototype.optionsToObject.call({
  31858. series: series
  31859. }, pointOptions)) || {};
  31860. };
  31861. data.forEach(function (pointOptions, i) {
  31862. data[i] = getPointOptionsObject(series, pointOptions);
  31863. data[i].index = i;
  31864. }, this);
  31865. // Sorting
  31866. sortedData = data.concat().sort(function (a, b) {
  31867. var aValue = getNestedProperty(sortKey, a);
  31868. var bValue = getNestedProperty(sortKey, b);
  31869. return bValue < aValue ? -1 : bValue > aValue ? 1 : 0;
  31870. });
  31871. // Set x value depending on the position in the array
  31872. sortedData.forEach(function (point, i) {
  31873. point.x = i;
  31874. }, this);
  31875. // Set the same x for linked series points if they don't have their
  31876. // own sorting
  31877. if (series.linkedSeries) {
  31878. series.linkedSeries.forEach(function (linkedSeries) {
  31879. var options = linkedSeries.options, seriesData = options.data;
  31880. if ((!options.dataSorting ||
  31881. !options.dataSorting.enabled) &&
  31882. seriesData) {
  31883. seriesData.forEach(function (pointOptions, i) {
  31884. seriesData[i] = getPointOptionsObject(linkedSeries, pointOptions);
  31885. if (data[i]) {
  31886. seriesData[i].x = data[i].x;
  31887. seriesData[i].index = i;
  31888. }
  31889. });
  31890. linkedSeries.setData(seriesData, false);
  31891. }
  31892. });
  31893. }
  31894. return data;
  31895. },
  31896. /**
  31897. * Internal function to process the data by cropping away unused data
  31898. * points if the series is longer than the crop threshold. This saves
  31899. * computing time for large series.
  31900. *
  31901. * @private
  31902. * @function Highcharts.Series#getProcessedData
  31903. * @param {boolean} [forceExtremesFromAll]
  31904. * Force getting extremes of a total series data range.
  31905. * @return {Highcharts.SeriesProcessedDataObject}
  31906. */
  31907. getProcessedData: function (forceExtremesFromAll) {
  31908. var series = this,
  31909. // copied during slice operation:
  31910. processedXData = series.xData, processedYData = series.yData, dataLength = processedXData.length, croppedData, cropStart = 0, cropped, distance, closestPointRange, xAxis = series.xAxis, i, // loop variable
  31911. options = series.options, cropThreshold = options.cropThreshold, getExtremesFromAll = forceExtremesFromAll ||
  31912. series.getExtremesFromAll ||
  31913. options.getExtremesFromAll, // #4599
  31914. isCartesian = series.isCartesian, xExtremes, val2lin = xAxis && xAxis.val2lin, isLog = !!(xAxis && xAxis.logarithmic), throwOnUnsorted = series.requireSorting, min, max;
  31915. if (xAxis) {
  31916. // corrected for log axis (#3053)
  31917. xExtremes = xAxis.getExtremes();
  31918. min = xExtremes.min;
  31919. max = xExtremes.max;
  31920. }
  31921. // optionally filter out points outside the plot area
  31922. if (isCartesian &&
  31923. series.sorted &&
  31924. !getExtremesFromAll &&
  31925. (!cropThreshold ||
  31926. dataLength > cropThreshold ||
  31927. series.forceCrop)) {
  31928. // it's outside current extremes
  31929. if (processedXData[dataLength - 1] < min ||
  31930. processedXData[0] > max) {
  31931. processedXData = [];
  31932. processedYData = [];
  31933. // only crop if it's actually spilling out
  31934. }
  31935. else if (series.yData && (processedXData[0] < min ||
  31936. processedXData[dataLength - 1] > max)) {
  31937. croppedData = this.cropData(series.xData, series.yData, min, max);
  31938. processedXData = croppedData.xData;
  31939. processedYData = croppedData.yData;
  31940. cropStart = croppedData.start;
  31941. cropped = true;
  31942. }
  31943. }
  31944. // Find the closest distance between processed points
  31945. i = processedXData.length || 1;
  31946. while (--i) {
  31947. distance = (isLog ?
  31948. (val2lin(processedXData[i]) -
  31949. val2lin(processedXData[i - 1])) :
  31950. (processedXData[i] -
  31951. processedXData[i - 1]));
  31952. if (distance > 0 &&
  31953. (typeof closestPointRange === 'undefined' ||
  31954. distance < closestPointRange)) {
  31955. closestPointRange = distance;
  31956. // Unsorted data is not supported by the line tooltip, as well
  31957. // as data grouping and navigation in Stock charts (#725) and
  31958. // width calculation of columns (#1900)
  31959. }
  31960. else if (distance < 0 && throwOnUnsorted) {
  31961. error(15, false, series.chart);
  31962. throwOnUnsorted = false; // Only once
  31963. }
  31964. }
  31965. return {
  31966. xData: processedXData,
  31967. yData: processedYData,
  31968. cropped: cropped,
  31969. cropStart: cropStart,
  31970. closestPointRange: closestPointRange
  31971. };
  31972. },
  31973. /**
  31974. * Internal function to apply processed data.
  31975. * In Highstock, this function is extended to provide data grouping.
  31976. *
  31977. * @private
  31978. * @function Highcharts.Series#processData
  31979. * @param {boolean} [force]
  31980. * Force data grouping.
  31981. * @return {boolean|undefined}
  31982. */
  31983. processData: function (force) {
  31984. var series = this, xAxis = series.xAxis, processedData;
  31985. // If the series data or axes haven't changed, don't go through
  31986. // this. Return false to pass the message on to override methods
  31987. // like in data grouping.
  31988. if (series.isCartesian &&
  31989. !series.isDirty &&
  31990. !xAxis.isDirty &&
  31991. !series.yAxis.isDirty &&
  31992. !force) {
  31993. return false;
  31994. }
  31995. processedData = series.getProcessedData();
  31996. // Record the properties
  31997. series.cropped = processedData.cropped; // undefined or true
  31998. series.cropStart = processedData.cropStart;
  31999. series.processedXData = processedData.xData;
  32000. series.processedYData = processedData.yData;
  32001. series.closestPointRange =
  32002. series.basePointRange = processedData.closestPointRange;
  32003. },
  32004. /**
  32005. * Iterate over xData and crop values between min and max. Returns
  32006. * object containing crop start/end cropped xData with corresponding
  32007. * part of yData, dataMin and dataMax within the cropped range.
  32008. *
  32009. * @private
  32010. * @function Highcharts.Series#cropData
  32011. * @param {Array<number>} xData
  32012. * @param {Array<number>} yData
  32013. * @param {number} min
  32014. * @param {number} max
  32015. * @param {number} [cropShoulder]
  32016. * @return {Highcharts.SeriesCropDataObject}
  32017. */
  32018. cropData: function (xData, yData, min, max, cropShoulder) {
  32019. var dataLength = xData.length, cropStart = 0, cropEnd = dataLength, i, j;
  32020. // line-type series need one point outside
  32021. cropShoulder = pick(cropShoulder, this.cropShoulder);
  32022. // iterate up to find slice start
  32023. for (i = 0; i < dataLength; i++) {
  32024. if (xData[i] >= min) {
  32025. cropStart = Math.max(0, i - cropShoulder);
  32026. break;
  32027. }
  32028. }
  32029. // proceed to find slice end
  32030. for (j = i; j < dataLength; j++) {
  32031. if (xData[j] > max) {
  32032. cropEnd = j + cropShoulder;
  32033. break;
  32034. }
  32035. }
  32036. return {
  32037. xData: xData.slice(cropStart, cropEnd),
  32038. yData: yData.slice(cropStart, cropEnd),
  32039. start: cropStart,
  32040. end: cropEnd
  32041. };
  32042. },
  32043. /**
  32044. * Generate the data point after the data has been processed by cropping
  32045. * away unused points and optionally grouped in Highcharts Stock.
  32046. *
  32047. * @private
  32048. * @function Highcharts.Series#generatePoints
  32049. */
  32050. generatePoints: function () {
  32051. var series = this, options = series.options, dataOptions = options.data, data = series.data, dataLength, processedXData = series.processedXData, processedYData = series.processedYData, PointClass = series.pointClass, processedDataLength = processedXData.length, cropStart = series.cropStart || 0, cursor, hasGroupedData = series.hasGroupedData, keys = options.keys, point, points = [], i;
  32052. if (!data && !hasGroupedData) {
  32053. var arr = [];
  32054. arr.length = dataOptions.length;
  32055. data = series.data = arr;
  32056. }
  32057. if (keys && hasGroupedData) {
  32058. // grouped data has already applied keys (#6590)
  32059. series.options.keys = false;
  32060. }
  32061. for (i = 0; i < processedDataLength; i++) {
  32062. cursor = cropStart + i;
  32063. if (!hasGroupedData) {
  32064. point = data[cursor];
  32065. // #970:
  32066. if (!point &&
  32067. typeof dataOptions[cursor] !== 'undefined') {
  32068. data[cursor] = point = (new PointClass()).init(series, dataOptions[cursor], processedXData[i]);
  32069. }
  32070. }
  32071. else {
  32072. // splat the y data in case of ohlc data array
  32073. point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
  32074. /**
  32075. * Highstock only. If a point object is created by data
  32076. * grouping, it doesn't reflect actual points in the raw
  32077. * data. In this case, the `dataGroup` property holds
  32078. * information that points back to the raw data.
  32079. *
  32080. * - `dataGroup.start` is the index of the first raw data
  32081. * point in the group.
  32082. *
  32083. * - `dataGroup.length` is the amount of points in the
  32084. * group.
  32085. *
  32086. * @product highstock
  32087. *
  32088. * @name Highcharts.Point#dataGroup
  32089. * @type {Highcharts.DataGroupingInfoObject|undefined}
  32090. */
  32091. point.dataGroup = series.groupMap[i];
  32092. if (point.dataGroup.options) {
  32093. point.options = point.dataGroup.options;
  32094. extend(point, point.dataGroup.options);
  32095. // Collision of props and options (#9770)
  32096. delete point.dataLabels;
  32097. }
  32098. }
  32099. if (point) { // #6279
  32100. /**
  32101. * Contains the point's index in the `Series.points` array.
  32102. *
  32103. * @name Highcharts.Point#index
  32104. * @type {number}
  32105. * @readonly
  32106. */
  32107. point.index = cursor; // For faster access in Point.update
  32108. points[i] = point;
  32109. }
  32110. }
  32111. // restore keys options (#6590)
  32112. series.options.keys = keys;
  32113. // Hide cropped-away points - this only runs when the number of
  32114. // points is above cropThreshold, or when swithching view from
  32115. // non-grouped data to grouped data (#637)
  32116. if (data &&
  32117. (processedDataLength !== (dataLength = data.length) ||
  32118. hasGroupedData)) {
  32119. for (i = 0; i < dataLength; i++) {
  32120. // when has grouped data, clear all points
  32121. if (i === cropStart && !hasGroupedData) {
  32122. i += processedDataLength;
  32123. }
  32124. if (data[i]) {
  32125. data[i].destroyElements();
  32126. data[i].plotX = void 0; // #1003
  32127. }
  32128. }
  32129. }
  32130. /**
  32131. * Read only. An array containing those values converted to points.
  32132. * In case the series data length exceeds the `cropThreshold`, or if
  32133. * the data is grouped, `series.data` doesn't contain all the
  32134. * points. Also, in case a series is hidden, the `data` array may be
  32135. * empty. To access raw values, `series.options.data` will always be
  32136. * up to date. `Series.data` only contains the points that have been
  32137. * created on demand. To modify the data, use
  32138. * {@link Highcharts.Series#setData} or
  32139. * {@link Highcharts.Point#update}.
  32140. *
  32141. * @see Series.points
  32142. *
  32143. * @name Highcharts.Series#data
  32144. * @type {Array<Highcharts.Point>}
  32145. */
  32146. series.data = data;
  32147. /**
  32148. * An array containing all currently visible point objects. In case
  32149. * of cropping, the cropped-away points are not part of this array.
  32150. * The `series.points` array starts at `series.cropStart` compared
  32151. * to `series.data` and `series.options.data`. If however the series
  32152. * data is grouped, these can't be correlated one to one. To modify
  32153. * the data, use {@link Highcharts.Series#setData} or
  32154. * {@link Highcharts.Point#update}.
  32155. *
  32156. * @name Highcharts.Series#points
  32157. * @type {Array<Highcharts.Point>}
  32158. */
  32159. series.points = points;
  32160. fireEvent(this, 'afterGeneratePoints');
  32161. },
  32162. /**
  32163. * Get current X extremes for the visible data.
  32164. *
  32165. * @private
  32166. * @function Highcharts.Series#getXExtremes
  32167. *
  32168. * @param {Array<number>} xData
  32169. * The data to inspect. Defaults to the current data within the
  32170. * visible range.
  32171. * @return {Highcharts.RangeObject}
  32172. */
  32173. getXExtremes: function (xData) {
  32174. return {
  32175. min: arrayMin(xData),
  32176. max: arrayMax(xData)
  32177. };
  32178. },
  32179. /**
  32180. * Calculate Y extremes for the visible data. The result is returned
  32181. * as an object with `dataMin` and `dataMax` properties.
  32182. *
  32183. * @private
  32184. * @function Highcharts.Series#getExtremes
  32185. * @param {Array<number>} [yData]
  32186. * The data to inspect. Defaults to the current data within the
  32187. * visible range.
  32188. * @param {boolean} [forceExtremesFromAll]
  32189. * Force getting extremes of a total series data range.
  32190. * @return {Highcharts.DataExtremesObject}
  32191. */
  32192. getExtremes: function (yData, forceExtremesFromAll) {
  32193. var xAxis = this.xAxis, yAxis = this.yAxis, xData = this.processedXData || this.xData, yDataLength, activeYData = [], activeCounter = 0,
  32194. // #2117, need to compensate for log X axis
  32195. xExtremes, xMin = 0, xMax = 0, validValue, withinRange,
  32196. // Handle X outside the viewed area. This does not work with
  32197. // non-sorted data like scatter (#7639).
  32198. shoulder = this.requireSorting ? this.cropShoulder : 0, positiveValuesOnly = yAxis ? yAxis.positiveValuesOnly : false, x, y, i, j;
  32199. yData = yData || this.stackedYData || this.processedYData || [];
  32200. yDataLength = yData.length;
  32201. if (xAxis) {
  32202. xExtremes = xAxis.getExtremes();
  32203. xMin = xExtremes.min;
  32204. xMax = xExtremes.max;
  32205. }
  32206. for (i = 0; i < yDataLength; i++) {
  32207. x = xData[i];
  32208. y = yData[i];
  32209. // For points within the visible range, including the first
  32210. // point outside the visible range (#7061), consider y extremes.
  32211. validValue = ((isNumber(y) || isArray(y)) &&
  32212. ((y.length || y > 0) || !positiveValuesOnly));
  32213. withinRange = (forceExtremesFromAll ||
  32214. this.getExtremesFromAll ||
  32215. this.options.getExtremesFromAll ||
  32216. this.cropped ||
  32217. !xAxis || // for colorAxis support
  32218. ((xData[i + shoulder] || x) >= xMin &&
  32219. (xData[i - shoulder] || x) <= xMax));
  32220. if (validValue && withinRange) {
  32221. j = y.length;
  32222. if (j) { // array, like ohlc or range data
  32223. while (j--) {
  32224. if (isNumber(y[j])) { // #7380, #11513
  32225. activeYData[activeCounter++] = y[j];
  32226. }
  32227. }
  32228. }
  32229. else {
  32230. activeYData[activeCounter++] = y;
  32231. }
  32232. }
  32233. }
  32234. var dataExtremes = {
  32235. dataMin: arrayMin(activeYData),
  32236. dataMax: arrayMax(activeYData)
  32237. };
  32238. fireEvent(this, 'afterGetExtremes', { dataExtremes: dataExtremes });
  32239. return dataExtremes;
  32240. },
  32241. /**
  32242. * Set the current data extremes as `dataMin` and `dataMax` on the
  32243. * Series item. Use this only when the series properties should be
  32244. * updated.
  32245. *
  32246. * @private
  32247. * @function Highcharts.Series#applyExtremes
  32248. * @return {void}
  32249. */
  32250. applyExtremes: function () {
  32251. var dataExtremes = this.getExtremes();
  32252. /**
  32253. * Contains the minimum value of the series' data point.
  32254. * @name Highcharts.Series#dataMin
  32255. * @type {number}
  32256. * @readonly
  32257. */
  32258. this.dataMin = dataExtremes.dataMin;
  32259. /* *
  32260. * Contains the maximum value of the series' data point.
  32261. * @name Highcharts.Series#dataMax
  32262. * @type {number}
  32263. * @readonly
  32264. */
  32265. this.dataMax = dataExtremes.dataMax;
  32266. return dataExtremes;
  32267. },
  32268. /**
  32269. * Find and return the first non null point in the data
  32270. *
  32271. * @private
  32272. * @function Highcharts.Series.getFirstValidPoint
  32273. * @param {Array<Highcharts.PointOptionsType>} data
  32274. * Array of options for points
  32275. *
  32276. * @return {Highcharts.PointOptionsType}
  32277. */
  32278. getFirstValidPoint: function (data) {
  32279. var firstPoint = null, dataLength = data.length, i = 0;
  32280. while (firstPoint === null && i < dataLength) {
  32281. firstPoint = data[i];
  32282. i++;
  32283. }
  32284. return firstPoint;
  32285. },
  32286. /**
  32287. * Translate data points from raw data values to chart specific
  32288. * positioning data needed later in the `drawPoints` and `drawGraph`
  32289. * functions. This function can be overridden in plugins and custom
  32290. * series type implementations.
  32291. *
  32292. * @function Highcharts.Series#translate
  32293. * @return {void}
  32294. * @fires Highcharts.Series#events:translate
  32295. */
  32296. translate: function () {
  32297. if (!this.processedXData) { // hidden series
  32298. this.processData();
  32299. }
  32300. this.generatePoints();
  32301. var 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, hasModifyValue = !!series.modifyValue, i, pointPlacement = series.pointPlacementToXValue(), // #7860
  32302. dynamicallyPlaced = Boolean(pointPlacement), threshold = options.threshold, stackThreshold = options.startFromThreshold ? threshold : 0, plotX, lastPlotX, stackIndicator, zoneAxis = this.zoneAxis || 'y', closestPointRangePx = Number.MAX_VALUE;
  32303. /**
  32304. * Plotted coordinates need to be within a limited range. Drawing
  32305. * too far outside the viewport causes various rendering issues
  32306. * (#3201, #3923, #7555).
  32307. * @private
  32308. */
  32309. function limitedRange(val) {
  32310. return clamp(val, -1e5, 1e5);
  32311. }
  32312. // Translate each point
  32313. for (i = 0; i < dataLength; i++) {
  32314. var point = points[i], xValue = point.x, yValue = point.y, yBottom = point.low, stack = stacking && yAxis.stacking && yAxis.stacking.stacks[(series.negStacks &&
  32315. yValue <
  32316. (stackThreshold ? 0 : threshold) ?
  32317. '-' :
  32318. '') + series.stackKey], pointStack, stackValues;
  32319. // Discard disallowed y values for log axes (#3434)
  32320. if (yAxis.positiveValuesOnly &&
  32321. yValue !== null &&
  32322. yValue <= 0) {
  32323. point.isNull = true;
  32324. }
  32325. // Get the plotX translation
  32326. point.plotX = plotX = correctFloat(// #5236
  32327. limitedRange(xAxis.translate(// #3923
  32328. xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags')) // #3923
  32329. );
  32330. // Calculate the bottom y value for stacked series
  32331. if (stacking &&
  32332. series.visible &&
  32333. stack &&
  32334. stack[xValue]) {
  32335. stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
  32336. if (!point.isNull) {
  32337. pointStack = stack[xValue];
  32338. stackValues =
  32339. pointStack.points[stackIndicator.key];
  32340. }
  32341. }
  32342. if (isArray(stackValues)) {
  32343. yBottom = stackValues[0];
  32344. yValue = stackValues[1];
  32345. if (yBottom === stackThreshold &&
  32346. stackIndicator.key ===
  32347. stack[xValue].base) {
  32348. yBottom = pick((isNumber(threshold) && threshold), yAxis.min);
  32349. }
  32350. // #1200, #1232
  32351. if (yAxis.positiveValuesOnly && yBottom <= 0) {
  32352. yBottom = null;
  32353. }
  32354. point.total = point.stackTotal = pointStack.total;
  32355. point.percentage =
  32356. pointStack.total &&
  32357. (point.y / pointStack.total * 100);
  32358. point.stackY = yValue;
  32359. // Place the stack label
  32360. // in case of variwide series (where widths of points are
  32361. // different in most cases), stack labels are positioned
  32362. // wrongly, so the call of the setOffset is omited here and
  32363. // labels are correctly positioned later, at the end of the
  32364. // variwide's translate function (#10962)
  32365. if (!series.irregularWidths) {
  32366. pointStack.setOffset(series.pointXOffset || 0, series.barW || 0);
  32367. }
  32368. }
  32369. // Set translated yBottom or remove it
  32370. point.yBottom = defined(yBottom) ?
  32371. limitedRange(yAxis.translate(yBottom, 0, 1, 0, 1)) :
  32372. null;
  32373. // general hook, used for Highstock compare mode
  32374. if (hasModifyValue) {
  32375. yValue = series.modifyValue(yValue, point);
  32376. }
  32377. // Set the the plotY value, reset it for redraws
  32378. // #3201
  32379. point.plotY = ((typeof yValue === 'number' && yValue !== Infinity) ?
  32380. limitedRange(yAxis.translate(yValue, 0, 1, 0, 1)) :
  32381. void 0);
  32382. point.isInside = this.isPointInside(point);
  32383. // Set client related positions for mouse tracking
  32384. point.clientX = dynamicallyPlaced ?
  32385. correctFloat(xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement)) :
  32386. plotX; // #1514, #5383, #5518
  32387. // Negative points. For bubble charts, this means negative z
  32388. // values (#9728)
  32389. point.negative = point[zoneAxis] < (options[zoneAxis + 'Threshold'] ||
  32390. threshold ||
  32391. 0);
  32392. // some API data
  32393. point.category = (categories &&
  32394. typeof categories[point.x] !== 'undefined' ?
  32395. categories[point.x] :
  32396. point.x);
  32397. // Determine auto enabling of markers (#3635, #5099)
  32398. if (!point.isNull && point.visible !== false) {
  32399. if (typeof lastPlotX !== 'undefined') {
  32400. closestPointRangePx = Math.min(closestPointRangePx, Math.abs(plotX - lastPlotX));
  32401. }
  32402. lastPlotX = plotX;
  32403. }
  32404. // Find point zone
  32405. point.zone = (this.zones.length && point.getZone());
  32406. // Animate new points with data sorting
  32407. if (!point.graphic && series.group && enabledDataSorting) {
  32408. point.isNew = true;
  32409. }
  32410. }
  32411. series.closestPointRangePx = closestPointRangePx;
  32412. fireEvent(this, 'afterTranslate');
  32413. },
  32414. /**
  32415. * Return the series points with null points filtered out.
  32416. *
  32417. * @function Highcharts.Series#getValidPoints
  32418. *
  32419. * @param {Array<Highcharts.Point>} [points]
  32420. * The points to inspect, defaults to {@link Series.points}.
  32421. *
  32422. * @param {boolean} [insideOnly=false]
  32423. * Whether to inspect only the points that are inside the visible
  32424. * view.
  32425. *
  32426. * @param {boolean} [allowNull=false]
  32427. * Whether to allow null points to pass as valid points.
  32428. *
  32429. * @return {Array<Highcharts.Point>}
  32430. * The valid points.
  32431. */
  32432. getValidPoints: function (points, insideOnly, allowNull) {
  32433. var chart = this.chart;
  32434. // #3916, #5029, #5085
  32435. return (points || this.points || []).filter(function isValidPoint(point) {
  32436. if (insideOnly && !chart.isInsidePlot(point.plotX, point.plotY, chart.inverted)) {
  32437. return false;
  32438. }
  32439. return point.visible !== false &&
  32440. (allowNull || !point.isNull);
  32441. });
  32442. },
  32443. /**
  32444. * Get the clipping for the series. Could be called for a series to
  32445. * initiate animating the clip or to set the final clip (only width
  32446. * and x).
  32447. *
  32448. * @private
  32449. * @function Highcharts.Series#getClip
  32450. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  32451. * Initialize the animation.
  32452. * @param {boolean} [finalBox]
  32453. * Final size for the clip - end state for the animation.
  32454. * @return {Highcharts.Dictionary<number>}
  32455. */
  32456. getClipBox: function (animation, finalBox) {
  32457. var series = this, options = series.options, chart = series.chart, inverted = chart.inverted, xAxis = series.xAxis, yAxis = xAxis && series.yAxis, clipBox, scrollablePlotAreaOptions = chart.options.chart.scrollablePlotArea || {};
  32458. if (animation && options.clip === false && yAxis) {
  32459. // support for not clipped series animation (#10450)
  32460. clipBox = inverted ? {
  32461. y: -chart.chartWidth + yAxis.len + yAxis.pos,
  32462. height: chart.chartWidth,
  32463. width: chart.chartHeight,
  32464. x: -chart.chartHeight + xAxis.len + xAxis.pos
  32465. } : {
  32466. y: -yAxis.pos,
  32467. height: chart.chartHeight,
  32468. width: chart.chartWidth,
  32469. x: -xAxis.pos
  32470. };
  32471. // x and width will be changed later when setting for animation
  32472. // initial state in Series.setClip
  32473. }
  32474. else {
  32475. clipBox = series.clipBox || chart.clipBox;
  32476. if (finalBox) {
  32477. clipBox.width = chart.plotSizeX;
  32478. clipBox.x = (chart.scrollablePixelsX || 0) *
  32479. (scrollablePlotAreaOptions.scrollPositionX || 0);
  32480. }
  32481. }
  32482. return !finalBox ? clipBox : {
  32483. width: clipBox.width,
  32484. x: clipBox.x
  32485. };
  32486. },
  32487. /**
  32488. * Set the clipping for the series. For animated series it is called
  32489. * twice, first to initiate animating the clip then the second time
  32490. * without the animation to set the final clip.
  32491. *
  32492. * @private
  32493. * @function Highcharts.Series#setClip
  32494. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  32495. */
  32496. setClip: function (animation) {
  32497. var chart = this.chart, options = this.options, renderer = chart.renderer, inverted = chart.inverted, seriesClipBox = this.clipBox, clipBox = this.getClipBox(animation), sharedClipKey = this.sharedClipKey ||
  32498. [
  32499. '_sharedClip',
  32500. animation && animation.duration,
  32501. animation && animation.easing,
  32502. clipBox.height,
  32503. options.xAxis,
  32504. options.yAxis
  32505. ].join(','), // #4526
  32506. clipRect = chart[sharedClipKey], markerClipRect = chart[sharedClipKey + 'm'];
  32507. if (animation) {
  32508. clipBox.width = 0;
  32509. if (inverted) {
  32510. clipBox.x = chart.plotHeight +
  32511. (options.clip !== false ? 0 : chart.plotTop);
  32512. }
  32513. }
  32514. // If a clipping rectangle with the same properties is currently
  32515. // present in the chart, use that.
  32516. if (!clipRect) {
  32517. // When animation is set, prepare the initial positions
  32518. if (animation) {
  32519. chart[sharedClipKey + 'm'] = markerClipRect =
  32520. renderer.clipRect(
  32521. // include the width of the first marker
  32522. inverted ? chart.plotSizeX + 99 : -99, inverted ? -chart.plotLeft : -chart.plotTop, 99, inverted ? chart.chartWidth : chart.chartHeight);
  32523. }
  32524. chart[sharedClipKey] = clipRect = renderer.clipRect(clipBox);
  32525. // Create hashmap for series indexes
  32526. clipRect.count = { length: 0 };
  32527. // When the series is rendered again before starting animating, in
  32528. // compliance to a responsive rule
  32529. }
  32530. else if (!chart.hasLoaded) {
  32531. clipRect.attr(clipBox);
  32532. }
  32533. if (animation) {
  32534. if (!clipRect.count[this.index]) {
  32535. clipRect.count[this.index] = true;
  32536. clipRect.count.length += 1;
  32537. }
  32538. }
  32539. if (options.clip !== false || animation) {
  32540. this.group.clip(animation || seriesClipBox ? clipRect : chart.clipRect);
  32541. this.markerGroup.clip(markerClipRect);
  32542. this.sharedClipKey = sharedClipKey;
  32543. }
  32544. // Remove the shared clipping rectangle when all series are shown
  32545. if (!animation) {
  32546. if (clipRect.count[this.index]) {
  32547. delete clipRect.count[this.index];
  32548. clipRect.count.length -= 1;
  32549. }
  32550. if (clipRect.count.length === 0 &&
  32551. sharedClipKey &&
  32552. chart[sharedClipKey]) {
  32553. if (!seriesClipBox) {
  32554. chart[sharedClipKey] =
  32555. chart[sharedClipKey].destroy();
  32556. }
  32557. if (chart[sharedClipKey + 'm']) {
  32558. chart[sharedClipKey + 'm'] =
  32559. chart[sharedClipKey + 'm'].destroy();
  32560. }
  32561. }
  32562. }
  32563. },
  32564. /**
  32565. * Animate in the series. Called internally twice. First with the `init`
  32566. * parameter set to true, which sets up the initial state of the
  32567. * animation. Then when ready, it is called with the `init` parameter
  32568. * undefined, in order to perform the actual animation. After the
  32569. * second run, the function is removed.
  32570. *
  32571. * @function Highcharts.Series#animate
  32572. *
  32573. * @param {boolean} [init]
  32574. * Initialize the animation.
  32575. */
  32576. animate: function (init) {
  32577. var series = this, chart = series.chart, animation = animObject(series.options.animation), clipRect, sharedClipKey, finalBox;
  32578. // Initialize the animation. Set up the clipping rectangle.
  32579. if (!chart.hasRendered) {
  32580. if (init) {
  32581. series.setClip(animation);
  32582. // Run the animation
  32583. }
  32584. else {
  32585. sharedClipKey = this.sharedClipKey;
  32586. clipRect = chart[sharedClipKey];
  32587. finalBox = series.getClipBox(animation, true);
  32588. if (clipRect) {
  32589. clipRect.animate(finalBox, animation);
  32590. }
  32591. if (chart[sharedClipKey + 'm']) {
  32592. chart[sharedClipKey + 'm'].animate({
  32593. width: finalBox.width + 99,
  32594. x: finalBox.x - (chart.inverted ? 0 : 99)
  32595. }, animation);
  32596. }
  32597. }
  32598. }
  32599. },
  32600. /**
  32601. * This runs after animation to land on the final plot clipping.
  32602. *
  32603. * @private
  32604. * @function Highcharts.Series#afterAnimate
  32605. * @fires Highcharts.Series#event:afterAnimate
  32606. */
  32607. afterAnimate: function () {
  32608. this.setClip();
  32609. fireEvent(this, 'afterAnimate');
  32610. this.finishedAnimating = true;
  32611. },
  32612. /**
  32613. * Draw the markers for line-like series types, and columns or other
  32614. * graphical representation for {@link Point} objects for other series
  32615. * types. The resulting element is typically stored as
  32616. * {@link Point.graphic}, and is created on the first call and updated
  32617. * and moved on subsequent calls.
  32618. *
  32619. * @function Highcharts.Series#drawPoints
  32620. */
  32621. drawPoints: function () {
  32622. var series = this, points = series.points, chart = series.chart, i, point, graphic, verb, options = series.options, seriesMarkerOptions = options.marker, pointMarkerOptions, hasPointMarker, markerGroup = (series[series.specialGroup] ||
  32623. series.markerGroup), xAxis = series.xAxis, markerAttribs, globallyEnabled = pick(seriesMarkerOptions.enabled, !xAxis || xAxis.isRadial ? true : null,
  32624. // Use larger or equal as radius is null in bubbles (#6321)
  32625. series.closestPointRangePx >= (seriesMarkerOptions.enabledThreshold *
  32626. seriesMarkerOptions.radius));
  32627. if (seriesMarkerOptions.enabled !== false ||
  32628. series._hasPointMarkers) {
  32629. for (i = 0; i < points.length; i++) {
  32630. point = points[i];
  32631. graphic = point.graphic;
  32632. verb = graphic ? 'animate' : 'attr';
  32633. pointMarkerOptions = point.marker || {};
  32634. hasPointMarker = !!point.marker;
  32635. var shouldDrawMarker = ((globallyEnabled &&
  32636. typeof pointMarkerOptions.enabled === 'undefined') || pointMarkerOptions.enabled) && !point.isNull && point.visible !== false;
  32637. // only draw the point if y is defined
  32638. if (shouldDrawMarker) {
  32639. // Shortcuts
  32640. var symbol = pick(pointMarkerOptions.symbol, series.symbol);
  32641. markerAttribs = series.markerAttribs(point, (point.selected && 'select'));
  32642. // Set starting position for point sliding animation.
  32643. if (series.enabledDataSorting) {
  32644. point.startXPos = xAxis.reversed ?
  32645. -markerAttribs.width :
  32646. xAxis.width;
  32647. }
  32648. var isInside = point.isInside !== false;
  32649. if (graphic) { // update
  32650. // Since the marker group isn't clipped, each
  32651. // individual marker must be toggled
  32652. graphic[isInside ? 'show' : 'hide'](isInside)
  32653. .animate(markerAttribs);
  32654. }
  32655. else if (isInside &&
  32656. (markerAttribs.width > 0 || point.hasImage)) {
  32657. /**
  32658. * The graphic representation of the point.
  32659. * Typically this is a simple shape, like a `rect`
  32660. * for column charts or `path` for line markers, but
  32661. * for some complex series types like boxplot or 3D
  32662. * charts, the graphic may be a `g` element
  32663. * containing other shapes. The graphic is generated
  32664. * the first time {@link Series#drawPoints} runs,
  32665. * and updated and moved on subsequent runs.
  32666. *
  32667. * @name Point#graphic
  32668. * @type {SVGElement}
  32669. */
  32670. point.graphic = graphic = chart.renderer
  32671. .symbol(symbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height, hasPointMarker ?
  32672. pointMarkerOptions :
  32673. seriesMarkerOptions)
  32674. .add(markerGroup);
  32675. // Sliding animation for new points
  32676. if (series.enabledDataSorting &&
  32677. chart.hasRendered) {
  32678. graphic.attr({
  32679. x: point.startXPos
  32680. });
  32681. verb = 'animate';
  32682. }
  32683. }
  32684. if (graphic && verb === 'animate') { // update
  32685. // Since the marker group isn't clipped, each
  32686. // individual marker must be toggled
  32687. graphic[isInside ? 'show' : 'hide'](isInside)
  32688. .animate(markerAttribs);
  32689. }
  32690. // Presentational attributes
  32691. if (graphic && !chart.styledMode) {
  32692. graphic[verb](series.pointAttribs(point, (point.selected && 'select')));
  32693. }
  32694. if (graphic) {
  32695. graphic.addClass(point.getClassName(), true);
  32696. }
  32697. }
  32698. else if (graphic) {
  32699. point.graphic = graphic.destroy(); // #1269
  32700. }
  32701. }
  32702. }
  32703. },
  32704. /**
  32705. * Get non-presentational attributes for a point. Used internally for
  32706. * both styled mode and classic. Can be overridden for different series
  32707. * types.
  32708. *
  32709. * @see Series#pointAttribs
  32710. *
  32711. * @function Highcharts.Series#markerAttribs
  32712. *
  32713. * @param {Highcharts.Point} point
  32714. * The Point to inspect.
  32715. *
  32716. * @param {string} [state]
  32717. * The state, can be either `hover`, `select` or undefined.
  32718. *
  32719. * @return {Highcharts.SVGAttributes}
  32720. * A hash containing those attributes that are not settable from
  32721. * CSS.
  32722. */
  32723. markerAttribs: function (point, state) {
  32724. var seriesOptions = this.options, seriesMarkerOptions = seriesOptions.marker, seriesStateOptions, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
  32725. seriesMarkerOptions.symbol), pointStateOptions, radius = pick(pointMarkerOptions.radius, seriesMarkerOptions.radius), attribs;
  32726. // Handle hover and select states
  32727. if (state) {
  32728. seriesStateOptions = seriesMarkerOptions.states[state];
  32729. pointStateOptions = pointMarkerOptions.states &&
  32730. pointMarkerOptions.states[state];
  32731. radius = pick(pointStateOptions && pointStateOptions.radius, seriesStateOptions && seriesStateOptions.radius, radius + (seriesStateOptions && seriesStateOptions.radiusPlus ||
  32732. 0));
  32733. }
  32734. point.hasImage = symbol && symbol.indexOf('url') === 0;
  32735. if (point.hasImage) {
  32736. radius = 0; // and subsequently width and height is not set
  32737. }
  32738. attribs = {
  32739. // Math.floor for #1843:
  32740. x: seriesOptions.crisp ?
  32741. Math.floor(point.plotX) - radius :
  32742. point.plotX - radius,
  32743. y: point.plotY - radius
  32744. };
  32745. if (radius) {
  32746. attribs.width = attribs.height = 2 * radius;
  32747. }
  32748. return attribs;
  32749. },
  32750. /**
  32751. * Internal function to get presentational attributes for each point.
  32752. * Unlike {@link Series#markerAttribs}, this function should return
  32753. * those attributes that can also be set in CSS. In styled mode,
  32754. * `pointAttribs` won't be called.
  32755. *
  32756. * @private
  32757. * @function Highcharts.Series#pointAttribs
  32758. *
  32759. * @param {Highcharts.Point} [point]
  32760. * The point instance to inspect.
  32761. *
  32762. * @param {string} [state]
  32763. * The point state, can be either `hover`, `select` or 'normal'.
  32764. * If undefined, normal state is assumed.
  32765. *
  32766. * @return {Highcharts.SVGAttributes}
  32767. * The presentational attributes to be set on the point.
  32768. */
  32769. pointAttribs: function (point, state) {
  32770. var seriesMarkerOptions = this.options.marker, seriesStateOptions, pointOptions = point && point.options, pointMarkerOptions = ((pointOptions && pointOptions.marker) || {}), pointStateOptions, color = this.color, pointColorOption = pointOptions && pointOptions.color, pointColor = point && point.color, strokeWidth = pick(pointMarkerOptions.lineWidth, seriesMarkerOptions.lineWidth), zoneColor = point && point.zone && point.zone.color, fill, stroke, opacity = 1;
  32771. color = (pointColorOption ||
  32772. zoneColor ||
  32773. pointColor ||
  32774. color);
  32775. fill = (pointMarkerOptions.fillColor ||
  32776. seriesMarkerOptions.fillColor ||
  32777. color);
  32778. stroke = (pointMarkerOptions.lineColor ||
  32779. seriesMarkerOptions.lineColor ||
  32780. color);
  32781. // Handle hover and select states
  32782. state = state || 'normal';
  32783. if (state) {
  32784. seriesStateOptions = seriesMarkerOptions.states[state];
  32785. pointStateOptions = (pointMarkerOptions.states &&
  32786. pointMarkerOptions.states[state]) || {};
  32787. strokeWidth = pick(pointStateOptions.lineWidth, seriesStateOptions.lineWidth, strokeWidth + pick(pointStateOptions.lineWidthPlus, seriesStateOptions.lineWidthPlus, 0));
  32788. fill = (pointStateOptions.fillColor ||
  32789. seriesStateOptions.fillColor ||
  32790. fill);
  32791. stroke = (pointStateOptions.lineColor ||
  32792. seriesStateOptions.lineColor ||
  32793. stroke);
  32794. opacity = pick(pointStateOptions.opacity, seriesStateOptions.opacity, opacity);
  32795. }
  32796. return {
  32797. 'stroke': stroke,
  32798. 'stroke-width': strokeWidth,
  32799. 'fill': fill,
  32800. 'opacity': opacity
  32801. };
  32802. },
  32803. /**
  32804. * Clear DOM objects and free up memory.
  32805. *
  32806. * @private
  32807. * @function Highcharts.Series#destroy
  32808. * @param {boolean} [keepEventsForUpdate]
  32809. * @return {void}
  32810. * @fires Highcharts.Series#event:destroy
  32811. */
  32812. destroy: function (keepEventsForUpdate) {
  32813. var series = this, chart = series.chart, issue134 = /AppleWebKit\/533/.test(win.navigator.userAgent), destroy, i, data = series.data || [], point, axis;
  32814. // add event hook
  32815. fireEvent(series, 'destroy');
  32816. // remove events
  32817. this.removeEvents(keepEventsForUpdate);
  32818. // erase from axes
  32819. (series.axisTypes || []).forEach(function (AXIS) {
  32820. axis = series[AXIS];
  32821. if (axis && axis.series) {
  32822. erase(axis.series, series);
  32823. axis.isDirty = axis.forceRedraw = true;
  32824. }
  32825. });
  32826. // remove legend items
  32827. if (series.legendItem) {
  32828. series.chart.legend.destroyItem(series);
  32829. }
  32830. // destroy all points with their elements
  32831. i = data.length;
  32832. while (i--) {
  32833. point = data[i];
  32834. if (point && point.destroy) {
  32835. point.destroy();
  32836. }
  32837. }
  32838. series.points = null;
  32839. // Clear the animation timeout if we are destroying the series
  32840. // during initial animation
  32841. U.clearTimeout(series.animationTimeout);
  32842. // Destroy all SVGElements associated to the series
  32843. objectEach(series, function (val, prop) {
  32844. // Survive provides a hook for not destroying
  32845. if (val instanceof SVGElement && !val.survive) {
  32846. // issue 134 workaround
  32847. destroy = issue134 && prop === 'group' ?
  32848. 'hide' :
  32849. 'destroy';
  32850. val[destroy]();
  32851. }
  32852. });
  32853. // remove from hoverSeries
  32854. if (chart.hoverSeries === series) {
  32855. chart.hoverSeries = null;
  32856. }
  32857. erase(chart.series, series);
  32858. chart.orderSeries();
  32859. // clear all members
  32860. objectEach(series, function (val, prop) {
  32861. if (!keepEventsForUpdate || prop !== 'hcEvents') {
  32862. delete series[prop];
  32863. }
  32864. });
  32865. },
  32866. /**
  32867. * Get the graph path.
  32868. *
  32869. * @private
  32870. * @function Highcharts.Series#getGraphPath
  32871. * @param {Array<Highcharts.Point>} points
  32872. * @param {boolean} [nullsAsZeroes]
  32873. * @param {boolean} [connectCliffs]
  32874. * @return {Highcharts.SVGPathArray}
  32875. */
  32876. getGraphPath: function (points, nullsAsZeroes, connectCliffs) {
  32877. var series = this, options = series.options, step = options.step, reversed, graphPath = [], xMap = [], gap;
  32878. points = points || series.points;
  32879. // Bottom of a stack is reversed
  32880. reversed = points.reversed;
  32881. if (reversed) {
  32882. points.reverse();
  32883. }
  32884. // Reverse the steps (#5004)
  32885. step = {
  32886. right: 1,
  32887. center: 2
  32888. }[step] || (step && 3);
  32889. if (step && reversed) {
  32890. step = 4 - step;
  32891. }
  32892. // Remove invalid points, especially in spline (#5015)
  32893. points = this.getValidPoints(points, false, !(options.connectNulls && !nullsAsZeroes && !connectCliffs));
  32894. // Build the line
  32895. points.forEach(function (point, i) {
  32896. var plotX = point.plotX, plotY = point.plotY, lastPoint = points[i - 1],
  32897. // the path to this point from the previous
  32898. pathToPoint;
  32899. if ((point.leftCliff || (lastPoint && lastPoint.rightCliff)) &&
  32900. !connectCliffs) {
  32901. gap = true; // ... and continue
  32902. }
  32903. // Line series, nullsAsZeroes is not handled
  32904. if (point.isNull && !defined(nullsAsZeroes) && i > 0) {
  32905. gap = !options.connectNulls;
  32906. // Area series, nullsAsZeroes is set
  32907. }
  32908. else if (point.isNull && !nullsAsZeroes) {
  32909. gap = true;
  32910. }
  32911. else {
  32912. if (i === 0 || gap) {
  32913. pathToPoint = [[
  32914. 'M',
  32915. point.plotX,
  32916. point.plotY
  32917. ]];
  32918. // Generate the spline as defined in the SplineSeries object
  32919. }
  32920. else if (series.getPointSpline) {
  32921. pathToPoint = [series.getPointSpline(points, point, i)];
  32922. }
  32923. else if (step) {
  32924. if (step === 1) { // right
  32925. pathToPoint = [[
  32926. 'L',
  32927. lastPoint.plotX,
  32928. plotY
  32929. ]];
  32930. }
  32931. else if (step === 2) { // center
  32932. pathToPoint = [[
  32933. 'L',
  32934. (lastPoint.plotX + plotX) / 2,
  32935. lastPoint.plotY
  32936. ], [
  32937. 'L',
  32938. (lastPoint.plotX + plotX) / 2,
  32939. plotY
  32940. ]];
  32941. }
  32942. else {
  32943. pathToPoint = [[
  32944. 'L',
  32945. plotX,
  32946. lastPoint.plotY
  32947. ]];
  32948. }
  32949. pathToPoint.push([
  32950. 'L',
  32951. plotX,
  32952. plotY
  32953. ]);
  32954. }
  32955. else {
  32956. // normal line to next point
  32957. pathToPoint = [[
  32958. 'L',
  32959. plotX,
  32960. plotY
  32961. ]];
  32962. }
  32963. // Prepare for animation. When step is enabled, there are
  32964. // two path nodes for each x value.
  32965. xMap.push(point.x);
  32966. if (step) {
  32967. xMap.push(point.x);
  32968. if (step === 2) { // step = center (#8073)
  32969. xMap.push(point.x);
  32970. }
  32971. }
  32972. graphPath.push.apply(graphPath, pathToPoint);
  32973. gap = false;
  32974. }
  32975. });
  32976. graphPath.xMap = xMap;
  32977. series.graphPath = graphPath;
  32978. return graphPath;
  32979. },
  32980. /**
  32981. * Draw the graph. Called internally when rendering line-like series
  32982. * types. The first time it generates the `series.graph` item and
  32983. * optionally other series-wide items like `series.area` for area
  32984. * charts. On subsequent calls these items are updated with new
  32985. * positions and attributes.
  32986. *
  32987. * @function Highcharts.Series#drawGraph
  32988. */
  32989. drawGraph: function () {
  32990. var series = this, options = this.options, graphPath = (this.gappedPath || this.getGraphPath).call(this), styledMode = this.chart.styledMode, props = [[
  32991. 'graph',
  32992. 'highcharts-graph'
  32993. ]];
  32994. // Presentational properties
  32995. if (!styledMode) {
  32996. props[0].push((options.lineColor ||
  32997. this.color ||
  32998. '#cccccc' // when colorByPoint = true
  32999. ), options.dashStyle);
  33000. }
  33001. props = series.getZonesGraphs(props);
  33002. // Draw the graph
  33003. props.forEach(function (prop, i) {
  33004. var graphKey = prop[0], graph = series[graphKey], verb = graph ? 'animate' : 'attr', attribs;
  33005. if (graph) {
  33006. graph.endX = series.preventGraphAnimation ?
  33007. null :
  33008. graphPath.xMap;
  33009. graph.animate({ d: graphPath });
  33010. }
  33011. else if (graphPath.length) { // #1487
  33012. /**
  33013. * SVG element of area-based charts. Can be used for styling
  33014. * purposes. If zones are configured, this element will be
  33015. * hidden and replaced by multiple zone areas, accessible
  33016. * via `series['zone-area-x']` (where x is a number,
  33017. * starting with 0).
  33018. *
  33019. * @name Highcharts.Series#area
  33020. * @type {Highcharts.SVGElement|undefined}
  33021. */
  33022. /**
  33023. * SVG element of line-based charts. Can be used for styling
  33024. * purposes. If zones are configured, this element will be
  33025. * hidden and replaced by multiple zone lines, accessible
  33026. * via `series['zone-graph-x']` (where x is a number,
  33027. * starting with 0).
  33028. *
  33029. * @name Highcharts.Series#graph
  33030. * @type {Highcharts.SVGElement|undefined}
  33031. */
  33032. series[graphKey] = graph = series.chart.renderer
  33033. .path(graphPath)
  33034. .addClass(prop[1])
  33035. .attr({ zIndex: 1 }) // #1069
  33036. .add(series.group);
  33037. }
  33038. if (graph && !styledMode) {
  33039. attribs = {
  33040. 'stroke': prop[2],
  33041. 'stroke-width': options.lineWidth,
  33042. // Polygon series use filled graph
  33043. 'fill': (series.fillGraph && series.color) || 'none'
  33044. };
  33045. if (prop[3]) {
  33046. attribs.dashstyle = prop[3];
  33047. }
  33048. else if (options.linecap !== 'square') {
  33049. attribs['stroke-linecap'] =
  33050. attribs['stroke-linejoin'] = 'round';
  33051. }
  33052. graph[verb](attribs)
  33053. // Add shadow to normal series (0) or to first
  33054. // zone (1) #3932
  33055. .shadow((i < 2) && options.shadow);
  33056. }
  33057. // Helpers for animation
  33058. if (graph) {
  33059. graph.startX = graphPath.xMap;
  33060. graph.isArea = graphPath.isArea; // For arearange animation
  33061. }
  33062. });
  33063. },
  33064. /**
  33065. * Get zones properties for building graphs. Extendable by series with
  33066. * multiple lines within one series.
  33067. *
  33068. * @private
  33069. * @function Highcharts.Series#getZonesGraphs
  33070. *
  33071. * @param {Array<Array<string>>} props
  33072. *
  33073. * @return {Array<Array<string>>}
  33074. */
  33075. getZonesGraphs: function (props) {
  33076. // Add the zone properties if any
  33077. this.zones.forEach(function (zone, i) {
  33078. var propset = [
  33079. 'zone-graph-' + i,
  33080. 'highcharts-graph highcharts-zone-graph-' + i + ' ' +
  33081. (zone.className || '')
  33082. ];
  33083. if (!this.chart.styledMode) {
  33084. propset.push((zone.color || this.color), (zone.dashStyle || this.options.dashStyle));
  33085. }
  33086. props.push(propset);
  33087. }, this);
  33088. return props;
  33089. },
  33090. /**
  33091. * Clip the graphs into zones for colors and styling.
  33092. *
  33093. * @private
  33094. * @function Highcharts.Series#applyZones
  33095. * @return {void}
  33096. */
  33097. applyZones: function () {
  33098. var series = this, chart = this.chart, renderer = chart.renderer, zones = this.zones, translatedFrom, translatedTo, clips = (this.clips || []), clipAttr, graph = this.graph, area = this.area, chartSizeMax = Math.max(chart.chartWidth, chart.chartHeight), axis = this[(this.zoneAxis || 'y') + 'Axis'], extremes, reversed, inverted = chart.inverted, horiz, pxRange, pxPosMin, pxPosMax, ignoreZones = false, zoneArea, zoneGraph;
  33099. if (zones.length &&
  33100. (graph || area) &&
  33101. axis &&
  33102. typeof axis.min !== 'undefined') {
  33103. reversed = axis.reversed;
  33104. horiz = axis.horiz;
  33105. // The use of the Color Threshold assumes there are no gaps
  33106. // so it is safe to hide the original graph and area
  33107. // unless it is not waterfall series, then use showLine property
  33108. // to set lines between columns to be visible (#7862)
  33109. if (graph && !this.showLine) {
  33110. graph.hide();
  33111. }
  33112. if (area) {
  33113. area.hide();
  33114. }
  33115. // Create the clips
  33116. extremes = axis.getExtremes();
  33117. zones.forEach(function (threshold, i) {
  33118. translatedFrom = reversed ?
  33119. (horiz ? chart.plotWidth : 0) :
  33120. (horiz ? 0 : (axis.toPixels(extremes.min) || 0));
  33121. translatedFrom = clamp(pick(translatedTo, translatedFrom), 0, chartSizeMax);
  33122. translatedTo = clamp(Math.round(axis.toPixels(pick(threshold.value, extremes.max), true) || 0), 0, chartSizeMax);
  33123. if (ignoreZones) {
  33124. translatedFrom = translatedTo =
  33125. axis.toPixels(extremes.max);
  33126. }
  33127. pxRange = Math.abs(translatedFrom - translatedTo);
  33128. pxPosMin = Math.min(translatedFrom, translatedTo);
  33129. pxPosMax = Math.max(translatedFrom, translatedTo);
  33130. if (axis.isXAxis) {
  33131. clipAttr = {
  33132. x: inverted ? pxPosMax : pxPosMin,
  33133. y: 0,
  33134. width: pxRange,
  33135. height: chartSizeMax
  33136. };
  33137. if (!horiz) {
  33138. clipAttr.x = chart.plotHeight - clipAttr.x;
  33139. }
  33140. }
  33141. else {
  33142. clipAttr = {
  33143. x: 0,
  33144. y: inverted ? pxPosMax : pxPosMin,
  33145. width: chartSizeMax,
  33146. height: pxRange
  33147. };
  33148. if (horiz) {
  33149. clipAttr.y = chart.plotWidth - clipAttr.y;
  33150. }
  33151. }
  33152. // VML SUPPPORT
  33153. if (inverted && renderer.isVML) {
  33154. if (axis.isXAxis) {
  33155. clipAttr = {
  33156. x: 0,
  33157. y: reversed ? pxPosMin : pxPosMax,
  33158. height: clipAttr.width,
  33159. width: chart.chartWidth
  33160. };
  33161. }
  33162. else {
  33163. clipAttr = {
  33164. x: (clipAttr.y -
  33165. chart.plotLeft -
  33166. chart.spacingBox.x),
  33167. y: 0,
  33168. width: clipAttr.height,
  33169. height: chart.chartHeight
  33170. };
  33171. }
  33172. }
  33173. // END OF VML SUPPORT
  33174. if (clips[i]) {
  33175. clips[i].animate(clipAttr);
  33176. }
  33177. else {
  33178. clips[i] = renderer.clipRect(clipAttr);
  33179. }
  33180. // when no data, graph zone is not applied and after setData
  33181. // clip was ignored. As a result, it should be applied each
  33182. // time.
  33183. zoneArea = series['zone-area-' + i];
  33184. zoneGraph = series['zone-graph-' + i];
  33185. if (graph && zoneGraph) {
  33186. zoneGraph.clip(clips[i]);
  33187. }
  33188. if (area && zoneArea) {
  33189. zoneArea.clip(clips[i]);
  33190. }
  33191. // if this zone extends out of the axis, ignore the others
  33192. ignoreZones = threshold.value > extremes.max;
  33193. // Clear translatedTo for indicators
  33194. if (series.resetZones && translatedTo === 0) {
  33195. translatedTo = void 0;
  33196. }
  33197. });
  33198. this.clips = clips;
  33199. }
  33200. else if (series.visible) {
  33201. // If zones were removed, restore graph and area
  33202. if (graph) {
  33203. graph.show(true);
  33204. }
  33205. if (area) {
  33206. area.show(true);
  33207. }
  33208. }
  33209. },
  33210. /**
  33211. * Initialize and perform group inversion on series.group and
  33212. * series.markerGroup.
  33213. *
  33214. * @private
  33215. * @function Highcharts.Series#invertGroups
  33216. * @param {boolean} [inverted]
  33217. * @return {void}
  33218. */
  33219. invertGroups: function (inverted) {
  33220. var series = this, chart = series.chart;
  33221. /**
  33222. * @private
  33223. */
  33224. function setInvert() {
  33225. ['group', 'markerGroup'].forEach(function (groupName) {
  33226. if (series[groupName]) {
  33227. // VML/HTML needs explicit attributes for flipping
  33228. if (chart.renderer.isVML) {
  33229. series[groupName].attr({
  33230. width: series.yAxis.len,
  33231. height: series.xAxis.len
  33232. });
  33233. }
  33234. series[groupName].width = series.yAxis.len;
  33235. series[groupName].height = series.xAxis.len;
  33236. // If inverted polar, don't invert series group
  33237. series[groupName].invert(series.isRadialSeries ? false : inverted);
  33238. }
  33239. });
  33240. }
  33241. // Pie, go away (#1736)
  33242. if (!series.xAxis) {
  33243. return;
  33244. }
  33245. // A fixed size is needed for inversion to work
  33246. series.eventsToUnbind.push(addEvent(chart, 'resize', setInvert));
  33247. // Do it now
  33248. setInvert();
  33249. // On subsequent render and redraw, just do setInvert without
  33250. // setting up events again
  33251. series.invertGroups = setInvert;
  33252. },
  33253. /**
  33254. * General abstraction for creating plot groups like series.group,
  33255. * series.dataLabelsGroup and series.markerGroup. On subsequent calls,
  33256. * the group will only be adjusted to the updated plot size.
  33257. *
  33258. * @private
  33259. * @function Highcharts.Series#plotGroup
  33260. * @param {string} prop
  33261. * @param {string} name
  33262. * @param {string} visibility
  33263. * @param {number} [zIndex]
  33264. * @param {Highcharts.SVGElement} [parent]
  33265. * @return {Highcharts.SVGElement}
  33266. */
  33267. plotGroup: function (prop, name, visibility, zIndex, parent) {
  33268. var group = this[prop], isNew = !group, attrs = {
  33269. visibility: visibility,
  33270. zIndex: zIndex || 0.1 // IE8 and pointer logic use this
  33271. };
  33272. // Avoid setting undefined opacity, or in styled mode
  33273. if (typeof this.opacity !== 'undefined' &&
  33274. !this.chart.styledMode) {
  33275. attrs.opacity = this.opacity;
  33276. }
  33277. // Generate it on first call
  33278. if (isNew) {
  33279. this[prop] = group = this.chart.renderer
  33280. .g()
  33281. .add(parent);
  33282. }
  33283. // Add the class names, and replace existing ones as response to
  33284. // Series.update (#6660)
  33285. group.addClass(('highcharts-' + name +
  33286. ' highcharts-series-' + this.index +
  33287. ' highcharts-' + this.type + '-series ' +
  33288. (defined(this.colorIndex) ?
  33289. 'highcharts-color-' + this.colorIndex + ' ' :
  33290. '') +
  33291. (this.options.className || '') +
  33292. (group.hasClass('highcharts-tracker') ?
  33293. ' highcharts-tracker' :
  33294. '')), true);
  33295. // Place it on first and subsequent (redraw) calls
  33296. group.attr(attrs)[isNew ? 'attr' : 'animate'](this.getPlotBox());
  33297. return group;
  33298. },
  33299. /**
  33300. * Get the translation and scale for the plot area of this series.
  33301. *
  33302. * @function Highcharts.Series#getPlotBox
  33303. *
  33304. * @return {Highcharts.SeriesPlotBoxObject}
  33305. */
  33306. getPlotBox: function () {
  33307. var chart = this.chart, xAxis = this.xAxis, yAxis = this.yAxis;
  33308. // Swap axes for inverted (#2339)
  33309. if (chart.inverted) {
  33310. xAxis = yAxis;
  33311. yAxis = this.xAxis;
  33312. }
  33313. return {
  33314. translateX: xAxis ? xAxis.left : chart.plotLeft,
  33315. translateY: yAxis ? yAxis.top : chart.plotTop,
  33316. scaleX: 1,
  33317. scaleY: 1
  33318. };
  33319. },
  33320. /**
  33321. * Removes the event handlers attached previously with addEvents.
  33322. *
  33323. * @private
  33324. * @function Highcharts.Series#removeEvents
  33325. * @param {boolean} [keepEventsForUpdate]
  33326. * @return {void}
  33327. */
  33328. removeEvents: function (keepEventsForUpdate) {
  33329. var series = this;
  33330. if (!keepEventsForUpdate) {
  33331. // remove all events
  33332. removeEvent(series);
  33333. }
  33334. else if (series.eventsToUnbind.length) {
  33335. // remove only internal events for proper update
  33336. // #12355 - solves problem with multiple destroy events
  33337. series.eventsToUnbind.forEach(function (unbind) {
  33338. unbind();
  33339. });
  33340. series.eventsToUnbind.length = 0;
  33341. }
  33342. },
  33343. /**
  33344. * Render the graph and markers. Called internally when first rendering
  33345. * and later when redrawing the chart. This function can be extended in
  33346. * plugins, but normally shouldn't be called directly.
  33347. *
  33348. * @function Highcharts.Series#render
  33349. *
  33350. * @return {void}
  33351. *
  33352. * @fires Highcharts.Series#event:afterRender
  33353. */
  33354. render: function () {
  33355. var series = this, chart = series.chart, group, options = series.options,
  33356. // Animation doesn't work in IE8 quirks when the group div is
  33357. // hidden, and looks bad in other oldIE
  33358. animDuration = (!series.finishedAnimating &&
  33359. chart.renderer.isSVG &&
  33360. animObject(options.animation).duration), visibility = series.visible ? 'inherit' : 'hidden', // #2597
  33361. zIndex = options.zIndex, hasRendered = series.hasRendered, chartSeriesGroup = chart.seriesGroup, inverted = chart.inverted;
  33362. fireEvent(this, 'render');
  33363. // the group
  33364. group = series.plotGroup('group', 'series', visibility, zIndex, chartSeriesGroup);
  33365. series.markerGroup = series.plotGroup('markerGroup', 'markers', visibility, zIndex, chartSeriesGroup);
  33366. // initiate the animation
  33367. if (animDuration && series.animate) {
  33368. series.animate(true);
  33369. }
  33370. // SVGRenderer needs to know this before drawing elements (#1089,
  33371. // #1795)
  33372. group.inverted = series.isCartesian || series.invertable ?
  33373. inverted : false;
  33374. // Draw the graph if any
  33375. if (series.drawGraph) {
  33376. series.drawGraph();
  33377. series.applyZones();
  33378. }
  33379. // Draw the points
  33380. if (series.visible) {
  33381. series.drawPoints();
  33382. }
  33383. /* series.points.forEach(function (point) {
  33384. if (point.redraw) {
  33385. point.redraw();
  33386. }
  33387. }); */
  33388. // Draw the data labels
  33389. if (series.drawDataLabels) {
  33390. series.drawDataLabels();
  33391. }
  33392. // In pie charts, slices are added to the DOM, but actual rendering
  33393. // is postponed until labels reserved their space
  33394. if (series.redrawPoints) {
  33395. series.redrawPoints();
  33396. }
  33397. // draw the mouse tracking area
  33398. if (series.drawTracker &&
  33399. series.options.enableMouseTracking !== false) {
  33400. series.drawTracker();
  33401. }
  33402. // Handle inverted series and tracker groups
  33403. series.invertGroups(inverted);
  33404. // Initial clipping, must be defined after inverting groups for VML.
  33405. // Applies to columns etc. (#3839).
  33406. if (options.clip !== false &&
  33407. !series.sharedClipKey &&
  33408. !hasRendered) {
  33409. group.clip(chart.clipRect);
  33410. }
  33411. // Run the animation
  33412. if (animDuration && series.animate) {
  33413. series.animate();
  33414. }
  33415. // Call the afterAnimate function on animation complete (but don't
  33416. // overwrite the animation.complete option which should be available
  33417. // to the user).
  33418. if (!hasRendered) {
  33419. series.animationTimeout = syncTimeout(function () {
  33420. series.afterAnimate();
  33421. }, animDuration || 0);
  33422. }
  33423. // Means data is in accordance with what you see
  33424. series.isDirty = false;
  33425. // (See #322) series.isDirty = series.isDirtyData = false; // means
  33426. // data is in accordance with what you see
  33427. series.hasRendered = true;
  33428. fireEvent(series, 'afterRender');
  33429. },
  33430. /**
  33431. * Redraw the series. This function is called internally from
  33432. * `chart.redraw` and normally shouldn't be called directly.
  33433. *
  33434. * @private
  33435. * @function Highcharts.Series#redraw
  33436. * @return {void}
  33437. */
  33438. redraw: function () {
  33439. var series = this, chart = series.chart,
  33440. // cache it here as it is set to false in render, but used after
  33441. wasDirty = series.isDirty || series.isDirtyData, group = series.group, xAxis = series.xAxis, yAxis = series.yAxis;
  33442. // reposition on resize
  33443. if (group) {
  33444. if (chart.inverted) {
  33445. group.attr({
  33446. width: chart.plotWidth,
  33447. height: chart.plotHeight
  33448. });
  33449. }
  33450. group.animate({
  33451. translateX: pick(xAxis && xAxis.left, chart.plotLeft),
  33452. translateY: pick(yAxis && yAxis.top, chart.plotTop)
  33453. });
  33454. }
  33455. series.translate();
  33456. series.render();
  33457. if (wasDirty) { // #3868, #3945
  33458. delete this.kdTree;
  33459. }
  33460. },
  33461. kdAxisArray: ['clientX', 'plotY'],
  33462. /**
  33463. * @private
  33464. * @function Highcharts.Series#searchPoint
  33465. * @param {Highcharts.PointerEventObject} e
  33466. * @param {boolean} [compareX]
  33467. * @return {Highcharts.Point}
  33468. */
  33469. searchPoint: function (e, compareX) {
  33470. var series = this, xAxis = series.xAxis, yAxis = series.yAxis, inverted = series.chart.inverted;
  33471. return this.searchKDTree({
  33472. clientX: inverted ?
  33473. xAxis.len - e.chartY + xAxis.pos :
  33474. e.chartX - xAxis.pos,
  33475. plotY: inverted ?
  33476. yAxis.len - e.chartX + yAxis.pos :
  33477. e.chartY - yAxis.pos
  33478. }, compareX, e);
  33479. },
  33480. /**
  33481. * Build the k-d-tree that is used by mouse and touch interaction to get
  33482. * the closest point. Line-like series typically have a one-dimensional
  33483. * tree where points are searched along the X axis, while scatter-like
  33484. * series typically search in two dimensions, X and Y.
  33485. *
  33486. * @private
  33487. * @function Highcharts.Series#buildKDTree
  33488. * @param {Highcharts.PointerEventObject} [e]
  33489. * @return {void}
  33490. */
  33491. buildKDTree: function (e) {
  33492. // Prevent multiple k-d-trees from being built simultaneously
  33493. // (#6235)
  33494. this.buildingKdTree = true;
  33495. var series = this, dimensions = series.options.findNearestPointBy
  33496. .indexOf('y') > -1 ? 2 : 1;
  33497. /**
  33498. * Internal function
  33499. * @private
  33500. */
  33501. function _kdtree(points, depth, dimensions) {
  33502. var axis, median, length = points && points.length;
  33503. if (length) {
  33504. // alternate between the axis
  33505. axis = series.kdAxisArray[depth % dimensions];
  33506. // sort point array
  33507. points.sort(function (a, b) {
  33508. return a[axis] - b[axis];
  33509. });
  33510. median = Math.floor(length / 2);
  33511. // build and return nod
  33512. return {
  33513. point: points[median],
  33514. left: _kdtree(points.slice(0, median), depth + 1, dimensions),
  33515. right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
  33516. };
  33517. }
  33518. }
  33519. /**
  33520. * Start the recursive build process with a clone of the points
  33521. * array and null points filtered out. (#3873)
  33522. * @private
  33523. */
  33524. function startRecursive() {
  33525. series.kdTree = _kdtree(series.getValidPoints(null,
  33526. // For line-type series restrict to plot area, but
  33527. // column-type series not (#3916, #4511)
  33528. !series.directTouch), dimensions, dimensions);
  33529. series.buildingKdTree = false;
  33530. }
  33531. delete series.kdTree;
  33532. // For testing tooltips, don't build async. Also if touchstart, we
  33533. // may be dealing with click events on mobile, so don't delay
  33534. // (#6817).
  33535. syncTimeout(startRecursive, series.options.kdNow || (e && e.type === 'touchstart') ? 0 : 1);
  33536. },
  33537. /**
  33538. * @private
  33539. * @function Highcharts.Series#searchKDTree
  33540. * @param {Highcharts.KDPointSearchObject} point
  33541. * @param {boolean} [compareX]
  33542. * @param {Highcharts.PointerEventObject} [e]
  33543. * @return {Highcharts.Point|undefined}
  33544. */
  33545. searchKDTree: function (point, compareX, e) {
  33546. var series = this, kdX = this.kdAxisArray[0], kdY = this.kdAxisArray[1], kdComparer = compareX ? 'distX' : 'dist', kdDimensions = series.options.findNearestPointBy
  33547. .indexOf('y') > -1 ? 2 : 1;
  33548. /**
  33549. * Set the one and two dimensional distance on the point object.
  33550. * @private
  33551. */
  33552. function setDistance(p1, p2) {
  33553. var x = (defined(p1[kdX]) &&
  33554. defined(p2[kdX])) ?
  33555. Math.pow(p1[kdX] - p2[kdX], 2) :
  33556. null, y = (defined(p1[kdY]) &&
  33557. defined(p2[kdY])) ?
  33558. Math.pow(p1[kdY] - p2[kdY], 2) :
  33559. null, r = (x || 0) + (y || 0);
  33560. p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
  33561. p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
  33562. }
  33563. /**
  33564. * @private
  33565. */
  33566. function _search(search, tree, depth, dimensions) {
  33567. var point = tree.point, axis = series.kdAxisArray[depth % dimensions], tdist, sideA, sideB, ret = point, nPoint1, nPoint2;
  33568. setDistance(search, point);
  33569. // Pick side based on distance to splitting point
  33570. tdist = search[axis] - point[axis];
  33571. sideA = tdist < 0 ? 'left' : 'right';
  33572. sideB = tdist < 0 ? 'right' : 'left';
  33573. // End of tree
  33574. if (tree[sideA]) {
  33575. nPoint1 = _search(search, tree[sideA], depth + 1, dimensions);
  33576. ret = (nPoint1[kdComparer] <
  33577. ret[kdComparer] ?
  33578. nPoint1 :
  33579. point);
  33580. }
  33581. if (tree[sideB]) {
  33582. // compare distance to current best to splitting point to
  33583. // decide wether to check side B or not
  33584. if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
  33585. nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
  33586. ret = (nPoint2[kdComparer] <
  33587. ret[kdComparer] ?
  33588. nPoint2 :
  33589. ret);
  33590. }
  33591. }
  33592. return ret;
  33593. }
  33594. if (!this.kdTree && !this.buildingKdTree) {
  33595. this.buildKDTree(e);
  33596. }
  33597. if (this.kdTree) {
  33598. return _search(point, this.kdTree, kdDimensions, kdDimensions);
  33599. }
  33600. },
  33601. /**
  33602. * @private
  33603. * @function Highcharts.Series#pointPlacementToXValue
  33604. * @return {number}
  33605. */
  33606. pointPlacementToXValue: function () {
  33607. var _a = this, _b = _a.options, pointPlacement = _b.pointPlacement, pointRange = _b.pointRange, axis = _a.xAxis;
  33608. var factor = pointPlacement;
  33609. // Point placement is relative to each series pointRange (#5889)
  33610. if (factor === 'between') {
  33611. factor = axis.reversed ? -0.5 : 0.5; // #11955
  33612. }
  33613. return isNumber(factor) ?
  33614. factor * pick(pointRange, axis.pointRange) :
  33615. 0;
  33616. },
  33617. /**
  33618. * @private
  33619. * @function Highcharts.Series#isPointInside
  33620. * @param {Highcharts.Point} point
  33621. * @return {boolean}
  33622. */
  33623. isPointInside: function (point) {
  33624. var isInside = typeof point.plotY !== 'undefined' &&
  33625. typeof point.plotX !== 'undefined' &&
  33626. point.plotY >= 0 &&
  33627. point.plotY <= this.yAxis.len && // #3519
  33628. point.plotX >= 0 &&
  33629. point.plotX <= this.xAxis.len;
  33630. return isInside;
  33631. }
  33632. }); // end Series prototype
  33633. /**
  33634. * A line series displays information as a series of data points connected by
  33635. * straight line segments.
  33636. *
  33637. * @sample {highcharts} highcharts/demo/line-basic/
  33638. * Line chart
  33639. * @sample {highstock} stock/demo/basic-line/
  33640. * Line chart
  33641. *
  33642. * @extends plotOptions.series
  33643. * @product highcharts highstock
  33644. * @apioption plotOptions.line
  33645. */
  33646. /**
  33647. * The SVG value used for the `stroke-linecap` and `stroke-linejoin`
  33648. * of a line graph. Round means that lines are rounded in the ends and
  33649. * bends.
  33650. *
  33651. * @type {Highcharts.SeriesLinecapValue}
  33652. * @default round
  33653. * @since 3.0.7
  33654. * @apioption plotOptions.line.linecap
  33655. */
  33656. /**
  33657. * A `line` series. If the [type](#series.line.type) option is not
  33658. * specified, it is inherited from [chart.type](#chart.type).
  33659. *
  33660. * @extends series,plotOptions.line
  33661. * @excluding dataParser,dataURL
  33662. * @product highcharts highstock
  33663. * @apioption series.line
  33664. */
  33665. /**
  33666. * An array of data points for the series. For the `line` series type,
  33667. * points can be given in the following ways:
  33668. *
  33669. * 1. An array of numerical values. In this case, the numerical values will be
  33670. * interpreted as `y` options. The `x` values will be automatically
  33671. * calculated, either starting at 0 and incremented by 1, or from
  33672. * `pointStart` and `pointInterval` given in the series options. If the axis
  33673. * has categories, these will be used. Example:
  33674. * ```js
  33675. * data: [0, 5, 3, 5]
  33676. * ```
  33677. *
  33678. * 2. An array of arrays with 2 values. In this case, the values correspond to
  33679. * `x,y`. If the first value is a string, it is applied as the name of the
  33680. * point, and the `x` value is inferred.
  33681. * ```js
  33682. * data: [
  33683. * [0, 1],
  33684. * [1, 2],
  33685. * [2, 8]
  33686. * ]
  33687. * ```
  33688. *
  33689. * 3. An array of objects with named values. The following snippet shows only a
  33690. * few settings, see the complete options set below. If the total number of
  33691. * data points exceeds the series'
  33692. * [turboThreshold](#series.line.turboThreshold),
  33693. * this option is not available.
  33694. * ```js
  33695. * data: [{
  33696. * x: 1,
  33697. * y: 9,
  33698. * name: "Point2",
  33699. * color: "#00FF00"
  33700. * }, {
  33701. * x: 1,
  33702. * y: 6,
  33703. * name: "Point1",
  33704. * color: "#FF00FF"
  33705. * }]
  33706. * ```
  33707. *
  33708. * **Note:** In TypeScript you have to extend `PointOptionsObject` with an
  33709. * additional declaration to allow custom data types:
  33710. * ```ts
  33711. * declare module `highcharts` {
  33712. * interface PointOptionsObject {
  33713. * custom: Record<string, (boolean|number|string)>;
  33714. * }
  33715. * }
  33716. * ```
  33717. *
  33718. * @sample {highcharts} highcharts/chart/reflow-true/
  33719. * Numerical values
  33720. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  33721. * Arrays of numeric x and y
  33722. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  33723. * Arrays of datetime x and y
  33724. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  33725. * Arrays of point.name and y
  33726. * @sample {highcharts} highcharts/series/data-array-of-objects/
  33727. * Config objects
  33728. *
  33729. * @declare Highcharts.PointOptionsObject
  33730. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  33731. * @apioption series.line.data
  33732. */
  33733. /**
  33734. * An additional, individual class name for the data point's graphic
  33735. * representation.
  33736. *
  33737. * @type {string}
  33738. * @since 5.0.0
  33739. * @product highcharts gantt
  33740. * @apioption series.line.data.className
  33741. */
  33742. /**
  33743. * Individual color for the point. By default the color is pulled from
  33744. * the global `colors` array.
  33745. *
  33746. * In styled mode, the `color` option doesn't take effect. Instead, use
  33747. * `colorIndex`.
  33748. *
  33749. * @sample {highcharts} highcharts/point/color/
  33750. * Mark the highest point
  33751. *
  33752. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  33753. * @product highcharts highstock gantt
  33754. * @apioption series.line.data.color
  33755. */
  33756. /**
  33757. * A specific color index to use for the point, so its graphic representations
  33758. * are given the class name `highcharts-color-{n}`. In styled mode this will
  33759. * change the color of the graphic. In non-styled mode, the color by is set by
  33760. * the `fill` attribute, so the change in class name won't have a visual effect
  33761. * by default.
  33762. *
  33763. * @type {number}
  33764. * @since 5.0.0
  33765. * @product highcharts gantt
  33766. * @apioption series.line.data.colorIndex
  33767. */
  33768. /**
  33769. * A reserved subspace to store options and values for customized functionality.
  33770. * Here you can add additional data for your own event callbacks and formatter
  33771. * callbacks.
  33772. *
  33773. * @sample {highcharts} highcharts/point/custom/
  33774. * Point and series with custom data
  33775. *
  33776. * @type {Highcharts.Dictionary<*>}
  33777. * @apioption series.line.data.custom
  33778. */
  33779. /**
  33780. * Individual data label for each point. The options are the same as
  33781. * the ones for [plotOptions.series.dataLabels](
  33782. * #plotOptions.series.dataLabels).
  33783. *
  33784. * @sample highcharts/point/datalabels/
  33785. * Show a label for the last value
  33786. *
  33787. * @declare Highcharts.DataLabelsOptions
  33788. * @extends plotOptions.line.dataLabels
  33789. * @product highcharts highstock gantt
  33790. * @apioption series.line.data.dataLabels
  33791. */
  33792. /**
  33793. * A description of the point to add to the screen reader information
  33794. * about the point.
  33795. *
  33796. * @type {string}
  33797. * @since 5.0.0
  33798. * @requires modules/accessibility
  33799. * @apioption series.line.data.description
  33800. */
  33801. /**
  33802. * An id for the point. This can be used after render time to get a
  33803. * pointer to the point object through `chart.get()`.
  33804. *
  33805. * @sample {highcharts} highcharts/point/id/
  33806. * Remove an id'd point
  33807. *
  33808. * @type {string}
  33809. * @since 1.2.0
  33810. * @product highcharts highstock gantt
  33811. * @apioption series.line.data.id
  33812. */
  33813. /**
  33814. * The rank for this point's data label in case of collision. If two
  33815. * data labels are about to overlap, only the one with the highest `labelrank`
  33816. * will be drawn.
  33817. *
  33818. * @type {number}
  33819. * @apioption series.line.data.labelrank
  33820. */
  33821. /**
  33822. * The name of the point as shown in the legend, tooltip, dataLabels, etc.
  33823. *
  33824. * @see [xAxis.uniqueNames](#xAxis.uniqueNames)
  33825. *
  33826. * @sample {highcharts} highcharts/series/data-array-of-objects/
  33827. * Point names
  33828. *
  33829. * @type {string}
  33830. * @apioption series.line.data.name
  33831. */
  33832. /**
  33833. * Whether the data point is selected initially.
  33834. *
  33835. * @type {boolean}
  33836. * @default false
  33837. * @product highcharts highstock gantt
  33838. * @apioption series.line.data.selected
  33839. */
  33840. /**
  33841. * The x value of the point. For datetime axes, the X value is the timestamp
  33842. * in milliseconds since 1970.
  33843. *
  33844. * @type {number}
  33845. * @product highcharts highstock
  33846. * @apioption series.line.data.x
  33847. */
  33848. /**
  33849. * The y value of the point.
  33850. *
  33851. * @type {number|null}
  33852. * @product highcharts highstock
  33853. * @apioption series.line.data.y
  33854. */
  33855. /**
  33856. * The individual point events.
  33857. *
  33858. * @extends plotOptions.series.point.events
  33859. * @product highcharts highstock gantt
  33860. * @apioption series.line.data.events
  33861. */
  33862. /**
  33863. * Options for the point markers of line-like series.
  33864. *
  33865. * @declare Highcharts.PointMarkerOptionsObject
  33866. * @extends plotOptions.series.marker
  33867. * @product highcharts highstock
  33868. * @apioption series.line.data.marker
  33869. */
  33870. ''; // include precedent doclets in transpilat
  33871. });
  33872. _registerModule(_modules, 'parts/Stacking.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/StackingAxis.js'], _modules['parts/Utilities.js']], function (Axis, Chart, H, StackingAxis, U) {
  33873. /* *
  33874. *
  33875. * (c) 2010-2020 Torstein Honsi
  33876. *
  33877. * License: www.highcharts.com/license
  33878. *
  33879. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  33880. *
  33881. * */
  33882. var correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, format = U.format, isNumber = U.isNumber, pick = U.pick;
  33883. /**
  33884. * Stack of data points
  33885. *
  33886. * @product highcharts
  33887. *
  33888. * @interface Highcharts.StackItemObject
  33889. */ /**
  33890. * Alignment settings
  33891. * @name Highcharts.StackItemObject#alignOptions
  33892. * @type {Highcharts.AlignObject}
  33893. */ /**
  33894. * Related axis
  33895. * @name Highcharts.StackItemObject#axis
  33896. * @type {Highcharts.Axis}
  33897. */ /**
  33898. * Cumulative value of the stacked data points
  33899. * @name Highcharts.StackItemObject#cumulative
  33900. * @type {number}
  33901. */ /**
  33902. * True if on the negative side
  33903. * @name Highcharts.StackItemObject#isNegative
  33904. * @type {boolean}
  33905. */ /**
  33906. * Related SVG element
  33907. * @name Highcharts.StackItemObject#label
  33908. * @type {Highcharts.SVGElement}
  33909. */ /**
  33910. * Related stack options
  33911. * @name Highcharts.StackItemObject#options
  33912. * @type {Highcharts.YAxisStackLabelsOptions}
  33913. */ /**
  33914. * Total value of the stacked data points
  33915. * @name Highcharts.StackItemObject#total
  33916. * @type {number}
  33917. */ /**
  33918. * Shared x value of the stack
  33919. * @name Highcharts.StackItemObject#x
  33920. * @type {number}
  33921. */
  33922. ''; // detached doclets above
  33923. var Series = H.Series;
  33924. /* eslint-disable no-invalid-this, valid-jsdoc */
  33925. /**
  33926. * The class for stacks. Each stack, on a specific X value and either negative
  33927. * or positive, has its own stack item.
  33928. *
  33929. * @private
  33930. * @class
  33931. * @name Highcharts.StackItem
  33932. * @param {Highcharts.Axis} axis
  33933. * @param {Highcharts.YAxisStackLabelsOptions} options
  33934. * @param {boolean} isNegative
  33935. * @param {number} x
  33936. * @param {Highcharts.OptionsStackingValue} [stackOption]
  33937. */
  33938. var StackItem = /** @class */ (function () {
  33939. function StackItem(axis, options, isNegative, x, stackOption) {
  33940. var inverted = axis.chart.inverted;
  33941. this.axis = axis;
  33942. // Tells if the stack is negative
  33943. this.isNegative = isNegative;
  33944. // Save the options to be able to style the label
  33945. this.options = options = options || {};
  33946. // Save the x value to be able to position the label later
  33947. this.x = x;
  33948. // Initialize total value
  33949. this.total = null;
  33950. // This will keep each points' extremes stored by series.index and point
  33951. // index
  33952. this.points = {};
  33953. this.hasValidPoints = false;
  33954. // Save the stack option on the series configuration object,
  33955. // and whether to treat it as percent
  33956. this.stack = stackOption;
  33957. this.leftCliff = 0;
  33958. this.rightCliff = 0;
  33959. // The align options and text align varies on whether the stack is
  33960. // negative and if the chart is inverted or not.
  33961. // First test the user supplied value, then use the dynamic.
  33962. this.alignOptions = {
  33963. align: options.align ||
  33964. (inverted ? (isNegative ? 'left' : 'right') : 'center'),
  33965. verticalAlign: options.verticalAlign ||
  33966. (inverted ? 'middle' : (isNegative ? 'bottom' : 'top')),
  33967. y: options.y,
  33968. x: options.x
  33969. };
  33970. this.textAlign = options.textAlign ||
  33971. (inverted ? (isNegative ? 'right' : 'left') : 'center');
  33972. }
  33973. /**
  33974. * @private
  33975. * @function Highcharts.StackItem#destroy
  33976. */
  33977. StackItem.prototype.destroy = function () {
  33978. destroyObjectProperties(this, this.axis);
  33979. };
  33980. /**
  33981. * Renders the stack total label and adds it to the stack label group.
  33982. *
  33983. * @private
  33984. * @function Highcharts.StackItem#render
  33985. * @param {Highcharts.SVGElement} group
  33986. */
  33987. StackItem.prototype.render = function (group) {
  33988. var chart = this.axis.chart, options = this.options, formatOption = options.format, attr = {}, str = formatOption ? // format the text in the label
  33989. format(formatOption, this, chart) :
  33990. options.formatter.call(this);
  33991. // Change the text to reflect the new total and set visibility to hidden
  33992. // in case the serie is hidden
  33993. if (this.label) {
  33994. this.label.attr({ text: str, visibility: 'hidden' });
  33995. }
  33996. else {
  33997. // Create new label
  33998. this.label = chart.renderer
  33999. .label(str, null, null, options.shape, null, null, options.useHTML, false, 'stack-labels');
  34000. attr = {
  34001. r: options.borderRadius || 0,
  34002. text: str,
  34003. rotation: options.rotation,
  34004. padding: pick(options.padding, 5),
  34005. visibility: 'hidden' // hidden until setOffset is called
  34006. };
  34007. if (!chart.styledMode) {
  34008. attr.fill = options.backgroundColor;
  34009. attr.stroke = options.borderColor;
  34010. attr['stroke-width'] = options.borderWidth;
  34011. this.label.css(options.style);
  34012. }
  34013. this.label.attr(attr);
  34014. if (!this.label.added) {
  34015. this.label.add(group); // add to the labels-group
  34016. }
  34017. }
  34018. // Rank it higher than data labels (#8742)
  34019. this.label.labelrank = chart.plotHeight;
  34020. };
  34021. /**
  34022. * Sets the offset that the stack has from the x value and repositions the
  34023. * label.
  34024. *
  34025. * @private
  34026. * @function Highcarts.StackItem#setOffset
  34027. * @param {number} xOffset
  34028. * @param {number} xWidth
  34029. * @param {number} [boxBottom]
  34030. * @param {number} [boxTop]
  34031. * @param {number} [defaultX]
  34032. */
  34033. StackItem.prototype.setOffset = function (xOffset, xWidth, boxBottom, boxTop, defaultX) {
  34034. var stackItem = this, axis = stackItem.axis, chart = axis.chart,
  34035. // stack value translated mapped to chart coordinates
  34036. y = axis.translate(axis.stacking.usePercentage ?
  34037. 100 :
  34038. (boxTop ?
  34039. boxTop :
  34040. stackItem.total), 0, 0, 0, 1), yZero = axis.translate(boxBottom ? boxBottom : 0), // stack origin
  34041. // stack height:
  34042. h = defined(y) && Math.abs(y - yZero),
  34043. // x position:
  34044. x = pick(defaultX, chart.xAxis[0].translate(stackItem.x)) +
  34045. xOffset, stackBox = defined(y) && stackItem.getStackBox(chart, stackItem, x, y, xWidth, h, axis), label = stackItem.label, isNegative = stackItem.isNegative, isJustify = pick(stackItem.options.overflow, 'justify') === 'justify', textAlign = stackItem.textAlign, visible;
  34046. if (label && stackBox) {
  34047. var bBox = label.getBBox(), padding = label.padding, boxOffsetX, boxOffsetY;
  34048. if (textAlign === 'left') {
  34049. boxOffsetX = chart.inverted ? -padding : padding;
  34050. }
  34051. else if (textAlign === 'right') {
  34052. boxOffsetX = bBox.width;
  34053. }
  34054. else {
  34055. if (chart.inverted && textAlign === 'center') {
  34056. boxOffsetX = bBox.width / 2;
  34057. }
  34058. else {
  34059. boxOffsetX = chart.inverted ?
  34060. (isNegative ? bBox.width + padding : -padding) : bBox.width / 2;
  34061. }
  34062. }
  34063. boxOffsetY = chart.inverted ?
  34064. bBox.height / 2 : (isNegative ? -padding : bBox.height);
  34065. // Reset alignOptions property after justify #12337
  34066. stackItem.alignOptions.x = pick(stackItem.options.x, 0);
  34067. stackItem.alignOptions.y = pick(stackItem.options.y, 0);
  34068. // Set the stackBox position
  34069. stackBox.x -= boxOffsetX;
  34070. stackBox.y -= boxOffsetY;
  34071. // Align the label to the box
  34072. label.align(stackItem.alignOptions, null, stackBox);
  34073. // Check if label is inside the plotArea #12294
  34074. if (chart.isInsidePlot(label.alignAttr.x + boxOffsetX - stackItem.alignOptions.x, label.alignAttr.y + boxOffsetY - stackItem.alignOptions.y)) {
  34075. label.show();
  34076. }
  34077. else {
  34078. // Move label away to avoid the overlapping issues
  34079. label.alignAttr.y = -9999;
  34080. isJustify = false;
  34081. }
  34082. if (isJustify) {
  34083. // Justify stackLabel into the stackBox
  34084. Series.prototype.justifyDataLabel.call(this.axis, label, stackItem.alignOptions, label.alignAttr, bBox, stackBox);
  34085. }
  34086. label.attr({
  34087. x: label.alignAttr.x,
  34088. y: label.alignAttr.y
  34089. });
  34090. if (pick(!isJustify && stackItem.options.crop, true)) {
  34091. visible =
  34092. isNumber(label.x) &&
  34093. isNumber(label.y) &&
  34094. chart.isInsidePlot(label.x - padding + label.width, label.y) &&
  34095. chart.isInsidePlot(label.x + padding, label.y);
  34096. if (!visible) {
  34097. label.hide();
  34098. }
  34099. }
  34100. }
  34101. };
  34102. /**
  34103. * @private
  34104. * @function Highcharts.StackItem#getStackBox
  34105. *
  34106. * @param {Highcharts.Chart} chart
  34107. *
  34108. * @param {Highcharts.StackItem} stackItem
  34109. *
  34110. * @param {number} x
  34111. *
  34112. * @param {number} y
  34113. *
  34114. * @param {number} xWidth
  34115. *
  34116. * @param {number} h
  34117. *
  34118. * @param {Highcharts.Axis} axis
  34119. *
  34120. * @return {Highcharts.BBoxObject}
  34121. */
  34122. StackItem.prototype.getStackBox = function (chart, stackItem, x, y, xWidth, h, axis) {
  34123. var reversed = stackItem.axis.reversed, inverted = chart.inverted, axisPos = axis.height + axis.pos -
  34124. (inverted ? chart.plotLeft : chart.plotTop), neg = (stackItem.isNegative && !reversed) ||
  34125. (!stackItem.isNegative && reversed); // #4056
  34126. return {
  34127. x: inverted ? (neg ? y - axis.right : y - h + axis.pos - chart.plotLeft) :
  34128. x + chart.xAxis[0].transB - chart.plotLeft,
  34129. y: inverted ?
  34130. axis.height - x - xWidth :
  34131. (neg ?
  34132. (axisPos - y - h) :
  34133. axisPos - y),
  34134. width: inverted ? h : xWidth,
  34135. height: inverted ? xWidth : h
  34136. };
  34137. };
  34138. return StackItem;
  34139. }());
  34140. /**
  34141. * Generate stacks for each series and calculate stacks total values
  34142. *
  34143. * @private
  34144. * @function Highcharts.Chart#getStacks
  34145. */
  34146. Chart.prototype.getStacks = function () {
  34147. var chart = this, inverted = chart.inverted;
  34148. // reset stacks for each yAxis
  34149. chart.yAxis.forEach(function (axis) {
  34150. if (axis.stacking && axis.stacking.stacks && axis.hasVisibleSeries) {
  34151. axis.stacking.oldStacks = axis.stacking.stacks;
  34152. }
  34153. });
  34154. chart.series.forEach(function (series) {
  34155. var xAxisOptions = series.xAxis && series.xAxis.options || {};
  34156. if (series.options.stacking &&
  34157. (series.visible === true ||
  34158. chart.options.chart.ignoreHiddenSeries === false)) {
  34159. series.stackKey = [
  34160. series.type,
  34161. pick(series.options.stack, ''),
  34162. inverted ? xAxisOptions.top : xAxisOptions.left,
  34163. inverted ? xAxisOptions.height : xAxisOptions.width
  34164. ].join(',');
  34165. }
  34166. });
  34167. };
  34168. // Stacking methods defined on the Axis prototype
  34169. StackingAxis.compose(Axis);
  34170. // Stacking methods defined for Series prototype
  34171. /**
  34172. * Set grouped points in a stack-like object. When `centerInCategory` is true,
  34173. * and `stacking` is not enabled, we need a pseudo (horizontal) stack in order
  34174. * to handle grouping of points within the same category.
  34175. *
  34176. * @private
  34177. * @function Highcharts.Series#setStackedPoints
  34178. * @return {void}
  34179. */
  34180. Series.prototype.setGroupedPoints = function () {
  34181. if (this.options.centerInCategory &&
  34182. (this.is('column') || this.is('columnrange')) &&
  34183. // With stacking enabled, we already have stacks that we can compute
  34184. // from
  34185. !this.options.stacking &&
  34186. // With only one series, we don't need to consider centerInCategory
  34187. this.chart.series.length > 1) {
  34188. Series.prototype.setStackedPoints.call(this, 'group');
  34189. }
  34190. };
  34191. /**
  34192. * Adds series' points value to corresponding stack
  34193. *
  34194. * @private
  34195. * @function Highcharts.Series#setStackedPoints
  34196. */
  34197. Series.prototype.setStackedPoints = function (stackingParam) {
  34198. var stacking = stackingParam || this.options.stacking;
  34199. if (!stacking ||
  34200. (this.visible !== true &&
  34201. this.chart.options.chart.ignoreHiddenSeries !== false)) {
  34202. return;
  34203. }
  34204. var 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 = series.yAxis, stacks = yAxis.stacking.stacks, oldStacks = yAxis.stacking.oldStacks, stackIndicator, isNegative, stack, other, key, pointKey, i, x, y;
  34205. yAxis.stacking.stacksTouched += 1;
  34206. // loop over the non-null y values and read them into a local array
  34207. for (i = 0; i < yDataLength; i++) {
  34208. x = xData[i];
  34209. y = yData[i];
  34210. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
  34211. pointKey = stackIndicator.key;
  34212. // Read stacked values into a stack based on the x value,
  34213. // the sign of y and the stack key. Stacking is also handled for null
  34214. // values (#739)
  34215. isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
  34216. key = isNegative ? negKey : stackKey;
  34217. // Create empty object for this stack if it doesn't exist yet
  34218. if (!stacks[key]) {
  34219. stacks[key] =
  34220. {};
  34221. }
  34222. // Initialize StackItem for this x
  34223. if (!stacks[key][x]) {
  34224. if (oldStacks[key] &&
  34225. oldStacks[key][x]) {
  34226. stacks[key][x] = oldStacks[key][x];
  34227. stacks[key][x].total = null;
  34228. }
  34229. else {
  34230. stacks[key][x] = new StackItem(yAxis, yAxis.options.stackLabels, isNegative, x, stackOption);
  34231. }
  34232. }
  34233. // If the StackItem doesn't exist, create it first
  34234. stack = stacks[key][x];
  34235. if (y !== null) {
  34236. stack.points[pointKey] = stack.points[series.index] =
  34237. [pick(stack.cumulative, stackThreshold)];
  34238. // Record the base of the stack
  34239. if (!defined(stack.cumulative)) {
  34240. stack.base = pointKey;
  34241. }
  34242. stack.touched = yAxis.stacking.stacksTouched;
  34243. // In area charts, if there are multiple points on the same X value,
  34244. // let the area fill the full span of those points
  34245. if (stackIndicator.index > 0 && series.singleStacks === false) {
  34246. stack.points[pointKey][0] =
  34247. stack.points[series.index + ',' + x + ',0'][0];
  34248. }
  34249. // When updating to null, reset the point stack (#7493)
  34250. }
  34251. else {
  34252. stack.points[pointKey] = stack.points[series.index] =
  34253. null;
  34254. }
  34255. // Add value to the stack total
  34256. if (stacking === 'percent') {
  34257. // Percent stacked column, totals are the same for the positive and
  34258. // negative stacks
  34259. other = isNegative ? stackKey : negKey;
  34260. if (negStacks && stacks[other] && stacks[other][x]) {
  34261. other = stacks[other][x];
  34262. stack.total = other.total =
  34263. Math.max(other.total, stack.total) +
  34264. Math.abs(y) ||
  34265. 0;
  34266. // Percent stacked areas
  34267. }
  34268. else {
  34269. stack.total =
  34270. correctFloat(stack.total + (Math.abs(y) || 0));
  34271. }
  34272. }
  34273. else if (stacking === 'group') {
  34274. // In this stack, the total is the number of valid points
  34275. if (y !== null) {
  34276. stack.total = (stack.total || 0) + 1;
  34277. }
  34278. }
  34279. else {
  34280. stack.total = correctFloat(stack.total + (y || 0));
  34281. }
  34282. if (stacking === 'group') {
  34283. // This point's index within the stack, pushed to stack.points[1]
  34284. stack.cumulative = (stack.total || 1) - 1;
  34285. }
  34286. else {
  34287. stack.cumulative =
  34288. pick(stack.cumulative, stackThreshold) + (y || 0);
  34289. }
  34290. if (y !== null) {
  34291. stack.points[pointKey].push(stack.cumulative);
  34292. stackedYData[i] = stack.cumulative;
  34293. stack.hasValidPoints = true;
  34294. }
  34295. }
  34296. if (stacking === 'percent') {
  34297. yAxis.stacking.usePercentage = true;
  34298. }
  34299. if (stacking !== 'group') {
  34300. this.stackedYData = stackedYData; // To be used in getExtremes
  34301. }
  34302. // Reset old stacks
  34303. yAxis.stacking.oldStacks = {};
  34304. };
  34305. /**
  34306. * Iterate over all stacks and compute the absolute values to percent
  34307. *
  34308. * @private
  34309. * @function Highcharts.Series#modifyStacks
  34310. */
  34311. Series.prototype.modifyStacks = function () {
  34312. var series = this, yAxis = series.yAxis, stackKey = series.stackKey, stacks = yAxis.stacking.stacks, processedXData = series.processedXData, stackIndicator, stacking = series.options.stacking;
  34313. if (series[stacking + 'Stacker']) { // Modifier function exists
  34314. [stackKey, '-' + stackKey].forEach(function (key) {
  34315. var i = processedXData.length, x, stack, pointExtremes;
  34316. while (i--) {
  34317. x = processedXData[i];
  34318. stackIndicator = series.getStackIndicator(stackIndicator, x, series.index, key);
  34319. stack = stacks[key] && stacks[key][x];
  34320. pointExtremes =
  34321. stack && stack.points[stackIndicator.key];
  34322. if (pointExtremes) {
  34323. series[stacking + 'Stacker'](pointExtremes, stack, i);
  34324. }
  34325. }
  34326. });
  34327. }
  34328. };
  34329. /**
  34330. * Modifier function for percent stacks. Blows up the stack to 100%.
  34331. *
  34332. * @private
  34333. * @function Highcharts.Series#percentStacker
  34334. * @param {Array<number>} pointExtremes
  34335. * @param {Highcharts.StackItem} stack
  34336. * @param {number} i
  34337. */
  34338. Series.prototype.percentStacker = function (pointExtremes, stack, i) {
  34339. var totalFactor = stack.total ? 100 / stack.total : 0;
  34340. // Y bottom value
  34341. pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor);
  34342. // Y value
  34343. pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor);
  34344. this.stackedYData[i] = pointExtremes[1];
  34345. };
  34346. /**
  34347. * Get stack indicator, according to it's x-value, to determine points with the
  34348. * same x-value
  34349. *
  34350. * @private
  34351. * @function Highcharts.Series#getStackIndicator
  34352. * @param {Highcharts.StackItemIndicatorObject|undefined} stackIndicator
  34353. * @param {number} x
  34354. * @param {number} index
  34355. * @param {string} [key]
  34356. * @return {Highcharts.StackItemIndicatorObject}
  34357. */
  34358. Series.prototype.getStackIndicator = function (stackIndicator, x, index, key) {
  34359. // Update stack indicator, when:
  34360. // first point in a stack || x changed || stack type (negative vs positive)
  34361. // changed:
  34362. if (!defined(stackIndicator) ||
  34363. stackIndicator.x !== x ||
  34364. (key && stackIndicator.key !== key)) {
  34365. stackIndicator = {
  34366. x: x,
  34367. index: 0,
  34368. key: key
  34369. };
  34370. }
  34371. else {
  34372. (stackIndicator).index++;
  34373. }
  34374. stackIndicator.key =
  34375. [index, x, stackIndicator.index].join(',');
  34376. return stackIndicator;
  34377. };
  34378. H.StackItem = StackItem;
  34379. return H.StackItem;
  34380. });
  34381. _registerModule(_modules, 'parts/Dynamics.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Options.js'], _modules['parts/Point.js'], _modules['parts/Time.js'], _modules['parts/Utilities.js']], function (Axis, Chart, H, O, Point, Time, U) {
  34382. /* *
  34383. *
  34384. * (c) 2010-2020 Torstein Honsi
  34385. *
  34386. * License: www.highcharts.com/license
  34387. *
  34388. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34389. *
  34390. * */
  34391. var time = O.time;
  34392. var addEvent = U.addEvent, animate = U.animate, createElement = U.createElement, css = U.css, defined = U.defined, erase = U.erase, error = U.error, extend = U.extend, fireEvent = U.fireEvent, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, merge = U.merge, objectEach = U.objectEach, pick = U.pick, relativeLength = U.relativeLength, setAnimation = U.setAnimation, splat = U.splat;
  34393. var Series = H.Series, seriesTypes = H.seriesTypes;
  34394. /* eslint-disable valid-jsdoc */
  34395. /**
  34396. * Remove settings that have not changed, to avoid unnecessary rendering or
  34397. * computing (#9197).
  34398. * @private
  34399. */
  34400. H.cleanRecursively = function (newer, older) {
  34401. var result = {};
  34402. objectEach(newer, function (val, key) {
  34403. var ob;
  34404. // Dive into objects (except DOM nodes)
  34405. if (isObject(newer[key], true) &&
  34406. !newer.nodeType && // #10044
  34407. older[key]) {
  34408. ob = H.cleanRecursively(newer[key], older[key]);
  34409. if (Object.keys(ob).length) {
  34410. result[key] = ob;
  34411. }
  34412. // Arrays, primitives and DOM nodes are copied directly
  34413. }
  34414. else if (isObject(newer[key]) ||
  34415. newer[key] !== older[key]) {
  34416. result[key] = newer[key];
  34417. }
  34418. });
  34419. return result;
  34420. };
  34421. // Extend the Chart prototype for dynamic methods
  34422. extend(Chart.prototype, /** @lends Highcharts.Chart.prototype */ {
  34423. /**
  34424. * Add a series to the chart after render time. Note that this method should
  34425. * never be used when adding data synchronously at chart render time, as it
  34426. * adds expense to the calculations and rendering. When adding data at the
  34427. * same time as the chart is initialized, add the series as a configuration
  34428. * option instead. With multiple axes, the `offset` is dynamically adjusted.
  34429. *
  34430. * @sample highcharts/members/chart-addseries/
  34431. * Add a series from a button
  34432. * @sample stock/members/chart-addseries/
  34433. * Add a series in Highstock
  34434. *
  34435. * @function Highcharts.Chart#addSeries
  34436. *
  34437. * @param {Highcharts.SeriesOptionsType} options
  34438. * The config options for the series.
  34439. *
  34440. * @param {boolean} [redraw=true]
  34441. * Whether to redraw the chart after adding.
  34442. *
  34443. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  34444. * Whether to apply animation, and optionally animation
  34445. * configuration.
  34446. *
  34447. * @return {Highcharts.Series}
  34448. * The newly created series object.
  34449. *
  34450. * @fires Highcharts.Chart#event:addSeries
  34451. * @fires Highcharts.Chart#event:afterAddSeries
  34452. */
  34453. addSeries: function (options, redraw, animation) {
  34454. var series, chart = this;
  34455. if (options) { // <- not necessary
  34456. redraw = pick(redraw, true); // defaults to true
  34457. fireEvent(chart, 'addSeries', { options: options }, function () {
  34458. series = chart.initSeries(options);
  34459. chart.isDirtyLegend = true;
  34460. chart.linkSeries();
  34461. if (series.enabledDataSorting) {
  34462. // We need to call `setData` after `linkSeries`
  34463. series.setData(options.data, false);
  34464. }
  34465. fireEvent(chart, 'afterAddSeries', { series: series });
  34466. if (redraw) {
  34467. chart.redraw(animation);
  34468. }
  34469. });
  34470. }
  34471. return series;
  34472. },
  34473. /**
  34474. * Add an axis to the chart after render time. Note that this method should
  34475. * never be used when adding data synchronously at chart render time, as it
  34476. * adds expense to the calculations and rendering. When adding data at the
  34477. * same time as the chart is initialized, add the axis as a configuration
  34478. * option instead.
  34479. *
  34480. * @sample highcharts/members/chart-addaxis/
  34481. * Add and remove axes
  34482. *
  34483. * @function Highcharts.Chart#addAxis
  34484. *
  34485. * @param {Highcharts.AxisOptions} options
  34486. * The axis options.
  34487. *
  34488. * @param {boolean} [isX=false]
  34489. * Whether it is an X axis or a value axis.
  34490. *
  34491. * @param {boolean} [redraw=true]
  34492. * Whether to redraw the chart after adding.
  34493. *
  34494. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  34495. * Whether and how to apply animation in the redraw.
  34496. *
  34497. * @return {Highcharts.Axis}
  34498. * The newly generated Axis object.
  34499. */
  34500. addAxis: function (options, isX, redraw, animation) {
  34501. return this.createAxis(isX ? 'xAxis' : 'yAxis', { axis: options, redraw: redraw, animation: animation });
  34502. },
  34503. /**
  34504. * Add a color axis to the chart after render time. Note that this method
  34505. * should never be used when adding data synchronously at chart render time,
  34506. * as it adds expense to the calculations and rendering. When adding data at
  34507. * the same time as the chart is initialized, add the axis as a
  34508. * configuration option instead.
  34509. *
  34510. * @sample highcharts/members/chart-addaxis/
  34511. * Add and remove axes
  34512. *
  34513. * @function Highcharts.Chart#addColorAxis
  34514. *
  34515. * @param {Highcharts.ColorAxisOptions} options
  34516. * The axis options.
  34517. *
  34518. * @param {boolean} [redraw=true]
  34519. * Whether to redraw the chart after adding.
  34520. *
  34521. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  34522. * Whether and how to apply animation in the redraw.
  34523. *
  34524. * @return {Highcharts.ColorAxis}
  34525. * The newly generated Axis object.
  34526. */
  34527. addColorAxis: function (options, redraw, animation) {
  34528. return this.createAxis('colorAxis', { axis: options, redraw: redraw, animation: animation });
  34529. },
  34530. /**
  34531. * Factory for creating different axis types.
  34532. *
  34533. * @private
  34534. * @function Highcharts.Chart#createAxis
  34535. *
  34536. * @param {string} type
  34537. * An axis type.
  34538. *
  34539. * @param {...Array<*>} arguments
  34540. * All arguments for the constructor.
  34541. *
  34542. * @return {Highcharts.Axis | Highcharts.ColorAxis}
  34543. * The newly generated Axis object.
  34544. */
  34545. createAxis: function (type, options) {
  34546. var chartOptions = this.options, isColorAxis = type === 'colorAxis', axisOptions = options.axis, redraw = options.redraw, animation = options.animation, userOptions = merge(axisOptions, {
  34547. index: this[type].length,
  34548. isX: type === 'xAxis'
  34549. }), axis;
  34550. if (isColorAxis) {
  34551. axis = new H.ColorAxis(this, userOptions);
  34552. }
  34553. else {
  34554. axis = new Axis(this, userOptions);
  34555. }
  34556. // Push the new axis options to the chart options
  34557. chartOptions[type] = splat(chartOptions[type] || {});
  34558. chartOptions[type].push(userOptions);
  34559. if (isColorAxis) {
  34560. this.isDirtyLegend = true;
  34561. // Clear before 'bindAxes' (#11924)
  34562. this.axes.forEach(function (axis) {
  34563. axis.series = [];
  34564. });
  34565. this.series.forEach(function (series) {
  34566. series.bindAxes();
  34567. series.isDirtyData = true;
  34568. });
  34569. }
  34570. if (pick(redraw, true)) {
  34571. this.redraw(animation);
  34572. }
  34573. return axis;
  34574. },
  34575. /**
  34576. * Dim the chart and show a loading text or symbol. Options for the loading
  34577. * screen are defined in {@link
  34578. * https://api.highcharts.com/highcharts/loading|the loading options}.
  34579. *
  34580. * @sample highcharts/members/chart-hideloading/
  34581. * Show and hide loading from a button
  34582. * @sample highcharts/members/chart-showloading/
  34583. * Apply different text labels
  34584. * @sample stock/members/chart-show-hide-loading/
  34585. * Toggle loading in Highstock
  34586. *
  34587. * @function Highcharts.Chart#showLoading
  34588. *
  34589. * @param {string} [str]
  34590. * An optional text to show in the loading label instead of the
  34591. * default one. The default text is set in
  34592. * [lang.loading](https://api.highcharts.com/highcharts/lang.loading).
  34593. */
  34594. showLoading: function (str) {
  34595. var chart = this, options = chart.options, loadingDiv = chart.loadingDiv, loadingOptions = options.loading, setLoadingSize = function () {
  34596. if (loadingDiv) {
  34597. css(loadingDiv, {
  34598. left: chart.plotLeft + 'px',
  34599. top: chart.plotTop + 'px',
  34600. width: chart.plotWidth + 'px',
  34601. height: chart.plotHeight + 'px'
  34602. });
  34603. }
  34604. };
  34605. // create the layer at the first call
  34606. if (!loadingDiv) {
  34607. chart.loadingDiv = loadingDiv = createElement('div', {
  34608. className: 'highcharts-loading highcharts-loading-hidden'
  34609. }, null, chart.container);
  34610. chart.loadingSpan = createElement('span', { className: 'highcharts-loading-inner' }, null, loadingDiv);
  34611. addEvent(chart, 'redraw', setLoadingSize); // #1080
  34612. }
  34613. loadingDiv.className = 'highcharts-loading';
  34614. // Update text
  34615. chart.loadingSpan.innerHTML =
  34616. pick(str, options.lang.loading, '');
  34617. if (!chart.styledMode) {
  34618. // Update visuals
  34619. css(loadingDiv, extend(loadingOptions.style, {
  34620. zIndex: 10
  34621. }));
  34622. css(chart.loadingSpan, loadingOptions.labelStyle);
  34623. // Show it
  34624. if (!chart.loadingShown) {
  34625. css(loadingDiv, {
  34626. opacity: 0,
  34627. display: ''
  34628. });
  34629. animate(loadingDiv, {
  34630. opacity: loadingOptions.style.opacity || 0.5
  34631. }, {
  34632. duration: loadingOptions.showDuration || 0
  34633. });
  34634. }
  34635. }
  34636. chart.loadingShown = true;
  34637. setLoadingSize();
  34638. },
  34639. /**
  34640. * Hide the loading layer.
  34641. *
  34642. * @see Highcharts.Chart#showLoading
  34643. *
  34644. * @sample highcharts/members/chart-hideloading/
  34645. * Show and hide loading from a button
  34646. * @sample stock/members/chart-show-hide-loading/
  34647. * Toggle loading in Highstock
  34648. *
  34649. * @function Highcharts.Chart#hideLoading
  34650. */
  34651. hideLoading: function () {
  34652. var options = this.options, loadingDiv = this.loadingDiv;
  34653. if (loadingDiv) {
  34654. loadingDiv.className =
  34655. 'highcharts-loading highcharts-loading-hidden';
  34656. if (!this.styledMode) {
  34657. animate(loadingDiv, {
  34658. opacity: 0
  34659. }, {
  34660. duration: options.loading.hideDuration || 100,
  34661. complete: function () {
  34662. css(loadingDiv, { display: 'none' });
  34663. }
  34664. });
  34665. }
  34666. }
  34667. this.loadingShown = false;
  34668. },
  34669. /**
  34670. * These properties cause isDirtyBox to be set to true when updating. Can be
  34671. * extended from plugins.
  34672. */
  34673. propsRequireDirtyBox: [
  34674. 'backgroundColor',
  34675. 'borderColor',
  34676. 'borderWidth',
  34677. 'borderRadius',
  34678. 'plotBackgroundColor',
  34679. 'plotBackgroundImage',
  34680. 'plotBorderColor',
  34681. 'plotBorderWidth',
  34682. 'plotShadow',
  34683. 'shadow'
  34684. ],
  34685. /**
  34686. * These properties require a full reflow of chart elements, best
  34687. * implemented through running `Chart.setSize` internally (#8190).
  34688. * @type {Array}
  34689. */
  34690. propsRequireReflow: [
  34691. 'margin',
  34692. 'marginTop',
  34693. 'marginRight',
  34694. 'marginBottom',
  34695. 'marginLeft',
  34696. 'spacing',
  34697. 'spacingTop',
  34698. 'spacingRight',
  34699. 'spacingBottom',
  34700. 'spacingLeft'
  34701. ],
  34702. /**
  34703. * These properties cause all series to be updated when updating. Can be
  34704. * extended from plugins.
  34705. */
  34706. propsRequireUpdateSeries: [
  34707. 'chart.inverted',
  34708. 'chart.polar',
  34709. 'chart.ignoreHiddenSeries',
  34710. 'chart.type',
  34711. 'colors',
  34712. 'plotOptions',
  34713. 'time',
  34714. 'tooltip'
  34715. ],
  34716. /**
  34717. * These collections (arrays) implement update() methods with support for
  34718. * one-to-one option.
  34719. */
  34720. collectionsWithUpdate: [
  34721. 'xAxis',
  34722. 'yAxis',
  34723. 'zAxis',
  34724. 'series'
  34725. ],
  34726. /**
  34727. * A generic function to update any element of the chart. Elements can be
  34728. * enabled and disabled, moved, re-styled, re-formatted etc.
  34729. *
  34730. * A special case is configuration objects that take arrays, for example
  34731. * [xAxis](https://api.highcharts.com/highcharts/xAxis),
  34732. * [yAxis](https://api.highcharts.com/highcharts/yAxis) or
  34733. * [series](https://api.highcharts.com/highcharts/series). For these
  34734. * collections, an `id` option is used to map the new option set to an
  34735. * existing object. If an existing object of the same id is not found, the
  34736. * corresponding item is updated. So for example, running `chart.update`
  34737. * with a series item without an id, will cause the existing chart's series
  34738. * with the same index in the series array to be updated. When the
  34739. * `oneToOne` parameter is true, `chart.update` will also take care of
  34740. * adding and removing items from the collection. Read more under the
  34741. * parameter description below.
  34742. *
  34743. * Note that when changing series data, `chart.update` may mutate the passed
  34744. * data options.
  34745. *
  34746. * See also the
  34747. * [responsive option set](https://api.highcharts.com/highcharts/responsive).
  34748. * Switching between `responsive.rules` basically runs `chart.update` under
  34749. * the hood.
  34750. *
  34751. * @sample highcharts/members/chart-update/
  34752. * Update chart geometry
  34753. *
  34754. * @function Highcharts.Chart#update
  34755. *
  34756. * @param {Highcharts.Options} options
  34757. * A configuration object for the new chart options.
  34758. *
  34759. * @param {boolean} [redraw=true]
  34760. * Whether to redraw the chart.
  34761. *
  34762. * @param {boolean} [oneToOne=false]
  34763. * When `true`, the `series`, `xAxis`, `yAxis` and `annotations`
  34764. * collections will be updated one to one, and items will be either
  34765. * added or removed to match the new updated options. For example,
  34766. * if the chart has two series and we call `chart.update` with a
  34767. * configuration containing three series, one will be added. If we
  34768. * call `chart.update` with one series, one will be removed. Setting
  34769. * an empty `series` array will remove all series, but leaving out
  34770. * the`series` property will leave all series untouched. If the
  34771. * series have id's, the new series options will be matched by id,
  34772. * and the remaining ones removed.
  34773. *
  34774. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  34775. * Whether to apply animation, and optionally animation
  34776. * configuration.
  34777. *
  34778. * @fires Highcharts.Chart#event:update
  34779. * @fires Highcharts.Chart#event:afterUpdate
  34780. */
  34781. update: function (options, redraw, oneToOne, animation) {
  34782. var chart = this, adders = {
  34783. credits: 'addCredits',
  34784. title: 'setTitle',
  34785. subtitle: 'setSubtitle',
  34786. caption: 'setCaption'
  34787. }, optionsChart, updateAllAxes, updateAllSeries, newWidth, newHeight, runSetSize, isResponsiveOptions = options.isResponsiveOptions, itemsForRemoval = [];
  34788. fireEvent(chart, 'update', { options: options });
  34789. // If there are responsive rules in action, undo the responsive rules
  34790. // before we apply the updated options and replay the responsive rules
  34791. // on top from the chart.redraw function (#9617).
  34792. if (!isResponsiveOptions) {
  34793. chart.setResponsive(false, true);
  34794. }
  34795. options = H.cleanRecursively(options, chart.options);
  34796. merge(true, chart.userOptions, options);
  34797. // If the top-level chart option is present, some special updates are
  34798. // required
  34799. optionsChart = options.chart;
  34800. if (optionsChart) {
  34801. merge(true, chart.options.chart, optionsChart);
  34802. // Setter function
  34803. if ('className' in optionsChart) {
  34804. chart.setClassName(optionsChart.className);
  34805. }
  34806. if ('reflow' in optionsChart) {
  34807. chart.setReflow(optionsChart.reflow);
  34808. }
  34809. if ('inverted' in optionsChart ||
  34810. 'polar' in optionsChart ||
  34811. 'type' in optionsChart) {
  34812. // Parse options.chart.inverted and options.chart.polar together
  34813. // with the available series.
  34814. chart.propFromSeries();
  34815. updateAllAxes = true;
  34816. }
  34817. if ('alignTicks' in optionsChart) { // #6452
  34818. updateAllAxes = true;
  34819. }
  34820. objectEach(optionsChart, function (val, key) {
  34821. if (chart.propsRequireUpdateSeries.indexOf('chart.' + key) !==
  34822. -1) {
  34823. updateAllSeries = true;
  34824. }
  34825. // Only dirty box
  34826. if (chart.propsRequireDirtyBox.indexOf(key) !== -1) {
  34827. chart.isDirtyBox = true;
  34828. }
  34829. // Chart setSize
  34830. if (chart.propsRequireReflow.indexOf(key) !== -1) {
  34831. if (isResponsiveOptions) {
  34832. chart.isDirtyBox = true;
  34833. }
  34834. else {
  34835. runSetSize = true;
  34836. }
  34837. }
  34838. });
  34839. if (!chart.styledMode && 'style' in optionsChart) {
  34840. chart.renderer.setStyle(optionsChart.style);
  34841. }
  34842. }
  34843. // Moved up, because tooltip needs updated plotOptions (#6218)
  34844. if (!chart.styledMode && options.colors) {
  34845. this.options.colors = options.colors;
  34846. }
  34847. if (options.plotOptions) {
  34848. merge(true, this.options.plotOptions, options.plotOptions);
  34849. }
  34850. // Maintaining legacy global time. If the chart is instanciated first
  34851. // with global time, then updated with time options, we need to create a
  34852. // new Time instance to avoid mutating the global time (#10536).
  34853. if (options.time && this.time === time) {
  34854. this.time = new Time(options.time);
  34855. }
  34856. // Some option stuctures correspond one-to-one to chart objects that
  34857. // have update methods, for example
  34858. // options.credits => chart.credits
  34859. // options.legend => chart.legend
  34860. // options.title => chart.title
  34861. // options.tooltip => chart.tooltip
  34862. // options.subtitle => chart.subtitle
  34863. // options.mapNavigation => chart.mapNavigation
  34864. // options.navigator => chart.navigator
  34865. // options.scrollbar => chart.scrollbar
  34866. objectEach(options, function (val, key) {
  34867. if (chart[key] &&
  34868. typeof chart[key].update === 'function') {
  34869. chart[key].update(val, false);
  34870. // If a one-to-one object does not exist, look for an adder function
  34871. }
  34872. else if (typeof chart[adders[key]] === 'function') {
  34873. chart[adders[key]](val);
  34874. }
  34875. if (key !== 'chart' &&
  34876. chart.propsRequireUpdateSeries.indexOf(key) !== -1) {
  34877. updateAllSeries = true;
  34878. }
  34879. });
  34880. // Setters for collections. For axes and series, each item is referred
  34881. // by an id. If the id is not found, it defaults to the corresponding
  34882. // item in the collection, so setting one series without an id, will
  34883. // update the first series in the chart. Setting two series without
  34884. // an id will update the first and the second respectively (#6019)
  34885. // chart.update and responsive.
  34886. this.collectionsWithUpdate.forEach(function (coll) {
  34887. var indexMap;
  34888. if (options[coll]) {
  34889. // In stock charts, the navigator series are also part of the
  34890. // chart.series array, but those series should not be handled
  34891. // here (#8196).
  34892. if (coll === 'series') {
  34893. indexMap = [];
  34894. chart[coll].forEach(function (s, i) {
  34895. if (!s.options.isInternal) {
  34896. indexMap.push(pick(s.options.index, i));
  34897. }
  34898. });
  34899. }
  34900. splat(options[coll]).forEach(function (newOptions, i) {
  34901. var hasId = defined(newOptions.id);
  34902. var item;
  34903. // Match by id
  34904. if (hasId) {
  34905. item = chart.get(newOptions.id);
  34906. }
  34907. // No match by id found, match by index instead
  34908. if (!item) {
  34909. item = chart[coll][indexMap ? indexMap[i] : i];
  34910. // Check if we grabbed an item with an exising but
  34911. // different id (#13541)
  34912. if (item && hasId && defined(item.options.id)) {
  34913. item = void 0;
  34914. }
  34915. }
  34916. if (item && item.coll === coll) {
  34917. item.update(newOptions, false);
  34918. if (oneToOne) {
  34919. item.touched = true;
  34920. }
  34921. }
  34922. // If oneToOne and no matching item is found, add one
  34923. if (!item && oneToOne && chart.collectionsWithInit[coll]) {
  34924. chart.collectionsWithInit[coll][0].apply(chart,
  34925. // [newOptions, ...extraArguments, redraw=false]
  34926. [
  34927. newOptions
  34928. ].concat(
  34929. // Not all initializers require extra args
  34930. chart.collectionsWithInit[coll][1] || []).concat([
  34931. false
  34932. ])).touched = true;
  34933. }
  34934. });
  34935. // Add items for removal
  34936. if (oneToOne) {
  34937. chart[coll].forEach(function (item) {
  34938. if (!item.touched && !item.options.isInternal) {
  34939. itemsForRemoval.push(item);
  34940. }
  34941. else {
  34942. delete item.touched;
  34943. }
  34944. });
  34945. }
  34946. }
  34947. });
  34948. itemsForRemoval.forEach(function (item) {
  34949. if (item.remove) {
  34950. item.remove(false);
  34951. }
  34952. });
  34953. if (updateAllAxes) {
  34954. chart.axes.forEach(function (axis) {
  34955. axis.update({}, false);
  34956. });
  34957. }
  34958. // Certain options require the whole series structure to be thrown away
  34959. // and rebuilt
  34960. if (updateAllSeries) {
  34961. chart.getSeriesOrderByLinks().forEach(function (series) {
  34962. // Avoid removed navigator series
  34963. if (series.chart) {
  34964. series.update({}, false);
  34965. }
  34966. }, this);
  34967. }
  34968. // For loading, just update the options, do not redraw
  34969. if (options.loading) {
  34970. merge(true, chart.options.loading, options.loading);
  34971. }
  34972. // Update size. Redraw is forced.
  34973. newWidth = optionsChart && optionsChart.width;
  34974. newHeight = optionsChart && optionsChart.height;
  34975. if (isString(newHeight)) {
  34976. newHeight = relativeLength(newHeight, newWidth || chart.chartWidth);
  34977. }
  34978. if (
  34979. // In this case, run chart.setSize with newWidth and newHeight which
  34980. // are undefined, only for reflowing chart elements because margin
  34981. // or spacing has been set (#8190)
  34982. runSetSize ||
  34983. // In this case, the size is actually set
  34984. (isNumber(newWidth) && newWidth !== chart.chartWidth) ||
  34985. (isNumber(newHeight) && newHeight !== chart.chartHeight)) {
  34986. chart.setSize(newWidth, newHeight, animation);
  34987. }
  34988. else if (pick(redraw, true)) {
  34989. chart.redraw(animation);
  34990. }
  34991. fireEvent(chart, 'afterUpdate', {
  34992. options: options,
  34993. redraw: redraw,
  34994. animation: animation
  34995. });
  34996. },
  34997. /**
  34998. * Shortcut to set the subtitle options. This can also be done from {@link
  34999. * Chart#update} or {@link Chart#setTitle}.
  35000. *
  35001. * @function Highcharts.Chart#setSubtitle
  35002. *
  35003. * @param {Highcharts.SubtitleOptions} options
  35004. * New subtitle options. The subtitle text itself is set by the
  35005. * `options.text` property.
  35006. */
  35007. setSubtitle: function (options, redraw) {
  35008. this.applyDescription('subtitle', options);
  35009. this.layOutTitles(redraw);
  35010. },
  35011. /**
  35012. * Set the caption options. This can also be done from {@link
  35013. * Chart#update}.
  35014. *
  35015. * @function Highcharts.Chart#setCaption
  35016. *
  35017. * @param {Highcharts.CaptionOptions} options
  35018. * New caption options. The caption text itself is set by the
  35019. * `options.text` property.
  35020. */
  35021. setCaption: function (options, redraw) {
  35022. this.applyDescription('caption', options);
  35023. this.layOutTitles(redraw);
  35024. }
  35025. });
  35026. /**
  35027. * These collections (arrays) implement `Chart.addSomethig` method used in
  35028. * chart.update() to create new object in the collection. Equivalent for
  35029. * deleting is resolved by simple `Somethig.remove()`.
  35030. *
  35031. * Note: We need to define these references after initializers are bound to
  35032. * chart's prototype.
  35033. */
  35034. Chart.prototype.collectionsWithInit = {
  35035. // collectionName: [ initializingMethod, [extraArguments] ]
  35036. xAxis: [Chart.prototype.addAxis, [true]],
  35037. yAxis: [Chart.prototype.addAxis, [false]],
  35038. series: [Chart.prototype.addSeries]
  35039. };
  35040. // extend the Point prototype for dynamic methods
  35041. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  35042. /**
  35043. * Update point with new options (typically x/y data) and optionally redraw
  35044. * the series.
  35045. *
  35046. * @sample highcharts/members/point-update-column/
  35047. * Update column value
  35048. * @sample highcharts/members/point-update-pie/
  35049. * Update pie slice
  35050. * @sample maps/members/point-update/
  35051. * Update map area value in Highmaps
  35052. *
  35053. * @function Highcharts.Point#update
  35054. *
  35055. * @param {Highcharts.PointOptionsType} options
  35056. * The point options. Point options are handled as described under
  35057. * the `series.type.data` item for each series type. For example
  35058. * for a line series, if options is a single number, the point will
  35059. * be given that number as the marin y value. If it is an array, it
  35060. * will be interpreted as x and y values respectively. If it is an
  35061. * object, advanced options are applied.
  35062. *
  35063. * @param {boolean} [redraw=true]
  35064. * Whether to redraw the chart after the point is updated. If doing
  35065. * more operations on the chart, it is best practice to set
  35066. * `redraw` to false and call `chart.redraw()` after.
  35067. *
  35068. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=true]
  35069. * Whether to apply animation, and optionally animation
  35070. * configuration.
  35071. *
  35072. * @return {void}
  35073. *
  35074. * @fires Highcharts.Point#event:update
  35075. */
  35076. update: function (options, redraw, animation, runEvent) {
  35077. var point = this, series = point.series, graphic = point.graphic, i, chart = series.chart, seriesOptions = series.options;
  35078. redraw = pick(redraw, true);
  35079. /**
  35080. * @private
  35081. */
  35082. function update() {
  35083. point.applyOptions(options);
  35084. // Update visuals, #4146
  35085. // Handle dummy graphic elements for a11y, #12718
  35086. var hasDummyGraphic = graphic && point.hasDummyGraphic;
  35087. var shouldDestroyGraphic = point.y === null ? !hasDummyGraphic : hasDummyGraphic;
  35088. if (graphic && shouldDestroyGraphic) {
  35089. point.graphic = graphic.destroy();
  35090. delete point.hasDummyGraphic;
  35091. }
  35092. if (isObject(options, true)) {
  35093. // Destroy so we can get new elements
  35094. if (graphic && graphic.element) {
  35095. // "null" is also a valid symbol
  35096. if (options &&
  35097. options.marker &&
  35098. typeof options.marker.symbol !== 'undefined') {
  35099. point.graphic = graphic.destroy();
  35100. }
  35101. }
  35102. if (options && options.dataLabels && point.dataLabel) {
  35103. point.dataLabel = point.dataLabel.destroy(); // #2468
  35104. }
  35105. if (point.connector) {
  35106. point.connector = point.connector.destroy(); // #7243
  35107. }
  35108. }
  35109. // record changes in the parallel arrays
  35110. i = point.index;
  35111. series.updateParallelArrays(point, i);
  35112. // Record the options to options.data. If the old or the new config
  35113. // is an object, use point options, otherwise use raw options
  35114. // (#4701, #4916).
  35115. seriesOptions.data[i] = (isObject(seriesOptions.data[i], true) ||
  35116. isObject(options, true)) ?
  35117. point.options :
  35118. pick(options, seriesOptions.data[i]);
  35119. // redraw
  35120. series.isDirty = series.isDirtyData = true;
  35121. if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
  35122. chart.isDirtyBox = true;
  35123. }
  35124. if (seriesOptions.legendType === 'point') { // #1831, #1885
  35125. chart.isDirtyLegend = true;
  35126. }
  35127. if (redraw) {
  35128. chart.redraw(animation);
  35129. }
  35130. }
  35131. // Fire the event with a default handler of doing the update
  35132. if (runEvent === false) { // When called from setData
  35133. update();
  35134. }
  35135. else {
  35136. point.firePointEvent('update', { options: options }, update);
  35137. }
  35138. },
  35139. /**
  35140. * Remove a point and optionally redraw the series and if necessary the axes
  35141. *
  35142. * @sample highcharts/plotoptions/series-point-events-remove/
  35143. * Remove point and confirm
  35144. * @sample highcharts/members/point-remove/
  35145. * Remove pie slice
  35146. * @sample maps/members/point-remove/
  35147. * Remove selected points in Highmaps
  35148. *
  35149. * @function Highcharts.Point#remove
  35150. *
  35151. * @param {boolean} [redraw=true]
  35152. * Whether to redraw the chart or wait for an explicit call. When
  35153. * doing more operations on the chart, for example running
  35154. * `point.remove()` in a loop, it is best practice to set `redraw`
  35155. * to false and call `chart.redraw()` after.
  35156. *
  35157. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=false]
  35158. * Whether to apply animation, and optionally animation
  35159. * configuration.
  35160. *
  35161. * @return {void}
  35162. */
  35163. remove: function (redraw, animation) {
  35164. this.series.removePoint(this.series.data.indexOf(this), redraw, animation);
  35165. }
  35166. });
  35167. // Extend the series prototype for dynamic methods
  35168. extend(Series.prototype, /** @lends Series.prototype */ {
  35169. /**
  35170. * Add a point to the series after render time. The point can be added at
  35171. * the end, or by giving it an X value, to the start or in the middle of the
  35172. * series.
  35173. *
  35174. * @sample highcharts/members/series-addpoint-append/
  35175. * Append point
  35176. * @sample highcharts/members/series-addpoint-append-and-shift/
  35177. * Append and shift
  35178. * @sample highcharts/members/series-addpoint-x-and-y/
  35179. * Both X and Y values given
  35180. * @sample highcharts/members/series-addpoint-pie/
  35181. * Append pie slice
  35182. * @sample stock/members/series-addpoint/
  35183. * Append 100 points in Highstock
  35184. * @sample stock/members/series-addpoint-shift/
  35185. * Append and shift in Highstock
  35186. * @sample maps/members/series-addpoint/
  35187. * Add a point in Highmaps
  35188. *
  35189. * @function Highcharts.Series#addPoint
  35190. *
  35191. * @param {Highcharts.PointOptionsType} options
  35192. * The point options. If options is a single number, a point with
  35193. * that y value is appended to the series. If it is an array, it will
  35194. * be interpreted as x and y values respectively. If it is an
  35195. * object, advanced options as outlined under `series.data` are
  35196. * applied.
  35197. *
  35198. * @param {boolean} [redraw=true]
  35199. * Whether to redraw the chart after the point is added. When adding
  35200. * more than one point, it is highly recommended that the redraw
  35201. * option be set to false, and instead {@link Chart#redraw} is
  35202. * explicitly called after the adding of points is finished.
  35203. * Otherwise, the chart will redraw after adding each point.
  35204. *
  35205. * @param {boolean} [shift=false]
  35206. * If true, a point is shifted off the start of the series as one is
  35207. * appended to the end.
  35208. *
  35209. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  35210. * Whether to apply animation, and optionally animation
  35211. * configuration.
  35212. *
  35213. * @param {boolean} [withEvent=true]
  35214. * Used internally, whether to fire the series `addPoint` event.
  35215. *
  35216. * @return {void}
  35217. *
  35218. * @fires Highcharts.Series#event:addPoint
  35219. */
  35220. addPoint: function (options, redraw, shift, animation, withEvent) {
  35221. var series = this, seriesOptions = series.options, data = series.data, chart = series.chart, xAxis = series.xAxis, names = xAxis && xAxis.hasNames && xAxis.names, dataOptions = seriesOptions.data, point, xData = series.xData, isInTheMiddle, i, x;
  35222. // Optional redraw, defaults to true
  35223. redraw = pick(redraw, true);
  35224. // Get options and push the point to xData, yData and series.options. In
  35225. // series.generatePoints the Point instance will be created on demand
  35226. // and pushed to the series.data array.
  35227. point = { series: series };
  35228. series.pointClass.prototype.applyOptions.apply(point, [options]);
  35229. x = point.x;
  35230. // Get the insertion point
  35231. i = xData.length;
  35232. if (series.requireSorting && x < xData[i - 1]) {
  35233. isInTheMiddle = true;
  35234. while (i && xData[i - 1] > x) {
  35235. i--;
  35236. }
  35237. }
  35238. // Insert undefined item
  35239. series.updateParallelArrays(point, 'splice', i, 0, 0);
  35240. // Update it
  35241. series.updateParallelArrays(point, i);
  35242. if (names && point.name) {
  35243. names[x] = point.name;
  35244. }
  35245. dataOptions.splice(i, 0, options);
  35246. if (isInTheMiddle) {
  35247. series.data.splice(i, 0, null);
  35248. series.processData();
  35249. }
  35250. // Generate points to be added to the legend (#1329)
  35251. if (seriesOptions.legendType === 'point') {
  35252. series.generatePoints();
  35253. }
  35254. // Shift the first point off the parallel arrays
  35255. if (shift) {
  35256. if (data[0] && data[0].remove) {
  35257. data[0].remove(false);
  35258. }
  35259. else {
  35260. data.shift();
  35261. series.updateParallelArrays(point, 'shift');
  35262. dataOptions.shift();
  35263. }
  35264. }
  35265. // Fire event
  35266. if (withEvent !== false) {
  35267. fireEvent(series, 'addPoint', { point: point });
  35268. }
  35269. // redraw
  35270. series.isDirty = true;
  35271. series.isDirtyData = true;
  35272. if (redraw) {
  35273. chart.redraw(animation); // Animation is set anyway on redraw, #5665
  35274. }
  35275. },
  35276. /**
  35277. * Remove a point from the series. Unlike the
  35278. * {@link Highcharts.Point#remove} method, this can also be done on a point
  35279. * that is not instanciated because it is outside the view or subject to
  35280. * Highstock data grouping.
  35281. *
  35282. * @sample highcharts/members/series-removepoint/
  35283. * Remove cropped point
  35284. *
  35285. * @function Highcharts.Series#removePoint
  35286. *
  35287. * @param {number} i
  35288. * The index of the point in the {@link Highcharts.Series.data|data}
  35289. * array.
  35290. *
  35291. * @param {boolean} [redraw=true]
  35292. * Whether to redraw the chart after the point is added. When
  35293. * removing more than one point, it is highly recommended that the
  35294. * `redraw` option be set to `false`, and instead {@link
  35295. * Highcharts.Chart#redraw} is explicitly called after the adding of
  35296. * points is finished.
  35297. *
  35298. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  35299. * Whether and optionally how the series should be animated.
  35300. *
  35301. * @return {void}
  35302. *
  35303. * @fires Highcharts.Point#event:remove
  35304. */
  35305. removePoint: function (i, redraw, animation) {
  35306. var series = this, data = series.data, point = data[i], points = series.points, chart = series.chart, remove = function () {
  35307. if (points && points.length === data.length) { // #4935
  35308. points.splice(i, 1);
  35309. }
  35310. data.splice(i, 1);
  35311. series.options.data.splice(i, 1);
  35312. series.updateParallelArrays(point || { series: series }, 'splice', i, 1);
  35313. if (point) {
  35314. point.destroy();
  35315. }
  35316. // redraw
  35317. series.isDirty = true;
  35318. series.isDirtyData = true;
  35319. if (redraw) {
  35320. chart.redraw();
  35321. }
  35322. };
  35323. setAnimation(animation, chart);
  35324. redraw = pick(redraw, true);
  35325. // Fire the event with a default handler of removing the point
  35326. if (point) {
  35327. point.firePointEvent('remove', null, remove);
  35328. }
  35329. else {
  35330. remove();
  35331. }
  35332. },
  35333. /**
  35334. * Remove a series and optionally redraw the chart.
  35335. *
  35336. * @sample highcharts/members/series-remove/
  35337. * Remove first series from a button
  35338. *
  35339. * @function Highcharts.Series#remove
  35340. *
  35341. * @param {boolean} [redraw=true]
  35342. * Whether to redraw the chart or wait for an explicit call to
  35343. * {@link Highcharts.Chart#redraw}.
  35344. *
  35345. * @param {boolean|Highcharts.AnimationOptionsObject} [animation]
  35346. * Whether to apply animation, and optionally animation
  35347. * configuration.
  35348. *
  35349. * @param {boolean} [withEvent=true]
  35350. * Used internally, whether to fire the series `remove` event.
  35351. *
  35352. * @return {void}
  35353. *
  35354. * @fires Highcharts.Series#event:remove
  35355. */
  35356. remove: function (redraw, animation, withEvent, keepEvents) {
  35357. var series = this, chart = series.chart;
  35358. /**
  35359. * @private
  35360. */
  35361. function remove() {
  35362. // Destroy elements
  35363. series.destroy(keepEvents);
  35364. series.remove = null; // Prevent from doing again (#9097)
  35365. // Redraw
  35366. chart.isDirtyLegend = chart.isDirtyBox = true;
  35367. chart.linkSeries();
  35368. if (pick(redraw, true)) {
  35369. chart.redraw(animation);
  35370. }
  35371. }
  35372. // Fire the event with a default handler of removing the point
  35373. if (withEvent !== false) {
  35374. fireEvent(series, 'remove', null, remove);
  35375. }
  35376. else {
  35377. remove();
  35378. }
  35379. },
  35380. /**
  35381. * Update the series with a new set of options. For a clean and precise
  35382. * handling of new options, all methods and elements from the series are
  35383. * removed, and it is initialized from scratch. Therefore, this method is
  35384. * more performance expensive than some other utility methods like {@link
  35385. * Series#setData} or {@link Series#setVisible}.
  35386. *
  35387. * Note that `Series.update` may mutate the passed `data` options.
  35388. *
  35389. * @sample highcharts/members/series-update/
  35390. * Updating series options
  35391. * @sample maps/members/series-update/
  35392. * Update series options in Highmaps
  35393. *
  35394. * @function Highcharts.Series#update
  35395. *
  35396. * @param {Highcharts.SeriesOptionsType} options
  35397. * New options that will be merged with the series' existing options.
  35398. *
  35399. * @param {boolean} [redraw=true]
  35400. * Whether to redraw the chart after the series is altered. If doing
  35401. * more operations on the chart, it is a good idea to set redraw to
  35402. * false and call {@link Chart#redraw} after.
  35403. *
  35404. * @return {void}
  35405. *
  35406. * @fires Highcharts.Series#event:update
  35407. * @fires Highcharts.Series#event:afterUpdate
  35408. */
  35409. update: function (options, redraw) {
  35410. options = H.cleanRecursively(options, this.userOptions);
  35411. fireEvent(this, 'update', { options: options });
  35412. var series = this, chart = series.chart,
  35413. // must use user options when changing type because series.options
  35414. // is merged in with type specific plotOptions
  35415. oldOptions = series.userOptions, seriesOptions, initialType = series.initialType || series.type, newType = (options.type ||
  35416. oldOptions.type ||
  35417. chart.options.chart.type), keepPoints = !(
  35418. // Indicators, histograms etc recalculate the data. It should be
  35419. // possible to omit this.
  35420. this.hasDerivedData ||
  35421. // Changes to data grouping requires new points in new groups
  35422. options.dataGrouping ||
  35423. // New type requires new point classes
  35424. (newType && newType !== this.type) ||
  35425. // New options affecting how the data points are built
  35426. typeof options.pointStart !== 'undefined' ||
  35427. options.pointInterval ||
  35428. options.pointIntervalUnit ||
  35429. options.keys), initialSeriesProto = seriesTypes[initialType].prototype, n, groups = [
  35430. 'group',
  35431. 'markerGroup',
  35432. 'dataLabelsGroup',
  35433. 'transformGroup'
  35434. ], preserve = [
  35435. 'eventOptions',
  35436. 'navigatorSeries',
  35437. 'baseSeries'
  35438. ],
  35439. // Animation must be enabled when calling update before the initial
  35440. // animation has first run. This happens when calling update
  35441. // directly after chart initialization, or when applying responsive
  35442. // rules (#6912).
  35443. animation = series.finishedAnimating && { animation: false }, kinds = {};
  35444. if (keepPoints) {
  35445. preserve.push('data', 'isDirtyData', 'points', 'processedXData', 'processedYData', 'xIncrement', 'cropped', '_hasPointMarkers', '_hasPointLabels',
  35446. // Map specific, consider moving it to series-specific preserve-
  35447. // properties (#10617)
  35448. 'mapMap', 'mapData', 'minY', 'maxY', 'minX', 'maxX');
  35449. if (options.visible !== false) {
  35450. preserve.push('area', 'graph');
  35451. }
  35452. series.parallelArrays.forEach(function (key) {
  35453. preserve.push(key + 'Data');
  35454. });
  35455. if (options.data) {
  35456. // setData uses dataSorting options so we need to update them
  35457. // earlier
  35458. if (options.dataSorting) {
  35459. extend(series.options.dataSorting, options.dataSorting);
  35460. }
  35461. this.setData(options.data, false);
  35462. }
  35463. }
  35464. // Do the merge, with some forced options
  35465. options = merge(oldOptions, animation, {
  35466. // When oldOptions.index is null it should't be cleared.
  35467. // Otherwise navigator series will have wrong indexes (#10193).
  35468. index: typeof oldOptions.index === 'undefined' ?
  35469. series.index : oldOptions.index,
  35470. pointStart: pick(
  35471. // when updating from blank (#7933)
  35472. oldOptions.pointStart,
  35473. // when updating after addPoint
  35474. series.xData[0])
  35475. }, (!keepPoints && { data: series.options.data }), options);
  35476. // Merge does not merge arrays, but replaces them. Since points were
  35477. // updated, `series.options.data` has correct merged options, use it:
  35478. if (keepPoints && options.data) {
  35479. options.data = series.options.data;
  35480. }
  35481. // Make sure preserved properties are not destroyed (#3094)
  35482. preserve = groups.concat(preserve);
  35483. preserve.forEach(function (prop) {
  35484. preserve[prop] = series[prop];
  35485. delete series[prop];
  35486. });
  35487. // Destroy the series and delete all properties. Reinsert all
  35488. // methods and properties from the new type prototype (#2270,
  35489. // #3719).
  35490. series.remove(false, null, false, true);
  35491. for (n in initialSeriesProto) { // eslint-disable-line guard-for-in
  35492. series[n] = void 0;
  35493. }
  35494. if (seriesTypes[newType || initialType]) {
  35495. extend(series, seriesTypes[newType || initialType].prototype);
  35496. }
  35497. else {
  35498. error(17, true, chart, { missingModuleFor: (newType || initialType) });
  35499. }
  35500. // Re-register groups (#3094) and other preserved properties
  35501. preserve.forEach(function (prop) {
  35502. series[prop] = preserve[prop];
  35503. });
  35504. series.init(chart, options);
  35505. // Remove particular elements of the points. Check `series.options`
  35506. // because we need to consider the options being set on plotOptions as
  35507. // well.
  35508. if (keepPoints && this.points) {
  35509. seriesOptions = series.options;
  35510. // What kind of elements to destroy
  35511. if (seriesOptions.visible === false) {
  35512. kinds.graphic = 1;
  35513. kinds.dataLabel = 1;
  35514. }
  35515. else if (!series._hasPointLabels) {
  35516. var marker = seriesOptions.marker, dataLabels = seriesOptions.dataLabels;
  35517. if (marker && (marker.enabled === false ||
  35518. 'symbol' in marker // #10870
  35519. )) {
  35520. kinds.graphic = 1;
  35521. }
  35522. if (dataLabels &&
  35523. dataLabels.enabled === false) {
  35524. kinds.dataLabel = 1;
  35525. }
  35526. }
  35527. this.points.forEach(function (point) {
  35528. if (point && point.series) {
  35529. point.resolveColor();
  35530. // Destroy elements in order to recreate based on updated
  35531. // series options.
  35532. if (Object.keys(kinds).length) {
  35533. point.destroyElements(kinds);
  35534. }
  35535. if (seriesOptions.showInLegend === false &&
  35536. point.legendItem) {
  35537. chart.legend.destroyItem(point);
  35538. }
  35539. }
  35540. }, this);
  35541. }
  35542. series.initialType = initialType;
  35543. chart.linkSeries(); // Links are lost in series.remove (#3028)
  35544. fireEvent(this, 'afterUpdate');
  35545. if (pick(redraw, true)) {
  35546. chart.redraw(keepPoints ? void 0 : false);
  35547. }
  35548. },
  35549. /**
  35550. * Used from within series.update
  35551. *
  35552. * @private
  35553. * @function Highcharts.Series#setName
  35554. *
  35555. * @param {string} name
  35556. *
  35557. * @return {void}
  35558. */
  35559. setName: function (name) {
  35560. this.name = this.options.name = this.userOptions.name = name;
  35561. this.chart.isDirtyLegend = true;
  35562. }
  35563. });
  35564. // Extend the Axis.prototype for dynamic methods
  35565. extend(Axis.prototype, /** @lends Highcharts.Axis.prototype */ {
  35566. /**
  35567. * Update an axis object with a new set of options. The options are merged
  35568. * with the existing options, so only new or altered options need to be
  35569. * specified.
  35570. *
  35571. * @sample highcharts/members/axis-update/
  35572. * Axis update demo
  35573. *
  35574. * @function Highcharts.Axis#update
  35575. *
  35576. * @param {Highcharts.AxisOptions} options
  35577. * The new options that will be merged in with existing options on
  35578. * the axis.
  35579. *
  35580. * @param {boolean} [redraw=true]
  35581. * Whether to redraw the chart after the axis is altered. If doing
  35582. * more operations on the chart, it is a good idea to set redraw to
  35583. * false and call {@link Chart#redraw} after.
  35584. *
  35585. * @return {void}
  35586. */
  35587. update: function (options, redraw) {
  35588. var chart = this.chart, newEvents = ((options && options.events) || {});
  35589. options = merge(this.userOptions, options);
  35590. // Color Axis is not an array,
  35591. // This change is applied in the ColorAxis wrapper
  35592. if (chart.options[this.coll].indexOf) {
  35593. // Don't use this.options.index,
  35594. // StockChart has Axes in navigator too
  35595. chart.options[this.coll][chart.options[this.coll].indexOf(this.userOptions)] = options;
  35596. }
  35597. // Remove old events, if no new exist (#8161)
  35598. objectEach(chart.options[this.coll].events, function (fn, ev) {
  35599. if (typeof newEvents[ev] === 'undefined') {
  35600. newEvents[ev] = void 0;
  35601. }
  35602. });
  35603. this.destroy(true);
  35604. this.init(chart, extend(options, { events: newEvents }));
  35605. chart.isDirtyBox = true;
  35606. if (pick(redraw, true)) {
  35607. chart.redraw();
  35608. }
  35609. },
  35610. /**
  35611. * Remove the axis from the chart.
  35612. *
  35613. * @sample highcharts/members/chart-addaxis/
  35614. * Add and remove axes
  35615. *
  35616. * @function Highcharts.Axis#remove
  35617. *
  35618. * @param {boolean} [redraw=true]
  35619. * Whether to redraw the chart following the remove.
  35620. *
  35621. * @return {void}
  35622. */
  35623. remove: function (redraw) {
  35624. var chart = this.chart, key = this.coll, // xAxis or yAxis
  35625. axisSeries = this.series, i = axisSeries.length;
  35626. // Remove associated series (#2687)
  35627. while (i--) {
  35628. if (axisSeries[i]) {
  35629. axisSeries[i].remove(false);
  35630. }
  35631. }
  35632. // Remove the axis
  35633. erase(chart.axes, this);
  35634. erase(chart[key], this);
  35635. if (isArray(chart.options[key])) {
  35636. chart.options[key].splice(this.options.index, 1);
  35637. }
  35638. else { // color axis, #6488
  35639. delete chart.options[key];
  35640. }
  35641. chart[key].forEach(function (axis, i) {
  35642. // Re-index, #1706, #8075
  35643. axis.options.index = axis.userOptions.index = i;
  35644. });
  35645. this.destroy();
  35646. chart.isDirtyBox = true;
  35647. if (pick(redraw, true)) {
  35648. chart.redraw();
  35649. }
  35650. },
  35651. /**
  35652. * Update the axis title by options after render time.
  35653. *
  35654. * @sample highcharts/members/axis-settitle/
  35655. * Set a new Y axis title
  35656. *
  35657. * @function Highcharts.Axis#setTitle
  35658. *
  35659. * @param {Highcharts.AxisTitleOptions} titleOptions
  35660. * The additional title options.
  35661. *
  35662. * @param {boolean} [redraw=true]
  35663. * Whether to redraw the chart after setting the title.
  35664. *
  35665. * @return {void}
  35666. */
  35667. setTitle: function (titleOptions, redraw) {
  35668. this.update({ title: titleOptions }, redraw);
  35669. },
  35670. /**
  35671. * Set new axis categories and optionally redraw.
  35672. *
  35673. * @sample highcharts/members/axis-setcategories/
  35674. * Set categories by click on a button
  35675. *
  35676. * @function Highcharts.Axis#setCategories
  35677. *
  35678. * @param {Array<string>} categories
  35679. * The new categories.
  35680. *
  35681. * @param {boolean} [redraw=true]
  35682. * Whether to redraw the chart.
  35683. *
  35684. * @return {void}
  35685. */
  35686. setCategories: function (categories, redraw) {
  35687. this.update({ categories: categories }, redraw);
  35688. }
  35689. });
  35690. });
  35691. _registerModule(_modules, 'parts/AreaSeries.js', [_modules['parts/Globals.js'], _modules['parts/Color.js'], _modules['mixins/legend-symbol.js'], _modules['parts/Utilities.js']], function (H, Color, LegendSymbolMixin, U) {
  35692. /* *
  35693. *
  35694. * (c) 2010-2020 Torstein Honsi
  35695. *
  35696. * License: www.highcharts.com/license
  35697. *
  35698. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  35699. *
  35700. * */
  35701. var color = Color.parse;
  35702. var objectEach = U.objectEach, pick = U.pick, seriesType = U.seriesType;
  35703. var Series = H.Series;
  35704. /**
  35705. * Area series type.
  35706. *
  35707. * @private
  35708. * @class
  35709. * @name Highcharts.seriesTypes.area
  35710. *
  35711. * @augments Highcharts.Series
  35712. */
  35713. seriesType('area', 'line',
  35714. /**
  35715. * The area series type.
  35716. *
  35717. * @sample {highcharts} highcharts/demo/area-basic/
  35718. * Area chart
  35719. * @sample {highstock} stock/demo/area/
  35720. * Area chart
  35721. *
  35722. * @extends plotOptions.line
  35723. * @excluding useOhlcData
  35724. * @product highcharts highstock
  35725. * @optionparent plotOptions.area
  35726. */
  35727. {
  35728. /**
  35729. * Fill color or gradient for the area. When `null`, the series' `color`
  35730. * is used with the series' `fillOpacity`.
  35731. *
  35732. * In styled mode, the fill color can be set with the `.highcharts-area`
  35733. * class name.
  35734. *
  35735. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
  35736. * Null by default
  35737. * @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
  35738. * Gradient
  35739. *
  35740. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35741. * @product highcharts highstock
  35742. * @apioption plotOptions.area.fillColor
  35743. */
  35744. /**
  35745. * Fill opacity for the area. When you set an explicit `fillColor`,
  35746. * the `fillOpacity` is not applied. Instead, you should define the
  35747. * opacity in the `fillColor` with an rgba color definition. The
  35748. * `fillOpacity` setting, also the default setting, overrides the alpha
  35749. * component of the `color` setting.
  35750. *
  35751. * In styled mode, the fill opacity can be set with the
  35752. * `.highcharts-area` class name.
  35753. *
  35754. * @sample {highcharts} highcharts/plotoptions/area-fillopacity/
  35755. * Automatic fill color and fill opacity of 0.1
  35756. *
  35757. * @type {number}
  35758. * @default {highcharts} 0.75
  35759. * @default {highstock} 0.75
  35760. * @product highcharts highstock
  35761. * @apioption plotOptions.area.fillOpacity
  35762. */
  35763. /**
  35764. * A separate color for the graph line. By default the line takes the
  35765. * `color` of the series, but the lineColor setting allows setting a
  35766. * separate color for the line without altering the `fillColor`.
  35767. *
  35768. * In styled mode, the line stroke can be set with the
  35769. * `.highcharts-graph` class name.
  35770. *
  35771. * @sample {highcharts} highcharts/plotoptions/area-linecolor/
  35772. * Dark gray line
  35773. *
  35774. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35775. * @product highcharts highstock
  35776. * @apioption plotOptions.area.lineColor
  35777. */
  35778. /**
  35779. * A separate color for the negative part of the area.
  35780. *
  35781. * In styled mode, a negative color is set with the
  35782. * `.highcharts-negative` class name.
  35783. *
  35784. * @see [negativeColor](#plotOptions.area.negativeColor)
  35785. *
  35786. * @sample {highcharts} highcharts/css/series-negative-color/
  35787. * Negative color in styled mode
  35788. *
  35789. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  35790. * @since 3.0
  35791. * @product highcharts
  35792. * @apioption plotOptions.area.negativeFillColor
  35793. */
  35794. /**
  35795. * Whether the whole area or just the line should respond to mouseover
  35796. * tooltips and other mouse or touch events.
  35797. *
  35798. * @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
  35799. * Display the tooltip when the area is hovered
  35800. *
  35801. * @type {boolean}
  35802. * @default false
  35803. * @since 1.1.6
  35804. * @product highcharts highstock
  35805. * @apioption plotOptions.area.trackByArea
  35806. */
  35807. /**
  35808. * When this is true, the series will not cause the Y axis to cross
  35809. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  35810. * unless the data actually crosses the plane.
  35811. *
  35812. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  35813. * 3 will make the Y axis show negative values according to the
  35814. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  35815. * at 0.
  35816. *
  35817. * @since 4.1.9
  35818. * @product highcharts highstock
  35819. */
  35820. softThreshold: false,
  35821. /**
  35822. * The Y axis value to serve as the base for the area, for
  35823. * distinguishing between values above and below a threshold. The area
  35824. * between the graph and the threshold is filled.
  35825. *
  35826. * * If a number is given, the Y axis will scale to the threshold.
  35827. * * If `null`, the scaling behaves like a line series with fill between
  35828. * the graph and the Y axis minimum.
  35829. * * If `Infinity` or `-Infinity`, the area between the graph and the
  35830. * corresponding Y axis extreme is filled (since v6.1.0).
  35831. *
  35832. * @sample {highcharts} highcharts/plotoptions/area-threshold/
  35833. * A threshold of 100
  35834. * @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
  35835. * A threshold of Infinity
  35836. *
  35837. * @type {number|null}
  35838. * @since 2.0
  35839. * @product highcharts highstock
  35840. */
  35841. threshold: 0
  35842. },
  35843. /* eslint-disable valid-jsdoc */
  35844. /**
  35845. * @lends seriesTypes.area.prototype
  35846. */
  35847. {
  35848. singleStacks: false,
  35849. /**
  35850. * Return an array of stacked points, where null and missing points are
  35851. * replaced by dummy points in order for gaps to be drawn correctly in
  35852. * stacks.
  35853. * @private
  35854. */
  35855. getStackPoints: function (points) {
  35856. var series = this, segment = [], keys = [], xAxis = this.xAxis, yAxis = this.yAxis, stack = yAxis.stacking.stacks[this.stackKey], pointMap = {}, seriesIndex = series.index, yAxisSeries = yAxis.series, seriesLength = yAxisSeries.length, visibleSeries, upOrDown = pick(yAxis.options.reversedStacks, true) ? 1 : -1, i;
  35857. points = points || this.points;
  35858. if (this.options.stacking) {
  35859. for (i = 0; i < points.length; i++) {
  35860. // Reset after point update (#7326)
  35861. points[i].leftNull = points[i].rightNull = void 0;
  35862. // Create a map where we can quickly look up the points by
  35863. // their X values.
  35864. pointMap[points[i].x] = points[i];
  35865. }
  35866. // Sort the keys (#1651)
  35867. objectEach(stack, function (stackX, x) {
  35868. // nulled after switching between
  35869. // grouping and not (#1651, #2336)
  35870. if (stackX.total !== null) {
  35871. keys.push(x);
  35872. }
  35873. });
  35874. keys.sort(function (a, b) {
  35875. return a - b;
  35876. });
  35877. visibleSeries = yAxisSeries.map(function (s) {
  35878. return s.visible;
  35879. });
  35880. keys.forEach(function (x, idx) {
  35881. var y = 0, stackPoint, stackedValues;
  35882. if (pointMap[x] && !pointMap[x].isNull) {
  35883. segment.push(pointMap[x]);
  35884. // Find left and right cliff. -1 goes left, 1 goes
  35885. // right.
  35886. [-1, 1].forEach(function (direction) {
  35887. var nullName = direction === 1 ?
  35888. 'rightNull' :
  35889. 'leftNull', cliffName = direction === 1 ?
  35890. 'rightCliff' :
  35891. 'leftCliff', cliff = 0, otherStack = stack[keys[idx + direction]];
  35892. // If there is a stack next to this one,
  35893. // to the left or to the right...
  35894. if (otherStack) {
  35895. i = seriesIndex;
  35896. // Can go either up or down,
  35897. // depending on reversedStacks
  35898. while (i >= 0 && i < seriesLength) {
  35899. stackPoint = otherStack.points[i];
  35900. if (!stackPoint) {
  35901. // If the next point in this series
  35902. // is missing, mark the point
  35903. // with point.leftNull or
  35904. // point.rightNull = true.
  35905. if (i === seriesIndex) {
  35906. pointMap[x][nullName] =
  35907. true;
  35908. // If there are missing points in
  35909. // the next stack in any of the
  35910. // series below this one, we need
  35911. // to substract the missing values
  35912. // and add a hiatus to the left or
  35913. // right.
  35914. }
  35915. else if (visibleSeries[i]) {
  35916. stackedValues =
  35917. stack[x].points[i];
  35918. if (stackedValues) {
  35919. cliff -=
  35920. stackedValues[1] -
  35921. stackedValues[0];
  35922. }
  35923. }
  35924. }
  35925. // When reversedStacks is true, loop up,
  35926. // else loop down
  35927. i += upOrDown;
  35928. }
  35929. }
  35930. pointMap[x][cliffName] = cliff;
  35931. });
  35932. // There is no point for this X value in this series, so we
  35933. // insert a dummy point in order for the areas to be drawn
  35934. // correctly.
  35935. }
  35936. else {
  35937. // Loop down the stack to find the series below this
  35938. // one that has a value (#1991)
  35939. i = seriesIndex;
  35940. while (i >= 0 && i < seriesLength) {
  35941. stackPoint = stack[x].points[i];
  35942. if (stackPoint) {
  35943. y = stackPoint[1];
  35944. break;
  35945. }
  35946. // When reversedStacks is true, loop up, else loop
  35947. // down
  35948. i += upOrDown;
  35949. }
  35950. y = yAxis.translate(// #6272
  35951. y, 0, 1, 0, 1);
  35952. segment.push({
  35953. isNull: true,
  35954. plotX: xAxis.translate(// #6272
  35955. x, 0, 0, 0, 1),
  35956. x: x,
  35957. plotY: y,
  35958. yBottom: y
  35959. });
  35960. }
  35961. });
  35962. }
  35963. return segment;
  35964. },
  35965. /**
  35966. * @private
  35967. */
  35968. getGraphPath: function (points) {
  35969. var getGraphPath = Series.prototype.getGraphPath, graphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, topPath, bottomPath, bottomPoints = [], graphPoints = [], seriesIndex = this.index, i, areaPath, plotX, stacks = yAxis.stacking.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
  35970. yAxis.getThreshold(options.threshold)), isNull, yBottom, connectNulls = pick(// #10574
  35971. options.connectNulls, stacking === 'percent'),
  35972. // To display null points in underlying stacked series, this
  35973. // series graph must be broken, and the area also fall down to
  35974. // fill the gap left by the null point. #2069
  35975. addDummyPoints = function (i, otherI, side) {
  35976. var point = points[i], stackedValues = stacking &&
  35977. stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0, top, bottom, isNull = true;
  35978. if (cliffVal || nullVal) {
  35979. top = (nullVal ?
  35980. stackedValues[0] :
  35981. stackedValues[1]) + cliffVal;
  35982. bottom = stackedValues[0] + cliffVal;
  35983. isNull = !!nullVal;
  35984. }
  35985. else if (!stacking &&
  35986. points[otherI] &&
  35987. points[otherI].isNull) {
  35988. top = bottom = threshold;
  35989. }
  35990. // Add to the top and bottom line of the area
  35991. if (typeof top !== 'undefined') {
  35992. graphPoints.push({
  35993. plotX: plotX,
  35994. plotY: top === null ?
  35995. translatedThreshold :
  35996. yAxis.getThreshold(top),
  35997. isNull: isNull,
  35998. isCliff: true
  35999. });
  36000. bottomPoints.push({
  36001. plotX: plotX,
  36002. plotY: bottom === null ?
  36003. translatedThreshold :
  36004. yAxis.getThreshold(bottom),
  36005. doCurve: false // #1041, gaps in areaspline areas
  36006. });
  36007. }
  36008. };
  36009. // Find what points to use
  36010. points = points || this.points;
  36011. // Fill in missing points
  36012. if (stacking) {
  36013. points = this.getStackPoints(points);
  36014. }
  36015. for (i = 0; i < points.length; i++) {
  36016. // Reset after series.update of stacking property (#12033)
  36017. if (!stacking) {
  36018. points[i].leftCliff = points[i].rightCliff =
  36019. points[i].leftNull = points[i].rightNull = void 0;
  36020. }
  36021. isNull = points[i].isNull;
  36022. plotX = pick(points[i].rectPlotX, points[i].plotX);
  36023. yBottom = pick(points[i].yBottom, translatedThreshold);
  36024. if (!isNull || connectNulls) {
  36025. if (!connectNulls) {
  36026. addDummyPoints(i, i - 1, 'left');
  36027. }
  36028. // Skip null point when stacking is false and connectNulls
  36029. // true
  36030. if (!(isNull && !stacking && connectNulls)) {
  36031. graphPoints.push(points[i]);
  36032. bottomPoints.push({
  36033. x: i,
  36034. plotX: plotX,
  36035. plotY: yBottom
  36036. });
  36037. }
  36038. if (!connectNulls) {
  36039. addDummyPoints(i, i + 1, 'right');
  36040. }
  36041. }
  36042. }
  36043. topPath = getGraphPath.call(this, graphPoints, true, true);
  36044. bottomPoints.reversed = true;
  36045. bottomPath = getGraphPath.call(this, bottomPoints, true, true);
  36046. var firstBottomPoint = bottomPath[0];
  36047. if (firstBottomPoint && firstBottomPoint[0] === 'M') {
  36048. bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]];
  36049. }
  36050. areaPath = topPath.concat(bottomPath);
  36051. // TODO: don't set leftCliff and rightCliff when connectNulls?
  36052. graphPath = getGraphPath
  36053. .call(this, graphPoints, false, connectNulls);
  36054. areaPath.xMap = topPath.xMap;
  36055. this.areaPath = areaPath;
  36056. return graphPath;
  36057. },
  36058. /**
  36059. * Draw the graph and the underlying area. This method calls the Series
  36060. * base function and adds the area. The areaPath is calculated in the
  36061. * getSegmentPath method called from Series.prototype.drawGraph.
  36062. * @private
  36063. */
  36064. drawGraph: function () {
  36065. // Define or reset areaPath
  36066. this.areaPath = [];
  36067. // Call the base method
  36068. Series.prototype.drawGraph.apply(this);
  36069. // Define local variables
  36070. var series = this, areaPath = this.areaPath, options = this.options, zones = this.zones, props = [[
  36071. 'area',
  36072. 'highcharts-area',
  36073. this.color,
  36074. options.fillColor
  36075. ]]; // area name, main color, fill color
  36076. zones.forEach(function (zone, i) {
  36077. props.push([
  36078. 'zone-area-' + i,
  36079. 'highcharts-area highcharts-zone-area-' + i + ' ' +
  36080. zone.className,
  36081. zone.color || series.color,
  36082. zone.fillColor || options.fillColor
  36083. ]);
  36084. });
  36085. props.forEach(function (prop) {
  36086. var areaKey = prop[0], area = series[areaKey], verb = area ? 'animate' : 'attr', attribs = {};
  36087. // Create or update the area
  36088. if (area) { // update
  36089. area.endX = series.preventGraphAnimation ?
  36090. null :
  36091. areaPath.xMap;
  36092. area.animate({ d: areaPath });
  36093. }
  36094. else { // create
  36095. attribs.zIndex = 0; // #1069
  36096. area = series[areaKey] = series.chart.renderer
  36097. .path(areaPath)
  36098. .addClass(prop[1])
  36099. .add(series.group);
  36100. area.isArea = true;
  36101. }
  36102. if (!series.chart.styledMode) {
  36103. attribs.fill = pick(prop[3], color(prop[2])
  36104. .setOpacity(pick(options.fillOpacity, 0.75))
  36105. .get());
  36106. }
  36107. area[verb](attribs);
  36108. area.startX = areaPath.xMap;
  36109. area.shiftUnit = options.step ? 2 : 1;
  36110. });
  36111. },
  36112. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  36113. });
  36114. /* eslint-enable valid-jsdoc */
  36115. /**
  36116. * A `area` series. If the [type](#series.area.type) option is not
  36117. * specified, it is inherited from [chart.type](#chart.type).
  36118. *
  36119. * @extends series,plotOptions.area
  36120. * @excluding dataParser, dataURL, useOhlcData
  36121. * @product highcharts highstock
  36122. * @apioption series.area
  36123. */
  36124. /**
  36125. * An array of data points for the series. For the `area` series type,
  36126. * points can be given in the following ways:
  36127. *
  36128. * 1. An array of numerical values. In this case, the numerical values will be
  36129. * interpreted as `y` options. The `x` values will be automatically
  36130. * calculated, either starting at 0 and incremented by 1, or from
  36131. * `pointStart` * and `pointInterval` given in the series options. If the
  36132. * axis has categories, these will be used. Example:
  36133. * ```js
  36134. * data: [0, 5, 3, 5]
  36135. * ```
  36136. *
  36137. * 2. An array of arrays with 2 values. In this case, the values correspond to
  36138. * `x,y`. If the first value is a string, it is applied as the name of the
  36139. * point, and the `x` value is inferred.
  36140. * ```js
  36141. * data: [
  36142. * [0, 9],
  36143. * [1, 7],
  36144. * [2, 6]
  36145. * ]
  36146. * ```
  36147. *
  36148. * 3. An array of objects with named values. The following snippet shows only a
  36149. * few settings, see the complete options set below. If the total number of
  36150. * data points exceeds the series'
  36151. * [turboThreshold](#series.area.turboThreshold), this option is not
  36152. * available.
  36153. * ```js
  36154. * data: [{
  36155. * x: 1,
  36156. * y: 9,
  36157. * name: "Point2",
  36158. * color: "#00FF00"
  36159. * }, {
  36160. * x: 1,
  36161. * y: 6,
  36162. * name: "Point1",
  36163. * color: "#FF00FF"
  36164. * }]
  36165. * ```
  36166. *
  36167. * @sample {highcharts} highcharts/chart/reflow-true/
  36168. * Numerical values
  36169. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  36170. * Arrays of numeric x and y
  36171. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  36172. * Arrays of datetime x and y
  36173. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  36174. * Arrays of point.name and y
  36175. * @sample {highcharts} highcharts/series/data-array-of-objects/
  36176. * Config objects
  36177. *
  36178. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  36179. * @extends series.line.data
  36180. * @product highcharts highstock
  36181. * @apioption series.area.data
  36182. */
  36183. ''; // adds doclets above to transpilat
  36184. });
  36185. _registerModule(_modules, 'parts/SplineSeries.js', [_modules['parts/Utilities.js']], function (U) {
  36186. /* *
  36187. *
  36188. * (c) 2010-2020 Torstein Honsi
  36189. *
  36190. * License: www.highcharts.com/license
  36191. *
  36192. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  36193. *
  36194. * */
  36195. var pick = U.pick, seriesType = U.seriesType;
  36196. /**
  36197. * Spline series type.
  36198. *
  36199. * @private
  36200. * @class
  36201. * @name Highcharts.seriesTypes.spline
  36202. *
  36203. * @augments Highcarts.Series
  36204. */
  36205. seriesType('spline', 'line',
  36206. /**
  36207. * A spline series is a special type of line series, where the segments
  36208. * between the data points are smoothed.
  36209. *
  36210. * @sample {highcharts} highcharts/demo/spline-irregular-time/
  36211. * Spline chart
  36212. * @sample {highstock} stock/demo/spline/
  36213. * Spline chart
  36214. *
  36215. * @extends plotOptions.series
  36216. * @excluding step
  36217. * @product highcharts highstock
  36218. * @optionparent plotOptions.spline
  36219. */
  36220. {},
  36221. /**
  36222. * @lends seriesTypes.spline.prototype
  36223. */
  36224. {
  36225. /* eslint-disable valid-jsdoc */
  36226. /**
  36227. * Get the spline segment from a given point's previous neighbour to the
  36228. * given point.
  36229. *
  36230. * @private
  36231. * @function Highcharts.seriesTypes.spline#getPointSpline
  36232. *
  36233. * @param {Array<Highcharts.Point>}
  36234. *
  36235. * @param {Highcharts.Point} point
  36236. *
  36237. * @param {number} i
  36238. *
  36239. * @return {Highcharts.SVGPathArray}
  36240. */
  36241. getPointSpline: function (points, point, i) {
  36242. var
  36243. // 1 means control points midway between points, 2 means 1/3
  36244. // from the point, 3 is 1/4 etc
  36245. smoothing = 1.5, denom = smoothing + 1, plotX = point.plotX || 0, plotY = point.plotY || 0, lastPoint = points[i - 1], nextPoint = points[i + 1], leftContX, leftContY, rightContX, rightContY, ret;
  36246. /**
  36247. * @private
  36248. */
  36249. function doCurve(otherPoint) {
  36250. return otherPoint &&
  36251. !otherPoint.isNull &&
  36252. otherPoint.doCurve !== false &&
  36253. // #6387, area splines next to null:
  36254. !point.isCliff;
  36255. }
  36256. // Find control points
  36257. if (doCurve(lastPoint) && doCurve(nextPoint)) {
  36258. var lastX = lastPoint.plotX || 0, lastY = lastPoint.plotY || 0, nextX = nextPoint.plotX || 0, nextY = nextPoint.plotY || 0, correction = 0;
  36259. leftContX = (smoothing * plotX + lastX) / denom;
  36260. leftContY = (smoothing * plotY + lastY) / denom;
  36261. rightContX = (smoothing * plotX + nextX) / denom;
  36262. rightContY = (smoothing * plotY + nextY) / denom;
  36263. // Have the two control points make a straight line through main
  36264. // point
  36265. if (rightContX !== leftContX) { // #5016, division by zero
  36266. correction = (((rightContY - leftContY) *
  36267. (rightContX - plotX)) /
  36268. (rightContX - leftContX) + plotY - rightContY);
  36269. }
  36270. leftContY += correction;
  36271. rightContY += correction;
  36272. // to prevent false extremes, check that control points are
  36273. // between neighbouring points' y values
  36274. if (leftContY > lastY && leftContY > plotY) {
  36275. leftContY = Math.max(lastY, plotY);
  36276. // mirror of left control point
  36277. rightContY = 2 * plotY - leftContY;
  36278. }
  36279. else if (leftContY < lastY && leftContY < plotY) {
  36280. leftContY = Math.min(lastY, plotY);
  36281. rightContY = 2 * plotY - leftContY;
  36282. }
  36283. if (rightContY > nextY && rightContY > plotY) {
  36284. rightContY = Math.max(nextY, plotY);
  36285. leftContY = 2 * plotY - rightContY;
  36286. }
  36287. else if (rightContY < nextY && rightContY < plotY) {
  36288. rightContY = Math.min(nextY, plotY);
  36289. leftContY = 2 * plotY - rightContY;
  36290. }
  36291. // record for drawing in next point
  36292. point.rightContX = rightContX;
  36293. point.rightContY = rightContY;
  36294. }
  36295. // Visualize control points for debugging
  36296. /*
  36297. if (leftContX) {
  36298. this.chart.renderer.circle(
  36299. leftContX + this.chart.plotLeft,
  36300. leftContY + this.chart.plotTop,
  36301. 2
  36302. )
  36303. .attr({
  36304. stroke: 'red',
  36305. 'stroke-width': 2,
  36306. fill: 'none',
  36307. zIndex: 9
  36308. })
  36309. .add();
  36310. this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
  36311. leftContY + this.chart.plotTop,
  36312. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  36313. .attr({
  36314. stroke: 'red',
  36315. 'stroke-width': 2,
  36316. zIndex: 9
  36317. })
  36318. .add();
  36319. }
  36320. if (rightContX) {
  36321. this.chart.renderer.circle(
  36322. rightContX + this.chart.plotLeft,
  36323. rightContY + this.chart.plotTop,
  36324. 2
  36325. )
  36326. .attr({
  36327. stroke: 'green',
  36328. 'stroke-width': 2,
  36329. fill: 'none',
  36330. zIndex: 9
  36331. })
  36332. .add();
  36333. this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
  36334. rightContY + this.chart.plotTop,
  36335. 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
  36336. .attr({
  36337. stroke: 'green',
  36338. 'stroke-width': 2,
  36339. zIndex: 9
  36340. })
  36341. .add();
  36342. }
  36343. // */
  36344. ret = [
  36345. 'C',
  36346. pick(lastPoint.rightContX, lastPoint.plotX, 0),
  36347. pick(lastPoint.rightContY, lastPoint.plotY, 0),
  36348. pick(leftContX, plotX, 0),
  36349. pick(leftContY, plotY, 0),
  36350. plotX,
  36351. plotY
  36352. ];
  36353. // reset for updating series later
  36354. lastPoint.rightContX = lastPoint.rightContY = void 0;
  36355. return ret;
  36356. }
  36357. /* eslint-enable valid-jsdoc */
  36358. });
  36359. /**
  36360. * A `spline` series. If the [type](#series.spline.type) option is
  36361. * not specified, it is inherited from [chart.type](#chart.type).
  36362. *
  36363. * @extends series,plotOptions.spline
  36364. * @excluding dataParser, dataURL, step
  36365. * @product highcharts highstock
  36366. * @apioption series.spline
  36367. */
  36368. /**
  36369. * An array of data points for the series. For the `spline` series type,
  36370. * points can be given in the following ways:
  36371. *
  36372. * 1. An array of numerical values. In this case, the numerical values will be
  36373. * interpreted as `y` options. The `x` values will be automatically
  36374. * calculated, either starting at 0 and incremented by 1, or from
  36375. * `pointStart` and `pointInterval` given in the series options. If the axis
  36376. * has categories, these will be used. Example:
  36377. * ```js
  36378. * data: [0, 5, 3, 5]
  36379. * ```
  36380. *
  36381. * 2. An array of arrays with 2 values. In this case, the values correspond to
  36382. * `x,y`. If the first value is a string, it is applied as the name of the
  36383. * point, and the `x` value is inferred.
  36384. * ```js
  36385. * data: [
  36386. * [0, 9],
  36387. * [1, 2],
  36388. * [2, 8]
  36389. * ]
  36390. * ```
  36391. *
  36392. * 3. An array of objects with named values. The following snippet shows only a
  36393. * few settings, see the complete options set below. If the total number of
  36394. * data points exceeds the series'
  36395. * [turboThreshold](#series.spline.turboThreshold),
  36396. * this option is not available.
  36397. * ```js
  36398. * data: [{
  36399. * x: 1,
  36400. * y: 9,
  36401. * name: "Point2",
  36402. * color: "#00FF00"
  36403. * }, {
  36404. * x: 1,
  36405. * y: 0,
  36406. * name: "Point1",
  36407. * color: "#FF00FF"
  36408. * }]
  36409. * ```
  36410. *
  36411. * @sample {highcharts} highcharts/chart/reflow-true/
  36412. * Numerical values
  36413. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  36414. * Arrays of numeric x and y
  36415. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  36416. * Arrays of datetime x and y
  36417. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  36418. * Arrays of point.name and y
  36419. * @sample {highcharts} highcharts/series/data-array-of-objects/
  36420. * Config objects
  36421. *
  36422. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  36423. * @extends series.line.data
  36424. * @product highcharts highstock
  36425. * @apioption series.spline.data
  36426. */
  36427. ''; // adds doclets above intro transpilat
  36428. });
  36429. _registerModule(_modules, 'parts/AreaSplineSeries.js', [_modules['parts/Globals.js'], _modules['mixins/legend-symbol.js'], _modules['parts/Options.js'], _modules['parts/Utilities.js']], function (H, LegendSymbolMixin, O, U) {
  36430. /* *
  36431. *
  36432. * (c) 2010-2020 Torstein Honsi
  36433. *
  36434. * License: www.highcharts.com/license
  36435. *
  36436. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  36437. *
  36438. * */
  36439. var defaultOptions = O.defaultOptions;
  36440. var seriesType = U.seriesType;
  36441. var areaProto = H.seriesTypes.area.prototype;
  36442. /**
  36443. * AreaSpline series type.
  36444. *
  36445. * @private
  36446. * @class
  36447. * @name Highcharts.seriesTypes.areaspline
  36448. *
  36449. * @augments Highcharts.Series
  36450. */
  36451. seriesType('areaspline', 'spline',
  36452. /**
  36453. * The area spline series is an area series where the graph between the
  36454. * points is smoothed into a spline.
  36455. *
  36456. * @sample {highcharts} highcharts/demo/areaspline/
  36457. * Area spline chart
  36458. * @sample {highstock} stock/demo/areaspline/
  36459. * Area spline chart
  36460. *
  36461. * @extends plotOptions.area
  36462. * @excluding step
  36463. * @product highcharts highstock
  36464. * @apioption plotOptions.areaspline
  36465. */
  36466. defaultOptions.plotOptions.area, {
  36467. getStackPoints: areaProto.getStackPoints,
  36468. getGraphPath: areaProto.getGraphPath,
  36469. drawGraph: areaProto.drawGraph,
  36470. drawLegendSymbol: LegendSymbolMixin.drawRectangle
  36471. });
  36472. /**
  36473. * A `areaspline` series. If the [type](#series.areaspline.type) option
  36474. * is not specified, it is inherited from [chart.type](#chart.type).
  36475. *
  36476. *
  36477. * @extends series,plotOptions.areaspline
  36478. * @excluding dataParser, dataURL, step
  36479. * @product highcharts highstock
  36480. * @apioption series.areaspline
  36481. */
  36482. /**
  36483. * An array of data points for the series. For the `areaspline` series
  36484. * type, points can be given in the following ways:
  36485. *
  36486. * 1. An array of numerical values. In this case, the numerical values will be
  36487. * interpreted as `y` options. The `x` values will be automatically
  36488. * calculated, either starting at 0 and incremented by 1, or from
  36489. * `pointStart` and `pointInterval` given in the series options. If the axis
  36490. * has categories, these will be used. Example:
  36491. * ```js
  36492. * data: [0, 5, 3, 5]
  36493. * ```
  36494. *
  36495. * 2. An array of arrays with 2 values. In this case, the values correspond to
  36496. * `x,y`. If the first value is a string, it is applied as the name of the
  36497. * point, and the `x` value is inferred.
  36498. * ```js
  36499. * data: [
  36500. * [0, 10],
  36501. * [1, 9],
  36502. * [2, 3]
  36503. * ]
  36504. * ```
  36505. *
  36506. * 3. An array of objects with named values. The following snippet shows only a
  36507. * few settings, see the complete options set below. If the total number of
  36508. * data points exceeds the series'
  36509. * [turboThreshold](#series.areaspline.turboThreshold), this option is not
  36510. * available.
  36511. * ```js
  36512. * data: [{
  36513. * x: 1,
  36514. * y: 4,
  36515. * name: "Point2",
  36516. * color: "#00FF00"
  36517. * }, {
  36518. * x: 1,
  36519. * y: 4,
  36520. * name: "Point1",
  36521. * color: "#FF00FF"
  36522. * }]
  36523. * ```
  36524. *
  36525. * @sample {highcharts} highcharts/chart/reflow-true/
  36526. * Numerical values
  36527. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  36528. * Arrays of numeric x and y
  36529. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  36530. * Arrays of datetime x and y
  36531. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  36532. * Arrays of point.name and y
  36533. * @sample {highcharts} highcharts/series/data-array-of-objects/
  36534. * Config objects
  36535. *
  36536. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  36537. * @extends series.line.data
  36538. * @product highcharts highstock
  36539. * @apioption series.areaspline.data
  36540. */
  36541. ''; // adds doclets above into transpilat
  36542. });
  36543. _registerModule(_modules, 'parts/ColumnSeries.js', [_modules['parts/Globals.js'], _modules['parts/Color.js'], _modules['mixins/legend-symbol.js'], _modules['parts/Utilities.js']], function (H, Color, LegendSymbolMixin, U) {
  36544. /* *
  36545. *
  36546. * (c) 2010-2020 Torstein Honsi
  36547. *
  36548. * License: www.highcharts.com/license
  36549. *
  36550. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  36551. *
  36552. * */
  36553. /**
  36554. * Adjusted width and x offset of the columns for grouping.
  36555. *
  36556. * @private
  36557. * @interface Highcharts.ColumnMetricsObject
  36558. */ /**
  36559. * Width of the columns.
  36560. * @name Highcharts.ColumnMetricsObject#width
  36561. * @type {number}
  36562. */ /**
  36563. * Offset of the columns.
  36564. * @name Highcharts.ColumnMetricsObject#offset
  36565. * @type {number}
  36566. */
  36567. ''; // detach doclets above
  36568. var color = Color.parse;
  36569. var animObject = U.animObject, clamp = U.clamp, defined = U.defined, extend = U.extend, isNumber = U.isNumber, merge = U.merge, pick = U.pick, seriesType = U.seriesType;
  36570. var noop = H.noop, Series = H.Series, svg = H.svg;
  36571. /**
  36572. * The column series type.
  36573. *
  36574. * @private
  36575. * @class
  36576. * @name Highcharts.seriesTypes.column
  36577. *
  36578. * @augments Highcharts.Series
  36579. */
  36580. seriesType('column', 'line',
  36581. /**
  36582. * Column series display one column per value along an X axis.
  36583. *
  36584. * @sample {highcharts} highcharts/demo/column-basic/
  36585. * Column chart
  36586. * @sample {highstock} stock/demo/column/
  36587. * Column chart
  36588. *
  36589. * @extends plotOptions.line
  36590. * @excluding connectEnds, connectNulls, gapSize, gapUnit, linecap,
  36591. * lineWidth, marker, step, useOhlcData
  36592. * @product highcharts highstock
  36593. * @optionparent plotOptions.column
  36594. */
  36595. {
  36596. /**
  36597. * The corner radius of the border surrounding each column or bar.
  36598. *
  36599. * @sample {highcharts} highcharts/plotoptions/column-borderradius/
  36600. * Rounded columns
  36601. *
  36602. * @product highcharts highstock gantt
  36603. *
  36604. * @private
  36605. */
  36606. borderRadius: 0,
  36607. /**
  36608. * When using automatic point colors pulled from the global
  36609. * [colors](colors) or series-specific
  36610. * [plotOptions.column.colors](series.colors) collections, this option
  36611. * determines whether the chart should receive one color per series or
  36612. * one color per point.
  36613. *
  36614. * In styled mode, the `colors` or `series.colors` arrays are not
  36615. * supported, and instead this option gives the points individual color
  36616. * class names on the form `highcharts-color-{n}`.
  36617. *
  36618. * @see [series colors](#plotOptions.column.colors)
  36619. *
  36620. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-false/
  36621. * False by default
  36622. * @sample {highcharts} highcharts/plotoptions/column-colorbypoint-true/
  36623. * True
  36624. *
  36625. * @type {boolean}
  36626. * @default false
  36627. * @since 2.0
  36628. * @product highcharts highstock gantt
  36629. * @apioption plotOptions.column.colorByPoint
  36630. */
  36631. /**
  36632. * A series specific or series type specific color set to apply instead
  36633. * of the global [colors](#colors) when [colorByPoint](
  36634. * #plotOptions.column.colorByPoint) is true.
  36635. *
  36636. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  36637. * @since 3.0
  36638. * @product highcharts highstock gantt
  36639. * @apioption plotOptions.column.colors
  36640. */
  36641. /**
  36642. * When `true`, the columns will center in the category, ignoring null
  36643. * or missing points. When `false`, space will be reserved for null or
  36644. * missing points.
  36645. *
  36646. * @sample {highcharts} highcharts/series-column/centerincategory/
  36647. * Center in category
  36648. *
  36649. * @since 8.0.1
  36650. * @product highcharts highstock gantt
  36651. */
  36652. centerInCategory: false,
  36653. /**
  36654. * Padding between each value groups, in x axis units.
  36655. *
  36656. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-default/
  36657. * 0.2 by default
  36658. * @sample {highcharts} highcharts/plotoptions/column-grouppadding-none/
  36659. * No group padding - all columns are evenly spaced
  36660. *
  36661. * @product highcharts highstock gantt
  36662. *
  36663. * @private
  36664. */
  36665. groupPadding: 0.2,
  36666. /**
  36667. * Whether to group non-stacked columns or to let them render
  36668. * independent of each other. Non-grouped columns will be laid out
  36669. * individually and overlap each other.
  36670. *
  36671. * @sample {highcharts} highcharts/plotoptions/column-grouping-false/
  36672. * Grouping disabled
  36673. * @sample {highstock} highcharts/plotoptions/column-grouping-false/
  36674. * Grouping disabled
  36675. *
  36676. * @type {boolean}
  36677. * @default true
  36678. * @since 2.3.0
  36679. * @product highcharts highstock gantt
  36680. * @apioption plotOptions.column.grouping
  36681. */
  36682. /**
  36683. * @ignore-option
  36684. * @private
  36685. */
  36686. marker: null,
  36687. /**
  36688. * The maximum allowed pixel width for a column, translated to the
  36689. * height of a bar in a bar chart. This prevents the columns from
  36690. * becoming too wide when there is a small number of points in the
  36691. * chart.
  36692. *
  36693. * @see [pointWidth](#plotOptions.column.pointWidth)
  36694. *
  36695. * @sample {highcharts} highcharts/plotoptions/column-maxpointwidth-20/
  36696. * Limited to 50
  36697. * @sample {highstock} highcharts/plotoptions/column-maxpointwidth-20/
  36698. * Limited to 50
  36699. *
  36700. * @type {number}
  36701. * @since 4.1.8
  36702. * @product highcharts highstock gantt
  36703. * @apioption plotOptions.column.maxPointWidth
  36704. */
  36705. /**
  36706. * Padding between each column or bar, in x axis units.
  36707. *
  36708. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-default/
  36709. * 0.1 by default
  36710. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-025/
  36711. * 0.25
  36712. * @sample {highcharts} highcharts/plotoptions/column-pointpadding-none/
  36713. * 0 for tightly packed columns
  36714. *
  36715. * @product highcharts highstock gantt
  36716. *
  36717. * @private
  36718. */
  36719. pointPadding: 0.1,
  36720. /**
  36721. * A pixel value specifying a fixed width for each column or bar. When
  36722. * `null`, the width is calculated from the `pointPadding` and
  36723. * `groupPadding`.
  36724. *
  36725. * @see [maxPointWidth](#plotOptions.column.maxPointWidth)
  36726. *
  36727. * @sample {highcharts} highcharts/plotoptions/column-pointwidth-20/
  36728. * 20px wide columns regardless of chart width or the amount of
  36729. * data points
  36730. *
  36731. * @type {number}
  36732. * @since 1.2.5
  36733. * @product highcharts highstock gantt
  36734. * @apioption plotOptions.column.pointWidth
  36735. */
  36736. /**
  36737. * A pixel value specifying a fixed width for the column or bar.
  36738. * Overrides pointWidth on the series.
  36739. *
  36740. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  36741. *
  36742. * @type {number}
  36743. * @default undefined
  36744. * @since 7.0.0
  36745. * @product highcharts highstock gantt
  36746. * @apioption series.column.data.pointWidth
  36747. */
  36748. /**
  36749. * The minimal height for a column or width for a bar. By default,
  36750. * 0 values are not shown. To visualize a 0 (or close to zero) point,
  36751. * set the minimal point length to a pixel value like 3\. In stacked
  36752. * column charts, minPointLength might not be respected for tightly
  36753. * packed values.
  36754. *
  36755. * @sample {highcharts} highcharts/plotoptions/column-minpointlength/
  36756. * Zero base value
  36757. * @sample {highcharts} highcharts/plotoptions/column-minpointlength-pos-and-neg/
  36758. * Positive and negative close to zero values
  36759. *
  36760. * @product highcharts highstock gantt
  36761. *
  36762. * @private
  36763. */
  36764. minPointLength: 0,
  36765. /**
  36766. * When the series contains less points than the crop threshold, all
  36767. * points are drawn, event if the points fall outside the visible plot
  36768. * area at the current zoom. The advantage of drawing all points
  36769. * (including markers and columns), is that animation is performed on
  36770. * updates. On the other hand, when the series contains more points than
  36771. * the crop threshold, the series data is cropped to only contain points
  36772. * that fall within the plot area. The advantage of cropping away
  36773. * invisible points is to increase performance on large series.
  36774. *
  36775. * @product highcharts highstock gantt
  36776. *
  36777. * @private
  36778. */
  36779. cropThreshold: 50,
  36780. /**
  36781. * The X axis range that each point is valid for. This determines the
  36782. * width of the column. On a categorized axis, the range will be 1
  36783. * by default (one category unit). On linear and datetime axes, the
  36784. * range will be computed as the distance between the two closest data
  36785. * points.
  36786. *
  36787. * The default `null` means it is computed automatically, but this
  36788. * option can be used to override the automatic value.
  36789. *
  36790. * This option is set by default to 1 if data sorting is enabled.
  36791. *
  36792. * @sample {highcharts} highcharts/plotoptions/column-pointrange/
  36793. * Set the point range to one day on a data set with one week
  36794. * between the points
  36795. *
  36796. * @type {number|null}
  36797. * @since 2.3
  36798. * @product highcharts highstock gantt
  36799. *
  36800. * @private
  36801. */
  36802. pointRange: null,
  36803. states: {
  36804. /**
  36805. * Options for the hovered point. These settings override the normal
  36806. * state options when a point is moused over or touched.
  36807. *
  36808. * @extends plotOptions.series.states.hover
  36809. * @excluding halo, lineWidth, lineWidthPlus, marker
  36810. * @product highcharts highstock gantt
  36811. */
  36812. hover: {
  36813. /** @ignore-option */
  36814. halo: false,
  36815. /**
  36816. * A specific border color for the hovered point. Defaults to
  36817. * inherit the normal state border color.
  36818. *
  36819. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36820. * @product highcharts gantt
  36821. * @apioption plotOptions.column.states.hover.borderColor
  36822. */
  36823. /**
  36824. * A specific color for the hovered point.
  36825. *
  36826. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36827. * @product highcharts gantt
  36828. * @apioption plotOptions.column.states.hover.color
  36829. */
  36830. /**
  36831. * How much to brighten the point on interaction. Requires the
  36832. * main color to be defined in hex or rgb(a) format.
  36833. *
  36834. * In styled mode, the hover brightening is by default replaced
  36835. * with a fill-opacity set in the `.highcharts-point:hover`
  36836. * rule.
  36837. *
  36838. * @sample {highcharts} highcharts/plotoptions/column-states-hover-brightness/
  36839. * Brighten by 0.5
  36840. *
  36841. * @product highcharts highstock gantt
  36842. */
  36843. brightness: 0.1
  36844. },
  36845. /**
  36846. * Options for the selected point. These settings override the
  36847. * normal state options when a point is selected.
  36848. *
  36849. * @extends plotOptions.series.states.select
  36850. * @excluding halo, lineWidth, lineWidthPlus, marker
  36851. * @product highcharts highstock gantt
  36852. */
  36853. select: {
  36854. /**
  36855. * A specific color for the selected point.
  36856. *
  36857. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36858. * @default #cccccc
  36859. * @product highcharts highstock gantt
  36860. */
  36861. color: '#cccccc',
  36862. /**
  36863. * A specific border color for the selected point.
  36864. *
  36865. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36866. * @default #000000
  36867. * @product highcharts highstock gantt
  36868. */
  36869. borderColor: '#000000'
  36870. }
  36871. },
  36872. dataLabels: {
  36873. align: void 0,
  36874. verticalAlign: void 0,
  36875. /**
  36876. * The y position offset of the label relative to the point in
  36877. * pixels.
  36878. *
  36879. * @type {number}
  36880. */
  36881. y: void 0
  36882. },
  36883. /**
  36884. * When this is true, the series will not cause the Y axis to cross
  36885. * the zero plane (or [threshold](#plotOptions.series.threshold) option)
  36886. * unless the data actually crosses the plane.
  36887. *
  36888. * For example, if `softThreshold` is `false`, a series of 0, 1, 2,
  36889. * 3 will make the Y axis show negative values according to the
  36890. * `minPadding` option. If `softThreshold` is `true`, the Y axis starts
  36891. * at 0.
  36892. *
  36893. * @since 4.1.9
  36894. * @product highcharts highstock
  36895. *
  36896. * @private
  36897. */
  36898. softThreshold: false,
  36899. // false doesn't work well: https://jsfiddle.net/highcharts/hz8fopan/14/
  36900. /**
  36901. * @ignore-option
  36902. * @private
  36903. */
  36904. startFromThreshold: true,
  36905. stickyTracking: false,
  36906. tooltip: {
  36907. distance: 6
  36908. },
  36909. /**
  36910. * The Y axis value to serve as the base for the columns, for
  36911. * distinguishing between values above and below a threshold. If `null`,
  36912. * the columns extend from the padding Y axis minimum.
  36913. *
  36914. * @type {number|null}
  36915. * @since 2.0
  36916. * @product highcharts
  36917. *
  36918. * @private
  36919. */
  36920. threshold: 0,
  36921. /**
  36922. * The width of the border surrounding each column or bar. Defaults to
  36923. * `1` when there is room for a border, but to `0` when the columns are
  36924. * so dense that a border would cover the next column.
  36925. *
  36926. * In styled mode, the stroke width can be set with the
  36927. * `.highcharts-point` rule.
  36928. *
  36929. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  36930. * 2px black border
  36931. *
  36932. * @type {number}
  36933. * @default undefined
  36934. * @product highcharts highstock gantt
  36935. * @apioption plotOptions.column.borderWidth
  36936. */
  36937. /**
  36938. * The color of the border surrounding each column or bar.
  36939. *
  36940. * In styled mode, the border stroke can be set with the
  36941. * `.highcharts-point` rule.
  36942. *
  36943. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  36944. * Dark gray border
  36945. *
  36946. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  36947. * @default #ffffff
  36948. * @product highcharts highstock gantt
  36949. *
  36950. * @private
  36951. */
  36952. borderColor: '#ffffff'
  36953. },
  36954. /**
  36955. * @lends seriesTypes.column.prototype
  36956. */
  36957. {
  36958. cropShoulder: 0,
  36959. // When tooltip is not shared, this series (and derivatives) requires
  36960. // direct touch/hover. KD-tree does not apply.
  36961. directTouch: true,
  36962. trackerGroups: ['group', 'dataLabelsGroup'],
  36963. // use separate negative stacks, unlike area stacks where a negative
  36964. // point is substracted from previous (#1910)
  36965. negStacks: true,
  36966. /* eslint-disable valid-jsdoc */
  36967. /**
  36968. * Initialize the series. Extends the basic Series.init method by
  36969. * marking other series of the same type as dirty.
  36970. *
  36971. * @private
  36972. * @function Highcharts.seriesTypes.column#init
  36973. * @return {void}
  36974. */
  36975. init: function () {
  36976. Series.prototype.init.apply(this, arguments);
  36977. var series = this, chart = series.chart;
  36978. // if the series is added dynamically, force redraw of other
  36979. // series affected by a new column
  36980. if (chart.hasRendered) {
  36981. chart.series.forEach(function (otherSeries) {
  36982. if (otherSeries.type === series.type) {
  36983. otherSeries.isDirty = true;
  36984. }
  36985. });
  36986. }
  36987. },
  36988. /**
  36989. * Return the width and x offset of the columns adjusted for grouping,
  36990. * groupPadding, pointPadding, pointWidth etc.
  36991. *
  36992. * @private
  36993. * @function Highcharts.seriesTypes.column#getColumnMetrics
  36994. * @return {Highcharts.ColumnMetricsObject}
  36995. */
  36996. getColumnMetrics: function () {
  36997. var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, reversedStacks = xAxis.options.reversedStacks,
  36998. // Keep backward compatibility: reversed xAxis had reversed
  36999. // stacks
  37000. reverseStacks = (xAxis.reversed && !reversedStacks) ||
  37001. (!xAxis.reversed && reversedStacks), stackKey, stackGroups = {}, columnCount = 0;
  37002. // Get the total number of column type series. This is called on
  37003. // every series. Consider moving this logic to a chart.orderStacks()
  37004. // function and call it on init, addSeries and removeSeries
  37005. if (options.grouping === false) {
  37006. columnCount = 1;
  37007. }
  37008. else {
  37009. series.chart.series.forEach(function (otherSeries) {
  37010. var otherYAxis = otherSeries.yAxis, otherOptions = otherSeries.options, columnIndex;
  37011. if (otherSeries.type === series.type &&
  37012. (otherSeries.visible ||
  37013. !series.chart.options.chart
  37014. .ignoreHiddenSeries) &&
  37015. yAxis.len === otherYAxis.len &&
  37016. yAxis.pos === otherYAxis.pos) { // #642, #2086
  37017. if (otherOptions.stacking && otherOptions.stacking !== 'group') {
  37018. stackKey = otherSeries.stackKey;
  37019. if (typeof stackGroups[stackKey] ===
  37020. 'undefined') {
  37021. stackGroups[stackKey] = columnCount++;
  37022. }
  37023. columnIndex = stackGroups[stackKey];
  37024. }
  37025. else if (otherOptions.grouping !== false) { // #1162
  37026. columnIndex = columnCount++;
  37027. }
  37028. otherSeries.columnIndex = columnIndex;
  37029. }
  37030. });
  37031. }
  37032. var categoryWidth = Math.min(Math.abs(xAxis.transA) * ((xAxis.ordinal && xAxis.ordinal.slope) ||
  37033. options.pointRange ||
  37034. xAxis.closestPointRange ||
  37035. xAxis.tickInterval ||
  37036. 1), // #2610
  37037. xAxis.len // #1535
  37038. ), 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,
  37039. // #1251, #3737
  37040. colIndex = (series.columnIndex || 0) + (reverseStacks ? 1 : 0), pointXOffset = pointPadding +
  37041. (groupPadding +
  37042. colIndex * pointOffsetWidth -
  37043. (categoryWidth / 2)) * (reverseStacks ? -1 : 1);
  37044. // Save it for reading in linked series (Error bars particularly)
  37045. series.columnMetrics = {
  37046. width: pointWidth,
  37047. offset: pointXOffset,
  37048. paddedWidth: pointOffsetWidth,
  37049. columnCount: columnCount
  37050. };
  37051. return series.columnMetrics;
  37052. },
  37053. /**
  37054. * Make the columns crisp. The edges are rounded to the nearest full
  37055. * pixel.
  37056. *
  37057. * @private
  37058. * @function Highcharts.seriesTypes.column#crispCol
  37059. * @param {number} x
  37060. * @param {number} y
  37061. * @param {number} w
  37062. * @param {number} h
  37063. * @return {Highcharts.BBoxObject}
  37064. */
  37065. crispCol: function (x, y, w, h) {
  37066. var chart = this.chart, borderWidth = this.borderWidth, xCrisp = -(borderWidth % 2 ? 0.5 : 0), yCrisp = borderWidth % 2 ? 0.5 : 1, right, bottom, fromTop;
  37067. if (chart.inverted && chart.renderer.isVML) {
  37068. yCrisp += 1;
  37069. }
  37070. // Horizontal. We need to first compute the exact right edge, then
  37071. // round it and compute the width from there.
  37072. if (this.options.crisp) {
  37073. right = Math.round(x + w) + xCrisp;
  37074. x = Math.round(x) + xCrisp;
  37075. w = right - x;
  37076. }
  37077. // Vertical
  37078. bottom = Math.round(y + h) + yCrisp;
  37079. fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656
  37080. y = Math.round(y) + yCrisp;
  37081. h = bottom - y;
  37082. // Top edges are exceptions
  37083. if (fromTop && h) { // #5146
  37084. y -= 1;
  37085. h += 1;
  37086. }
  37087. return {
  37088. x: x,
  37089. y: y,
  37090. width: w,
  37091. height: h
  37092. };
  37093. },
  37094. /**
  37095. * Adjust for missing columns, according to the `centerInCategory`
  37096. * option. Missing columns are either single points or stacks where the
  37097. * point or points are either missing or null.
  37098. *
  37099. * @private
  37100. * @function Highcharts.seriesTypes.column#adjustForMissingColumns
  37101. * @param {number} x
  37102. * The x coordinate of the column, left side
  37103. * @param {number} pointWidth
  37104. * The pointWidth, already computed upstream
  37105. * @param {Highcharts.ColumnPoint} point
  37106. * The point instance
  37107. * @param {Highcharts.ColumnMetricsObject} metrics
  37108. * The series-wide column metrics
  37109. * @return {number}
  37110. * The adjusted x position, or the original if not adjusted
  37111. */
  37112. adjustForMissingColumns: function (x, pointWidth, point, metrics) {
  37113. var _this = this;
  37114. var stacking = this.options.stacking;
  37115. if (!point.isNull && metrics.columnCount > 1) {
  37116. var indexInCategory_1 = 0;
  37117. var totalInCategory_1 = 0;
  37118. // Loop over all the stacks on the Y axis. When stacking is
  37119. // enabled, these are real point stacks. When stacking is not
  37120. // enabled, but `centerInCategory` is true, there is one stack
  37121. // handling the grouping of points in each category. This is
  37122. // done in the `setGroupedPoints` function.
  37123. Highcharts.objectEach(this.yAxis.stacking && this.yAxis.stacking.stacks, function (stack) {
  37124. if (typeof point.x === 'number') {
  37125. var stackItem = stack[point.x.toString()];
  37126. if (stackItem) {
  37127. var pointValues = stackItem.points[_this.index], total = stackItem.total;
  37128. // If true `stacking` is enabled, count the
  37129. // total number of non-null stacks in the
  37130. // category, and note which index this point is
  37131. // within those stacks.
  37132. if (stacking) {
  37133. if (pointValues) {
  37134. indexInCategory_1 = totalInCategory_1;
  37135. }
  37136. if (stackItem.hasValidPoints) {
  37137. totalInCategory_1++;
  37138. }
  37139. // If `stacking` is not enabled, look for the
  37140. // index and total of the `group` stack.
  37141. }
  37142. else if (H.isArray(pointValues)) {
  37143. indexInCategory_1 = pointValues[1];
  37144. totalInCategory_1 = total || 0;
  37145. }
  37146. }
  37147. }
  37148. });
  37149. // Compute the adjusted x position
  37150. var boxWidth = (totalInCategory_1 - 1) * metrics.paddedWidth +
  37151. pointWidth;
  37152. x = (point.plotX || 0) + boxWidth / 2 - pointWidth -
  37153. indexInCategory_1 * metrics.paddedWidth;
  37154. }
  37155. return x;
  37156. },
  37157. /**
  37158. * Translate each point to the plot area coordinate system and find
  37159. * shape positions
  37160. *
  37161. * @private
  37162. * @function Highcharts.seriesTypes.column#translate
  37163. */
  37164. translate: function () {
  37165. var series = this, chart = series.chart, options = series.options, dense = series.dense =
  37166. series.closestPointRange * series.xAxis.transA < 2, borderWidth = series.borderWidth = pick(options.borderWidth, dense ? 0 : 1 // #3635
  37167. ), xAxis = series.xAxis, yAxis = series.yAxis, threshold = options.threshold, translatedThreshold = series.translatedThreshold =
  37168. yAxis.getThreshold(threshold), minPointLength = pick(options.minPointLength, 5), metrics = series.getColumnMetrics(), seriesPointWidth = metrics.width,
  37169. // postprocessed for border width
  37170. seriesBarW = series.barW =
  37171. Math.max(seriesPointWidth, 1 + 2 * borderWidth), seriesXOffset = series.pointXOffset = metrics.offset, dataMin = series.dataMin, dataMax = series.dataMax;
  37172. if (chart.inverted) {
  37173. translatedThreshold -= 0.5; // #3355
  37174. }
  37175. // When the pointPadding is 0, we want the columns to be packed
  37176. // tightly, so we allow individual columns to have individual sizes.
  37177. // When pointPadding is greater, we strive for equal-width columns
  37178. // (#2694).
  37179. if (options.pointPadding) {
  37180. seriesBarW = Math.ceil(seriesBarW);
  37181. }
  37182. Series.prototype.translate.apply(series);
  37183. // Record the new values
  37184. series.points.forEach(function (point) {
  37185. var yBottom = pick(point.yBottom, translatedThreshold), safeDistance = 999 + Math.abs(yBottom), pointWidth = seriesPointWidth, plotX = point.plotX || 0,
  37186. // Don't draw too far outside plot area (#1303, #2241,
  37187. // #4264)
  37188. plotY = clamp(point.plotY, -safeDistance, yAxis.len + safeDistance), barX = plotX + seriesXOffset, barW = seriesBarW, barY = Math.min(plotY, yBottom), up, barH = Math.max(plotY, yBottom) - barY;
  37189. // Handle options.minPointLength
  37190. if (minPointLength && Math.abs(barH) < minPointLength) {
  37191. barH = minPointLength;
  37192. up = (!yAxis.reversed && !point.negative) ||
  37193. (yAxis.reversed && point.negative);
  37194. // Reverse zeros if there's no positive value in the series
  37195. // in visible range (#7046)
  37196. if (isNumber(threshold) &&
  37197. isNumber(dataMax) &&
  37198. point.y === threshold &&
  37199. dataMax <= threshold &&
  37200. // and if there's room for it (#7311)
  37201. (yAxis.min || 0) < threshold &&
  37202. // if all points are the same value (i.e zero) not draw
  37203. // as negative points (#10646)
  37204. dataMin !== dataMax) {
  37205. up = !up;
  37206. }
  37207. // If stacked...
  37208. barY = (Math.abs(barY - translatedThreshold) > minPointLength ?
  37209. // ...keep position
  37210. yBottom - minPointLength :
  37211. // #1485, #4051
  37212. translatedThreshold -
  37213. (up ? minPointLength : 0));
  37214. }
  37215. // Handle point.options.pointWidth
  37216. // @todo Handle grouping/stacking too. Calculate offset properly
  37217. if (defined(point.options.pointWidth)) {
  37218. pointWidth = barW =
  37219. Math.ceil(point.options.pointWidth);
  37220. barX -= Math.round((pointWidth - seriesPointWidth) / 2);
  37221. }
  37222. // Adjust for null or missing points
  37223. if (options.centerInCategory) {
  37224. barX = series.adjustForMissingColumns(barX, pointWidth, point, metrics);
  37225. }
  37226. // Cache for access in polar
  37227. point.barX = barX;
  37228. point.pointWidth = pointWidth;
  37229. // Fix the tooltip on center of grouped columns (#1216, #424,
  37230. // #3648)
  37231. point.tooltipPos = chart.inverted ?
  37232. [
  37233. yAxis.len + yAxis.pos - chart.plotLeft - plotY,
  37234. xAxis.len + xAxis.pos - chart.plotTop - (plotX || 0) - seriesXOffset - barW / 2,
  37235. barH
  37236. ] :
  37237. [barX + barW / 2, plotY + yAxis.pos -
  37238. chart.plotTop, barH];
  37239. // Register shape type and arguments to be used in drawPoints
  37240. // Allow shapeType defined on pointClass level
  37241. point.shapeType =
  37242. series.pointClass.prototype.shapeType || 'rect';
  37243. point.shapeArgs = series.crispCol.apply(series, point.isNull ?
  37244. // #3169, drilldown from null must have a position to work
  37245. // from #6585, dataLabel should be placed on xAxis, not
  37246. // floating in the middle of the chart
  37247. [barX, translatedThreshold, barW, 0] :
  37248. [barX, barY, barW, barH]);
  37249. });
  37250. },
  37251. getSymbol: noop,
  37252. /**
  37253. * Use a solid rectangle like the area series types
  37254. *
  37255. * @private
  37256. * @function Highcharts.seriesTypes.column#drawLegendSymbol
  37257. *
  37258. * @param {Highcharts.Legend} legend
  37259. * The legend object
  37260. *
  37261. * @param {Highcharts.Series|Highcharts.Point} item
  37262. * The series (this) or point
  37263. */
  37264. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  37265. /**
  37266. * Columns have no graph
  37267. *
  37268. * @private
  37269. * @function Highcharts.seriesTypes.column#drawGraph
  37270. */
  37271. drawGraph: function () {
  37272. this.group[this.dense ? 'addClass' : 'removeClass']('highcharts-dense-data');
  37273. },
  37274. /**
  37275. * Get presentational attributes
  37276. *
  37277. * @private
  37278. * @function Highcharts.seriesTypes.column#pointAttribs
  37279. *
  37280. * @param {Highcharts.ColumnPoint} point
  37281. *
  37282. * @param {string} state
  37283. *
  37284. * @return {Highcharts.SVGAttributes}
  37285. */
  37286. pointAttribs: function (point, state) {
  37287. var options = this.options, stateOptions, ret, p2o = this.pointAttrToOptions || {}, strokeOption = p2o.stroke || 'borderColor', strokeWidthOption = p2o['stroke-width'] || 'borderWidth', fill = (point && point.color) || this.color,
  37288. // set to fill when borderColor null:
  37289. stroke = ((point && point[strokeOption]) ||
  37290. options[strokeOption] ||
  37291. this.color ||
  37292. fill), strokeWidth = (point && point[strokeWidthOption]) ||
  37293. options[strokeWidthOption] ||
  37294. this[strokeWidthOption] || 0, dashstyle = (point && point.options.dashStyle) || options.dashStyle, opacity = pick(point && point.opacity, options.opacity, 1), zone, brightness;
  37295. // Handle zone colors
  37296. if (point && this.zones.length) {
  37297. zone = point.getZone();
  37298. // When zones are present, don't use point.color (#4267).
  37299. // Changed order (#6527), added support for colorAxis (#10670)
  37300. fill = (point.options.color ||
  37301. (zone && (zone.color || point.nonZonedColor)) ||
  37302. this.color);
  37303. if (zone) {
  37304. stroke = zone.borderColor || stroke;
  37305. dashstyle = zone.dashStyle || dashstyle;
  37306. strokeWidth = zone.borderWidth || strokeWidth;
  37307. }
  37308. }
  37309. // Select or hover states
  37310. if (state && point) {
  37311. stateOptions = merge(options.states[state],
  37312. // #6401
  37313. point.options.states &&
  37314. point.options.states[state] ||
  37315. {});
  37316. brightness = stateOptions.brightness;
  37317. fill =
  37318. stateOptions.color || (typeof brightness !== 'undefined' &&
  37319. color(fill)
  37320. .brighten(stateOptions.brightness)
  37321. .get()) || fill;
  37322. stroke = stateOptions[strokeOption] || stroke;
  37323. strokeWidth =
  37324. stateOptions[strokeWidthOption] || strokeWidth;
  37325. dashstyle = stateOptions.dashStyle || dashstyle;
  37326. opacity = pick(stateOptions.opacity, opacity);
  37327. }
  37328. ret = {
  37329. fill: fill,
  37330. stroke: stroke,
  37331. 'stroke-width': strokeWidth,
  37332. opacity: opacity
  37333. };
  37334. if (dashstyle) {
  37335. ret.dashstyle = dashstyle;
  37336. }
  37337. return ret;
  37338. },
  37339. /**
  37340. * Draw the columns. For bars, the series.group is rotated, so the same
  37341. * coordinates apply for columns and bars. This method is inherited by
  37342. * scatter series.
  37343. *
  37344. * @private
  37345. * @function Highcharts.seriesTypes.column#drawPoints
  37346. */
  37347. drawPoints: function () {
  37348. var series = this, chart = this.chart, options = series.options, renderer = chart.renderer, animationLimit = options.animationLimit || 250, shapeArgs;
  37349. // draw the columns
  37350. series.points.forEach(function (point) {
  37351. var plotY = point.plotY, graphic = point.graphic, hasGraphic = !!graphic, verb = graphic && chart.pointCount < animationLimit ?
  37352. 'animate' : 'attr';
  37353. if (isNumber(plotY) && point.y !== null) {
  37354. shapeArgs = point.shapeArgs;
  37355. // When updating a series between 2d and 3d or cartesian and
  37356. // polar, the shape type changes.
  37357. if (graphic && point.hasNewShapeType()) {
  37358. graphic = graphic.destroy();
  37359. }
  37360. // Set starting position for point sliding animation.
  37361. if (series.enabledDataSorting) {
  37362. point.startXPos = series.xAxis.reversed ?
  37363. -(shapeArgs ? shapeArgs.width : 0) :
  37364. series.xAxis.width;
  37365. }
  37366. if (!graphic) {
  37367. point.graphic = graphic =
  37368. renderer[point.shapeType](shapeArgs)
  37369. .add(point.group || series.group);
  37370. if (graphic &&
  37371. series.enabledDataSorting &&
  37372. chart.hasRendered &&
  37373. chart.pointCount < animationLimit) {
  37374. graphic.attr({
  37375. x: point.startXPos
  37376. });
  37377. hasGraphic = true;
  37378. verb = 'animate';
  37379. }
  37380. }
  37381. if (graphic && hasGraphic) { // update
  37382. graphic[verb](merge(shapeArgs));
  37383. }
  37384. // Border radius is not stylable (#6900)
  37385. if (options.borderRadius) {
  37386. graphic[verb]({
  37387. r: options.borderRadius
  37388. });
  37389. }
  37390. // Presentational
  37391. if (!chart.styledMode) {
  37392. graphic[verb](series.pointAttribs(point, (point.selected && 'select')))
  37393. .shadow(point.allowShadow !== false && options.shadow, null, options.stacking && !options.borderRadius);
  37394. }
  37395. graphic.addClass(point.getClassName(), true);
  37396. }
  37397. else if (graphic) {
  37398. point.graphic = graphic.destroy(); // #1269
  37399. }
  37400. });
  37401. },
  37402. /**
  37403. * Animate the column heights one by one from zero.
  37404. *
  37405. * @private
  37406. * @function Highcharts.seriesTypes.column#animate
  37407. *
  37408. * @param {boolean} init
  37409. * Whether to initialize the animation or run it
  37410. */
  37411. animate: function (init) {
  37412. var series = this, yAxis = this.yAxis, options = series.options, inverted = this.chart.inverted, attr = {}, translateProp = inverted ? 'translateX' : 'translateY', translateStart, translatedThreshold;
  37413. if (init) {
  37414. attr.scaleY = 0.001;
  37415. translatedThreshold = clamp(yAxis.toPixels(options.threshold), yAxis.pos, yAxis.pos + yAxis.len);
  37416. if (inverted) {
  37417. attr.translateX = translatedThreshold - yAxis.len;
  37418. }
  37419. else {
  37420. attr.translateY = translatedThreshold;
  37421. }
  37422. // apply finnal clipping (used in Highstock) (#7083)
  37423. // animation is done by scaleY, so cliping is for panes
  37424. if (series.clipBox) {
  37425. series.setClip();
  37426. }
  37427. series.group.attr(attr);
  37428. }
  37429. else { // run the animation
  37430. translateStart = series.group.attr(translateProp);
  37431. series.group.animate({ scaleY: 1 }, extend(animObject(series.options.animation), {
  37432. // Do the scale synchronously to ensure smooth
  37433. // updating (#5030, #7228)
  37434. step: function (val, fx) {
  37435. if (series.group) {
  37436. attr[translateProp] = translateStart +
  37437. fx.pos * (yAxis.pos - translateStart);
  37438. series.group.attr(attr);
  37439. }
  37440. }
  37441. }));
  37442. }
  37443. },
  37444. /**
  37445. * Remove this series from the chart
  37446. *
  37447. * @private
  37448. * @function Highcharts.seriesTypes.column#remove
  37449. */
  37450. remove: function () {
  37451. var series = this, chart = series.chart;
  37452. // column and bar series affects other series of the same type
  37453. // as they are either stacked or grouped
  37454. if (chart.hasRendered) {
  37455. chart.series.forEach(function (otherSeries) {
  37456. if (otherSeries.type === series.type) {
  37457. otherSeries.isDirty = true;
  37458. }
  37459. });
  37460. }
  37461. Series.prototype.remove.apply(series, arguments);
  37462. }
  37463. });
  37464. /* eslint-enable valid-jsdoc */
  37465. /**
  37466. * A `column` series. If the [type](#series.column.type) option is
  37467. * not specified, it is inherited from [chart.type](#chart.type).
  37468. *
  37469. * @extends series,plotOptions.column
  37470. * @excluding connectNulls, dataParser, dataURL, gapSize, gapUnit, linecap,
  37471. * lineWidth, marker, connectEnds, step
  37472. * @product highcharts highstock
  37473. * @apioption series.column
  37474. */
  37475. /**
  37476. * An array of data points for the series. For the `column` series type,
  37477. * points can be given in the following ways:
  37478. *
  37479. * 1. An array of numerical values. In this case, the numerical values will be
  37480. * interpreted as `y` options. The `x` values will be automatically
  37481. * calculated, either starting at 0 and incremented by 1, or from
  37482. * `pointStart` and `pointInterval` given in the series options. If the axis
  37483. * has categories, these will be used. Example:
  37484. * ```js
  37485. * data: [0, 5, 3, 5]
  37486. * ```
  37487. *
  37488. * 2. An array of arrays with 2 values. In this case, the values correspond to
  37489. * `x,y`. If the first value is a string, it is applied as the name of the
  37490. * point, and the `x` value is inferred.
  37491. * ```js
  37492. * data: [
  37493. * [0, 6],
  37494. * [1, 2],
  37495. * [2, 6]
  37496. * ]
  37497. * ```
  37498. *
  37499. * 3. An array of objects with named values. The following snippet shows only a
  37500. * few settings, see the complete options set below. If the total number of
  37501. * data points exceeds the series'
  37502. * [turboThreshold](#series.column.turboThreshold), this option is not
  37503. * available.
  37504. * ```js
  37505. * data: [{
  37506. * x: 1,
  37507. * y: 9,
  37508. * name: "Point2",
  37509. * color: "#00FF00"
  37510. * }, {
  37511. * x: 1,
  37512. * y: 6,
  37513. * name: "Point1",
  37514. * color: "#FF00FF"
  37515. * }]
  37516. * ```
  37517. *
  37518. * @sample {highcharts} highcharts/chart/reflow-true/
  37519. * Numerical values
  37520. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  37521. * Arrays of numeric x and y
  37522. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  37523. * Arrays of datetime x and y
  37524. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  37525. * Arrays of point.name and y
  37526. * @sample {highcharts} highcharts/series/data-array-of-objects/
  37527. * Config objects
  37528. *
  37529. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  37530. * @extends series.line.data
  37531. * @excluding marker
  37532. * @product highcharts highstock
  37533. * @apioption series.column.data
  37534. */
  37535. /**
  37536. * The color of the border surrounding the column or bar.
  37537. *
  37538. * In styled mode, the border stroke can be set with the `.highcharts-point`
  37539. * rule.
  37540. *
  37541. * @sample {highcharts} highcharts/plotoptions/column-bordercolor/
  37542. * Dark gray border
  37543. *
  37544. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  37545. * @product highcharts highstock
  37546. * @apioption series.column.data.borderColor
  37547. */
  37548. /**
  37549. * The width of the border surrounding the column or bar.
  37550. *
  37551. * In styled mode, the stroke width can be set with the `.highcharts-point`
  37552. * rule.
  37553. *
  37554. * @sample {highcharts} highcharts/plotoptions/column-borderwidth/
  37555. * 2px black border
  37556. *
  37557. * @type {number}
  37558. * @product highcharts highstock
  37559. * @apioption series.column.data.borderWidth
  37560. */
  37561. /**
  37562. * A name for the dash style to use for the column or bar. Overrides
  37563. * dashStyle on the series.
  37564. *
  37565. * In styled mode, the stroke dash-array can be set with the same classes as
  37566. * listed under [data.color](#series.column.data.color).
  37567. *
  37568. * @see [series.pointWidth](#plotOptions.column.dashStyle)
  37569. *
  37570. * @type {Highcharts.DashStyleValue}
  37571. * @apioption series.column.data.dashStyle
  37572. */
  37573. /**
  37574. * A pixel value specifying a fixed width for the column or bar. Overrides
  37575. * pointWidth on the series.
  37576. *
  37577. * @see [series.pointWidth](#plotOptions.column.pointWidth)
  37578. *
  37579. * @type {number}
  37580. * @apioption series.column.data.pointWidth
  37581. */
  37582. /**
  37583. * @excluding halo, lineWidth, lineWidthPlus, marker
  37584. * @product highcharts highstock
  37585. * @apioption series.column.states.hover
  37586. */
  37587. /**
  37588. * @excluding halo, lineWidth, lineWidthPlus, marker
  37589. * @product highcharts highstock
  37590. * @apioption series.column.states.select
  37591. */
  37592. ''; // includes above doclets in transpilat
  37593. });
  37594. _registerModule(_modules, 'parts/BarSeries.js', [_modules['parts/Utilities.js']], function (U) {
  37595. /* *
  37596. *
  37597. * (c) 2010-2020 Torstein Honsi
  37598. *
  37599. * License: www.highcharts.com/license
  37600. *
  37601. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37602. *
  37603. * */
  37604. var seriesType = U.seriesType;
  37605. /**
  37606. * Bar series type.
  37607. *
  37608. * @private
  37609. * @class
  37610. * @name Highcharts.seriesTypes.bar
  37611. *
  37612. * @augments Highcharts.Series
  37613. */
  37614. seriesType('bar', 'column',
  37615. /**
  37616. * A bar series is a special type of column series where the columns are
  37617. * horizontal.
  37618. *
  37619. * @sample highcharts/demo/bar-basic/
  37620. * Bar chart
  37621. *
  37622. * @extends plotOptions.column
  37623. * @product highcharts
  37624. * @apioption plotOptions.bar
  37625. */
  37626. /**
  37627. * @ignore
  37628. */
  37629. null, {
  37630. inverted: true
  37631. });
  37632. /**
  37633. * A `bar` series. If the [type](#series.bar.type) option is not specified,
  37634. * it is inherited from [chart.type](#chart.type).
  37635. *
  37636. * @extends series,plotOptions.bar
  37637. * @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
  37638. * linecap, lineWidth, marker, connectEnds, step
  37639. * @product highcharts
  37640. * @apioption series.bar
  37641. */
  37642. /**
  37643. * An array of data points for the series. For the `bar` series type,
  37644. * points can be given in the following ways:
  37645. *
  37646. * 1. An array of numerical values. In this case, the numerical values will be
  37647. * interpreted as `y` options. The `x` values will be automatically
  37648. * calculated, either starting at 0 and incremented by 1, or from
  37649. * `pointStart` and `pointInterval` given in the series options. If the axis
  37650. * has categories, these will be used. Example:
  37651. * ```js
  37652. * data: [0, 5, 3, 5]
  37653. * ```
  37654. *
  37655. * 2. An array of arrays with 2 values. In this case, the values correspond to
  37656. * `x,y`. If the first value is a string, it is applied as the name of the
  37657. * point, and the `x` value is inferred.
  37658. * ```js
  37659. * data: [
  37660. * [0, 5],
  37661. * [1, 10],
  37662. * [2, 3]
  37663. * ]
  37664. * ```
  37665. *
  37666. * 3. An array of objects with named values. The following snippet shows only a
  37667. * few settings, see the complete options set below. If the total number of
  37668. * data points exceeds the series'
  37669. * [turboThreshold](#series.bar.turboThreshold), this option is not
  37670. * available.
  37671. * ```js
  37672. * data: [{
  37673. * x: 1,
  37674. * y: 1,
  37675. * name: "Point2",
  37676. * color: "#00FF00"
  37677. * }, {
  37678. * x: 1,
  37679. * y: 10,
  37680. * name: "Point1",
  37681. * color: "#FF00FF"
  37682. * }]
  37683. * ```
  37684. *
  37685. * @sample {highcharts} highcharts/chart/reflow-true/
  37686. * Numerical values
  37687. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  37688. * Arrays of numeric x and y
  37689. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  37690. * Arrays of datetime x and y
  37691. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  37692. * Arrays of point.name and y
  37693. * @sample {highcharts} highcharts/series/data-array-of-objects/
  37694. * Config objects
  37695. *
  37696. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  37697. * @extends series.column.data
  37698. * @product highcharts
  37699. * @apioption series.bar.data
  37700. */
  37701. /**
  37702. * @excluding halo,lineWidth,lineWidthPlus,marker
  37703. * @product highcharts highstock
  37704. * @apioption series.bar.states.hover
  37705. */
  37706. /**
  37707. * @excluding halo,lineWidth,lineWidthPlus,marker
  37708. * @product highcharts highstock
  37709. * @apioption series.bar.states.select
  37710. */
  37711. ''; // gets doclets above into transpilat
  37712. });
  37713. _registerModule(_modules, 'parts/ScatterSeries.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  37714. /* *
  37715. *
  37716. * (c) 2010-2020 Torstein Honsi
  37717. *
  37718. * License: www.highcharts.com/license
  37719. *
  37720. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37721. *
  37722. * */
  37723. var addEvent = U.addEvent, seriesType = U.seriesType;
  37724. var Series = H.Series;
  37725. /**
  37726. * Scatter series type.
  37727. *
  37728. * @private
  37729. * @class
  37730. * @name Highcharts.seriesTypes.scatter
  37731. *
  37732. * @augments Highcharts.Series
  37733. */
  37734. seriesType('scatter', 'line',
  37735. /**
  37736. * A scatter plot uses cartesian coordinates to display values for two
  37737. * variables for a set of data.
  37738. *
  37739. * @sample {highcharts} highcharts/demo/scatter/
  37740. * Scatter plot
  37741. *
  37742. * @extends plotOptions.line
  37743. * @excluding pointPlacement, shadow, useOhlcData
  37744. * @product highcharts highstock
  37745. * @optionparent plotOptions.scatter
  37746. */
  37747. {
  37748. /**
  37749. * The width of the line connecting the data points.
  37750. *
  37751. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
  37752. * 0 by default
  37753. * @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
  37754. * 1px
  37755. *
  37756. * @product highcharts highstock
  37757. */
  37758. lineWidth: 0,
  37759. findNearestPointBy: 'xy',
  37760. /**
  37761. * Apply a jitter effect for the rendered markers. When plotting
  37762. * discrete values, a little random noise may help telling the points
  37763. * apart. The jitter setting applies a random displacement of up to `n`
  37764. * axis units in either direction. So for example on a horizontal X
  37765. * axis, setting the `jitter.x` to 0.24 will render the point in a
  37766. * random position between 0.24 units to the left and 0.24 units to the
  37767. * right of the true axis position. On a category axis, setting it to
  37768. * 0.5 will fill up the bin and make the data appear continuous.
  37769. *
  37770. * When rendered on top of a box plot or a column series, a jitter value
  37771. * of 0.24 will correspond to the underlying series' default
  37772. * [groupPadding](
  37773. * https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
  37774. * and [pointPadding](
  37775. * https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
  37776. * settings.
  37777. *
  37778. * @sample {highcharts} highcharts/series-scatter/jitter
  37779. * Jitter on a scatter plot
  37780. *
  37781. * @sample {highcharts} highcharts/series-scatter/jitter-boxplot
  37782. * Jittered scatter plot on top of a box plot
  37783. *
  37784. * @product highcharts highstock
  37785. * @since 7.0.2
  37786. */
  37787. jitter: {
  37788. /**
  37789. * The maximal X offset for the random jitter effect.
  37790. */
  37791. x: 0,
  37792. /**
  37793. * The maximal Y offset for the random jitter effect.
  37794. */
  37795. y: 0
  37796. },
  37797. marker: {
  37798. enabled: true // Overrides auto-enabling in line series (#3647)
  37799. },
  37800. /**
  37801. * Sticky tracking of mouse events. When true, the `mouseOut` event
  37802. * on a series isn't triggered until the mouse moves over another
  37803. * series, or out of the plot area. When false, the `mouseOut` event on
  37804. * a series is triggered when the mouse leaves the area around the
  37805. * series' graph or markers. This also implies the tooltip. When
  37806. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  37807. * will be hidden when moving the mouse between series.
  37808. *
  37809. * @type {boolean}
  37810. * @default false
  37811. * @product highcharts highstock
  37812. * @apioption plotOptions.scatter.stickyTracking
  37813. */
  37814. /**
  37815. * A configuration object for the tooltip rendering of each single
  37816. * series. Properties are inherited from [tooltip](#tooltip).
  37817. * Overridable properties are `headerFormat`, `pointFormat`,
  37818. * `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
  37819. * series, in a scatter plot the series.name by default shows in the
  37820. * headerFormat and point.x and point.y in the pointFormat.
  37821. *
  37822. * @product highcharts highstock
  37823. */
  37824. tooltip: {
  37825. headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  37826. '<span style="font-size: 10px"> {series.name}</span><br/>',
  37827. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
  37828. }
  37829. // Prototype members
  37830. }, {
  37831. sorted: false,
  37832. requireSorting: false,
  37833. noSharedTooltip: true,
  37834. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  37835. takeOrdinalPosition: false,
  37836. /* eslint-disable valid-jsdoc */
  37837. /**
  37838. * @private
  37839. * @function Highcharts.seriesTypes.scatter#drawGraph
  37840. */
  37841. drawGraph: function () {
  37842. if (this.options.lineWidth) {
  37843. Series.prototype.drawGraph.call(this);
  37844. }
  37845. },
  37846. // Optionally add the jitter effect
  37847. applyJitter: function () {
  37848. var series = this, jitter = this.options.jitter, len = this.points.length;
  37849. /**
  37850. * Return a repeatable, pseudo-random number based on an integer
  37851. * seed.
  37852. * @private
  37853. */
  37854. function unrandom(seed) {
  37855. var rand = Math.sin(seed) * 10000;
  37856. return rand - Math.floor(rand);
  37857. }
  37858. if (jitter) {
  37859. this.points.forEach(function (point, i) {
  37860. ['x', 'y'].forEach(function (dim, j) {
  37861. var axis, plotProp = 'plot' + dim.toUpperCase(), min, max, translatedJitter;
  37862. if (jitter[dim] && !point.isNull) {
  37863. axis = series[dim + 'Axis'];
  37864. translatedJitter =
  37865. jitter[dim] * axis.transA;
  37866. if (axis && !axis.isLog) {
  37867. // Identify the outer bounds of the jitter range
  37868. min = Math.max(0, point[plotProp] - translatedJitter);
  37869. max = Math.min(axis.len, point[plotProp] + translatedJitter);
  37870. // Find a random position within this range
  37871. point[plotProp] = min +
  37872. (max - min) * unrandom(i + j * len);
  37873. // Update clientX for the tooltip k-d-tree
  37874. if (dim === 'x') {
  37875. point.clientX = point.plotX;
  37876. }
  37877. }
  37878. }
  37879. });
  37880. });
  37881. }
  37882. }
  37883. /* eslint-enable valid-jsdoc */
  37884. });
  37885. /* eslint-disable no-invalid-this */
  37886. addEvent(Series, 'afterTranslate', function () {
  37887. if (this.applyJitter) {
  37888. this.applyJitter();
  37889. }
  37890. });
  37891. /* eslint-enable no-invalid-this */
  37892. /**
  37893. * A `scatter` series. If the [type](#series.scatter.type) option is
  37894. * not specified, it is inherited from [chart.type](#chart.type).
  37895. *
  37896. * @extends series,plotOptions.scatter
  37897. * @excluding dataParser, dataURL, useOhlcData
  37898. * @product highcharts highstock
  37899. * @apioption series.scatter
  37900. */
  37901. /**
  37902. * An array of data points for the series. For the `scatter` series
  37903. * type, points can be given in the following ways:
  37904. *
  37905. * 1. An array of numerical values. In this case, the numerical values will be
  37906. * interpreted as `y` options. The `x` values will be automatically
  37907. * calculated, either starting at 0 and incremented by 1, or from
  37908. * `pointStart` and `pointInterval` given in the series options. If the axis
  37909. * has categories, these will be used. Example:
  37910. * ```js
  37911. * data: [0, 5, 3, 5]
  37912. * ```
  37913. *
  37914. * 2. An array of arrays with 2 values. In this case, the values correspond to
  37915. * `x,y`. If the first value is a string, it is applied as the name of the
  37916. * point, and the `x` value is inferred.
  37917. * ```js
  37918. * data: [
  37919. * [0, 0],
  37920. * [1, 8],
  37921. * [2, 9]
  37922. * ]
  37923. * ```
  37924. *
  37925. * 3. An array of objects with named values. The following snippet shows only a
  37926. * few settings, see the complete options set below. If the total number of
  37927. * data points exceeds the series'
  37928. * [turboThreshold](#series.scatter.turboThreshold), this option is not
  37929. * available.
  37930. * ```js
  37931. * data: [{
  37932. * x: 1,
  37933. * y: 2,
  37934. * name: "Point2",
  37935. * color: "#00FF00"
  37936. * }, {
  37937. * x: 1,
  37938. * y: 4,
  37939. * name: "Point1",
  37940. * color: "#FF00FF"
  37941. * }]
  37942. * ```
  37943. *
  37944. * @sample {highcharts} highcharts/chart/reflow-true/
  37945. * Numerical values
  37946. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  37947. * Arrays of numeric x and y
  37948. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  37949. * Arrays of datetime x and y
  37950. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  37951. * Arrays of point.name and y
  37952. * @sample {highcharts} highcharts/series/data-array-of-objects/
  37953. * Config objects
  37954. *
  37955. * @type {Array<number|Array<(number|string),(number|null)>|null|*>}
  37956. * @extends series.line.data
  37957. * @product highcharts highstock
  37958. * @apioption series.scatter.data
  37959. */
  37960. ''; // adds doclets above to transpilat
  37961. });
  37962. _registerModule(_modules, 'mixins/centered-series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  37963. /* *
  37964. *
  37965. * (c) 2010-2020 Torstein Honsi
  37966. *
  37967. * License: www.highcharts.com/license
  37968. *
  37969. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37970. *
  37971. * */
  37972. /**
  37973. * @private
  37974. * @interface Highcharts.RadianAngles
  37975. */ /**
  37976. * @name Highcharts.RadianAngles#end
  37977. * @type {number}
  37978. */ /**
  37979. * @name Highcharts.RadianAngles#start
  37980. * @type {number}
  37981. */
  37982. var isNumber = U.isNumber, pick = U.pick, relativeLength = U.relativeLength;
  37983. var deg2rad = H.deg2rad;
  37984. /* eslint-disable valid-jsdoc */
  37985. /**
  37986. * @private
  37987. * @mixin Highcharts.CenteredSeriesMixin
  37988. */
  37989. H.CenteredSeriesMixin = {
  37990. /**
  37991. * Get the center of the pie based on the size and center options relative
  37992. * to the plot area. Borrowed by the polar and gauge series types.
  37993. *
  37994. * @private
  37995. * @function Highcharts.CenteredSeriesMixin.getCenter
  37996. *
  37997. * @return {Array<number>}
  37998. */
  37999. getCenter: function () {
  38000. var options = this.options, chart = this.chart, slicingRoom = 2 * (options.slicedOffset || 0), handleSlicingRoom, plotWidth = chart.plotWidth - 2 * slicingRoom, plotHeight = chart.plotHeight - 2 * slicingRoom, centerOption = options.center, smallestSize = Math.min(plotWidth, plotHeight), size = options.size, innerSize = options.innerSize || 0, positions, i, value;
  38001. if (typeof size === 'string') {
  38002. size = parseFloat(size);
  38003. }
  38004. if (typeof innerSize === 'string') {
  38005. innerSize = parseFloat(innerSize);
  38006. }
  38007. positions = [
  38008. pick(centerOption[0], '50%'),
  38009. pick(centerOption[1], '50%'),
  38010. // Prevent from negative values
  38011. pick(size && size < 0 ? void 0 : options.size, '100%'),
  38012. pick(innerSize && innerSize < 0 ? void 0 : options.innerSize || 0, '0%')
  38013. ];
  38014. // No need for inner size in angular (gauges) series but still required
  38015. // for pie series
  38016. if (chart.angular && !(this instanceof H.Series)) {
  38017. positions[3] = 0;
  38018. }
  38019. for (i = 0; i < 4; ++i) {
  38020. value = positions[i];
  38021. handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
  38022. // i == 0: centerX, relative to width
  38023. // i == 1: centerY, relative to height
  38024. // i == 2: size, relative to smallestSize
  38025. // i == 3: innerSize, relative to size
  38026. positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) + (handleSlicingRoom ? slicingRoom : 0);
  38027. }
  38028. // innerSize cannot be larger than size (#3632)
  38029. if (positions[3] > positions[2]) {
  38030. positions[3] = positions[2];
  38031. }
  38032. return positions;
  38033. },
  38034. /**
  38035. * getStartAndEndRadians - Calculates start and end angles in radians.
  38036. * Used in series types such as pie and sunburst.
  38037. *
  38038. * @private
  38039. * @function Highcharts.CenteredSeriesMixin.getStartAndEndRadians
  38040. *
  38041. * @param {number} [start]
  38042. * Start angle in degrees.
  38043. *
  38044. * @param {number} [end]
  38045. * Start angle in degrees.
  38046. *
  38047. * @return {Highcharts.RadianAngles}
  38048. * Returns an object containing start and end angles as radians.
  38049. */
  38050. getStartAndEndRadians: function (start, end) {
  38051. var startAngle = isNumber(start) ? start : 0, // must be a number
  38052. endAngle = ((isNumber(end) && // must be a number
  38053. end > startAngle && // must be larger than the start angle
  38054. // difference must be less than 360 degrees
  38055. (end - startAngle) < 360) ?
  38056. end :
  38057. startAngle + 360), correction = -90;
  38058. return {
  38059. start: deg2rad * (startAngle + correction),
  38060. end: deg2rad * (endAngle + correction)
  38061. };
  38062. }
  38063. };
  38064. });
  38065. _registerModule(_modules, 'parts/PieSeries.js', [_modules['parts/Globals.js'], _modules['mixins/legend-symbol.js'], _modules['parts/Point.js'], _modules['parts/Utilities.js']], function (H, LegendSymbolMixin, Point, U) {
  38066. /* *
  38067. *
  38068. * (c) 2010-2020 Torstein Honsi
  38069. *
  38070. * License: www.highcharts.com/license
  38071. *
  38072. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  38073. *
  38074. * */
  38075. var addEvent = U.addEvent, clamp = U.clamp, defined = U.defined, fireEvent = U.fireEvent, isNumber = U.isNumber, merge = U.merge, pick = U.pick, relativeLength = U.relativeLength, seriesType = U.seriesType, setAnimation = U.setAnimation;
  38076. var CenteredSeriesMixin = H.CenteredSeriesMixin, getStartAndEndRadians = CenteredSeriesMixin.getStartAndEndRadians, noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes;
  38077. /**
  38078. * Pie series type.
  38079. *
  38080. * @private
  38081. * @class
  38082. * @name Highcharts.seriesTypes.pie
  38083. *
  38084. * @augments Highcharts.Series
  38085. */
  38086. seriesType('pie', 'line',
  38087. /**
  38088. * A pie chart is a circular graphic which is divided into slices to
  38089. * illustrate numerical proportion.
  38090. *
  38091. * @sample highcharts/demo/pie-basic/
  38092. * Pie chart
  38093. *
  38094. * @extends plotOptions.line
  38095. * @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
  38096. * cropThreshold, dashStyle, dataSorting, dragDrop,
  38097. * findNearestPointBy, getExtremesFromAll, label, lineWidth,
  38098. * marker, negativeColor, pointInterval, pointIntervalUnit,
  38099. * pointPlacement, pointStart, softThreshold, stacking, step,
  38100. * threshold, turboThreshold, zoneAxis, zones, dataSorting
  38101. * @product highcharts
  38102. * @optionparent plotOptions.pie
  38103. */
  38104. {
  38105. /**
  38106. * @excluding legendItemClick
  38107. * @apioption plotOptions.pie.events
  38108. */
  38109. /**
  38110. * Fires when the checkbox next to the point name in the legend is
  38111. * clicked. One parameter, event, is passed to the function. The state
  38112. * of the checkbox is found by event.checked. The checked item is found
  38113. * by event.item. Return false to prevent the default action which is to
  38114. * toggle the select state of the series.
  38115. *
  38116. * @sample {highcharts} highcharts/plotoptions/series-events-checkboxclick/
  38117. * Alert checkbox status
  38118. *
  38119. * @type {Function}
  38120. * @since 1.2.0
  38121. * @product highcharts
  38122. * @context Highcharts.Point
  38123. * @apioption plotOptions.pie.events.checkboxClick
  38124. */
  38125. /**
  38126. * Fires when the legend item belonging to the pie point (slice) is
  38127. * clicked. The `this` keyword refers to the point itself. One
  38128. * parameter, `event`, is passed to the function, containing common
  38129. * event information. The default action is to toggle the visibility of
  38130. * the point. This can be prevented by calling `event.preventDefault()`.
  38131. *
  38132. * @sample {highcharts} highcharts/plotoptions/pie-point-events-legenditemclick/
  38133. * Confirm toggle visibility
  38134. *
  38135. * @type {Highcharts.PointLegendItemClickCallbackFunction}
  38136. * @since 1.2.0
  38137. * @product highcharts
  38138. * @apioption plotOptions.pie.point.events.legendItemClick
  38139. */
  38140. /**
  38141. * The center of the pie chart relative to the plot area. Can be
  38142. * percentages or pixel values. The default behaviour (as of 3.0) is to
  38143. * center the pie so that all slices and data labels are within the plot
  38144. * area. As a consequence, the pie may actually jump around in a chart
  38145. * with dynamic values, as the data labels move. In that case, the
  38146. * center should be explicitly set, for example to `["50%", "50%"]`.
  38147. *
  38148. * @sample {highcharts} highcharts/plotoptions/pie-center/
  38149. * Centered at 100, 100
  38150. *
  38151. * @type {Array<(number|string|null),(number|string|null)>}
  38152. * @default [null, null]
  38153. * @product highcharts
  38154. *
  38155. * @private
  38156. */
  38157. center: [null, null],
  38158. /**
  38159. * The color of the pie series. A pie series is represented as an empty
  38160. * circle if the total sum of its values is 0. Use this property to
  38161. * define the color of its border.
  38162. *
  38163. * In styled mode, the color can be defined by the
  38164. * [colorIndex](#plotOptions.series.colorIndex) option. Also, the series
  38165. * color can be set with the `.highcharts-series`,
  38166. * `.highcharts-color-{n}`, `.highcharts-{type}-series` or
  38167. * `.highcharts-series-{n}` class, or individual classes given by the
  38168. * `className` option.
  38169. *
  38170. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  38171. * Empty pie series
  38172. *
  38173. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  38174. * @default #cccccc
  38175. * @apioption plotOptions.pie.color
  38176. */
  38177. /**
  38178. * @product highcharts
  38179. *
  38180. * @private
  38181. */
  38182. clip: false,
  38183. /**
  38184. * @ignore-option
  38185. *
  38186. * @private
  38187. */
  38188. colorByPoint: true,
  38189. /**
  38190. * A series specific or series type specific color set to use instead
  38191. * of the global [colors](#colors).
  38192. *
  38193. * @sample {highcharts} highcharts/demo/pie-monochrome/
  38194. * Set default colors for all pies
  38195. *
  38196. * @type {Array<Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject>}
  38197. * @since 3.0
  38198. * @product highcharts
  38199. * @apioption plotOptions.pie.colors
  38200. */
  38201. /**
  38202. * @declare Highcharts.SeriesPieDataLabelsOptionsObject
  38203. * @extends plotOptions.series.dataLabels
  38204. * @excluding align, allowOverlap, inside, staggerLines, step
  38205. * @private
  38206. */
  38207. dataLabels: {
  38208. /**
  38209. * Alignment method for data labels. Possible values are:
  38210. *
  38211. * - `toPlotEdges`: Each label touches the nearest vertical edge of
  38212. * the plot area.
  38213. *
  38214. * - `connectors`: Connectors have the same x position and the
  38215. * widest label of each half (left & right) touches the nearest
  38216. * vertical edge of the plot area.
  38217. *
  38218. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-connectors/
  38219. * alignTo: connectors
  38220. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-alignto-plotedges/
  38221. * alignTo: plotEdges
  38222. *
  38223. * @type {string}
  38224. * @since 7.0.0
  38225. * @product highcharts
  38226. * @apioption plotOptions.pie.dataLabels.alignTo
  38227. */
  38228. allowOverlap: true,
  38229. /**
  38230. * The color of the line connecting the data label to the pie slice.
  38231. * The default color is the same as the point's color.
  38232. *
  38233. * In styled mode, the connector stroke is given in the
  38234. * `.highcharts-data-label-connector` class.
  38235. *
  38236. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorcolor/
  38237. * Blue connectors
  38238. * @sample {highcharts} highcharts/css/pie-point/
  38239. * Styled connectors
  38240. *
  38241. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  38242. * @since 2.1
  38243. * @product highcharts
  38244. * @apioption plotOptions.pie.dataLabels.connectorColor
  38245. */
  38246. /**
  38247. * The distance from the data label to the connector. Note that
  38248. * data labels also have a default `padding`, so in order for the
  38249. * connector to touch the text, the `padding` must also be 0.
  38250. *
  38251. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorpadding/
  38252. * No padding
  38253. *
  38254. * @since 2.1
  38255. * @product highcharts
  38256. */
  38257. connectorPadding: 5,
  38258. /**
  38259. * Specifies the method that is used to generate the connector path.
  38260. * Highcharts provides 3 built-in connector shapes: `'fixedOffset'`
  38261. * (default), `'straight'` and `'crookedLine'`. Using
  38262. * `'crookedLine'` has the most sense (in most of the cases) when
  38263. * `'alignTo'` is set.
  38264. *
  38265. * Users can provide their own method by passing a function instead
  38266. * of a String. 3 arguments are passed to the callback:
  38267. *
  38268. * - Object that holds the information about the coordinates of the
  38269. * label (`x` & `y` properties) and how the label is located in
  38270. * relation to the pie (`alignment` property). `alignment` can by
  38271. * one of the following:
  38272. * `'left'` (pie on the left side of the data label),
  38273. * `'right'` (pie on the right side of the data label) or
  38274. * `'center'` (data label overlaps the pie).
  38275. *
  38276. * - Object that holds the information about the position of the
  38277. * connector. Its `touchingSliceAt` porperty tells the position
  38278. * of the place where the connector touches the slice.
  38279. *
  38280. * - Data label options
  38281. *
  38282. * The function has to return an SVG path definition in array form
  38283. * (see the example).
  38284. *
  38285. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-string/
  38286. * connectorShape is a String
  38287. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorshape-function/
  38288. * connectorShape is a function
  38289. *
  38290. * @type {string|Function}
  38291. * @since 7.0.0
  38292. * @product highcharts
  38293. */
  38294. connectorShape: 'fixedOffset',
  38295. /**
  38296. * The width of the line connecting the data label to the pie slice.
  38297. *
  38298. * In styled mode, the connector stroke width is given in the
  38299. * `.highcharts-data-label-connector` class.
  38300. *
  38301. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-connectorwidth-disabled/
  38302. * Disable the connector
  38303. * @sample {highcharts} highcharts/css/pie-point/
  38304. * Styled connectors
  38305. *
  38306. * @type {number}
  38307. * @default 1
  38308. * @since 2.1
  38309. * @product highcharts
  38310. * @apioption plotOptions.pie.dataLabels.connectorWidth
  38311. */
  38312. /**
  38313. * Works only if `connectorShape` is `'crookedLine'`. It defines how
  38314. * far from the vertical plot edge the coonnector path should be
  38315. * crooked.
  38316. *
  38317. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-crookdistance/
  38318. * crookDistance set to 90%
  38319. *
  38320. * @since 7.0.0
  38321. * @product highcharts
  38322. */
  38323. crookDistance: '70%',
  38324. /**
  38325. * The distance of the data label from the pie's edge. Negative
  38326. * numbers put the data label on top of the pie slices. Can also be
  38327. * defined as a percentage of pie's radius. Connectors are only
  38328. * shown for data labels outside the pie.
  38329. *
  38330. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-distance/
  38331. * Data labels on top of the pie
  38332. *
  38333. * @type {number|string}
  38334. * @since 2.1
  38335. * @product highcharts
  38336. */
  38337. distance: 30,
  38338. enabled: true,
  38339. formatter: function () {
  38340. return this.point.isNull ? void 0 : this.point.name;
  38341. },
  38342. /**
  38343. * Whether to render the connector as a soft arc or a line with
  38344. * sharp break. Works only if `connectorShape` equals to
  38345. * `fixedOffset`.
  38346. *
  38347. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-true/
  38348. * Soft
  38349. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-softconnector-false/
  38350. * Non soft
  38351. *
  38352. * @since 2.1.7
  38353. * @product highcharts
  38354. */
  38355. softConnector: true,
  38356. /**
  38357. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow
  38358. * Long labels truncated with an ellipsis
  38359. * @sample {highcharts} highcharts/plotoptions/pie-datalabels-overflow-wrap
  38360. * Long labels are wrapped
  38361. *
  38362. * @type {Highcharts.CSSObject}
  38363. * @apioption plotOptions.pie.dataLabels.style
  38364. */
  38365. x: 0
  38366. },
  38367. /**
  38368. * If the total sum of the pie's values is 0, the series is represented
  38369. * as an empty circle . The `fillColor` option defines the color of that
  38370. * circle. Use [pie.borderWidth](#plotOptions.pie.borderWidth) to set
  38371. * the border thickness.
  38372. *
  38373. * @sample {highcharts} highcharts/plotoptions/pie-emptyseries/
  38374. * Empty pie series
  38375. *
  38376. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  38377. * @private
  38378. */
  38379. fillColor: void 0,
  38380. /**
  38381. * The end angle of the pie in degrees where 0 is top and 90 is right.
  38382. * Defaults to `startAngle` plus 360.
  38383. *
  38384. * @sample {highcharts} highcharts/demo/pie-semi-circle/
  38385. * Semi-circle donut
  38386. *
  38387. * @type {number}
  38388. * @since 1.3.6
  38389. * @product highcharts
  38390. * @apioption plotOptions.pie.endAngle
  38391. */
  38392. /**
  38393. * Equivalent to [chart.ignoreHiddenSeries](#chart.ignoreHiddenSeries),
  38394. * this option tells whether the series shall be redrawn as if the
  38395. * hidden point were `null`.
  38396. *
  38397. * The default value changed from `false` to `true` with Highcharts
  38398. * 3.0.
  38399. *
  38400. * @sample {highcharts} highcharts/plotoptions/pie-ignorehiddenpoint/
  38401. * True, the hiddden point is ignored
  38402. *
  38403. * @since 2.3.0
  38404. * @product highcharts
  38405. *
  38406. * @private
  38407. */
  38408. ignoreHiddenPoint: true,
  38409. /**
  38410. * @ignore-option
  38411. *
  38412. * @private
  38413. */
  38414. inactiveOtherPoints: true,
  38415. /**
  38416. * The size of the inner diameter for the pie. A size greater than 0
  38417. * renders a donut chart. Can be a percentage or pixel value.
  38418. * Percentages are relative to the pie size. Pixel values are given as
  38419. * integers.
  38420. *
  38421. *
  38422. * Note: in Highcharts < 4.1.2, the percentage was relative to the plot
  38423. * area, not the pie size.
  38424. *
  38425. * @sample {highcharts} highcharts/plotoptions/pie-innersize-80px/
  38426. * 80px inner size
  38427. * @sample {highcharts} highcharts/plotoptions/pie-innersize-50percent/
  38428. * 50% of the plot area
  38429. * @sample {highcharts} highcharts/demo/3d-pie-donut/
  38430. * 3D donut
  38431. *
  38432. * @type {number|string}
  38433. * @default 0
  38434. * @since 2.0
  38435. * @product highcharts
  38436. * @apioption plotOptions.pie.innerSize
  38437. */
  38438. /**
  38439. * @ignore-option
  38440. *
  38441. * @private
  38442. */
  38443. legendType: 'point',
  38444. /**
  38445. * @ignore-option
  38446. *
  38447. * @private
  38448. */
  38449. marker: null,
  38450. /**
  38451. * The minimum size for a pie in response to auto margins. The pie will
  38452. * try to shrink to make room for data labels in side the plot area,
  38453. * but only to this size.
  38454. *
  38455. * @type {number|string}
  38456. * @default 80
  38457. * @since 3.0
  38458. * @product highcharts
  38459. * @apioption plotOptions.pie.minSize
  38460. */
  38461. /**
  38462. * The diameter of the pie relative to the plot area. Can be a
  38463. * percentage or pixel value. Pixel values are given as integers. The
  38464. * default behaviour (as of 3.0) is to scale to the plot area and give
  38465. * room for data labels within the plot area.
  38466. * [slicedOffset](#plotOptions.pie.slicedOffset) is also included in the
  38467. * default size calculation. As a consequence, the size of the pie may
  38468. * vary when points are updated and data labels more around. In that
  38469. * case it is best to set a fixed value, for example `"75%"`.
  38470. *
  38471. * @sample {highcharts} highcharts/plotoptions/pie-size/
  38472. * Smaller pie
  38473. *
  38474. * @type {number|string|null}
  38475. * @product highcharts
  38476. *
  38477. * @private
  38478. */
  38479. size: null,
  38480. /**
  38481. * Whether to display this particular series or series type in the
  38482. * legend. Since 2.1, pies are not shown in the legend by default.
  38483. *
  38484. * @sample {highcharts} highcharts/plotoptions/series-showinlegend/
  38485. * One series in the legend, one hidden
  38486. *
  38487. * @product highcharts
  38488. *
  38489. * @private
  38490. */
  38491. showInLegend: false,
  38492. /**
  38493. * If a point is sliced, moved out from the center, how many pixels
  38494. * should it be moved?.
  38495. *
  38496. * @sample {highcharts} highcharts/plotoptions/pie-slicedoffset-20/
  38497. * 20px offset
  38498. *
  38499. * @product highcharts
  38500. *
  38501. * @private
  38502. */
  38503. slicedOffset: 10,
  38504. /**
  38505. * The start angle of the pie slices in degrees where 0 is top and 90
  38506. * right.
  38507. *
  38508. * @sample {highcharts} highcharts/plotoptions/pie-startangle-90/
  38509. * Start from right
  38510. *
  38511. * @type {number}
  38512. * @default 0
  38513. * @since 2.3.4
  38514. * @product highcharts
  38515. * @apioption plotOptions.pie.startAngle
  38516. */
  38517. /**
  38518. * Sticky tracking of mouse events. When true, the `mouseOut` event
  38519. * on a series isn't triggered until the mouse moves over another
  38520. * series, or out of the plot area. When false, the `mouseOut` event on
  38521. * a series is triggered when the mouse leaves the area around the
  38522. * series' graph or markers. This also implies the tooltip. When
  38523. * `stickyTracking` is false and `tooltip.shared` is false, the tooltip
  38524. * will be hidden when moving the mouse between series.
  38525. *
  38526. * @product highcharts
  38527. *
  38528. * @private
  38529. */
  38530. stickyTracking: false,
  38531. tooltip: {
  38532. followPointer: true
  38533. },
  38534. /**
  38535. * The color of the border surrounding each slice. When `null`, the
  38536. * border takes the same color as the slice fill. This can be used
  38537. * together with a `borderWidth` to fill drawing gaps created by
  38538. * antialiazing artefacts in borderless pies.
  38539. *
  38540. * In styled mode, the border stroke is given in the `.highcharts-point`
  38541. * class.
  38542. *
  38543. * @sample {highcharts} highcharts/plotoptions/pie-bordercolor-black/
  38544. * Black border
  38545. *
  38546. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  38547. * @default #ffffff
  38548. * @product highcharts
  38549. *
  38550. * @private
  38551. */
  38552. borderColor: '#ffffff',
  38553. /**
  38554. * The width of the border surrounding each slice.
  38555. *
  38556. * When setting the border width to 0, there may be small gaps between
  38557. * the slices due to SVG antialiasing artefacts. To work around this,
  38558. * keep the border width at 0.5 or 1, but set the `borderColor` to
  38559. * `null` instead.
  38560. *
  38561. * In styled mode, the border stroke width is given in the
  38562. * `.highcharts-point` class.
  38563. *
  38564. * @sample {highcharts} highcharts/plotoptions/pie-borderwidth/
  38565. * 3px border
  38566. *
  38567. * @product highcharts
  38568. *
  38569. * @private
  38570. */
  38571. borderWidth: 1,
  38572. /**
  38573. * @ignore-options
  38574. * @private
  38575. */
  38576. lineWidth: void 0,
  38577. states: {
  38578. /**
  38579. * @extends plotOptions.series.states.hover
  38580. * @excluding marker, lineWidth, lineWidthPlus
  38581. * @product highcharts
  38582. */
  38583. hover: {
  38584. /**
  38585. * How much to brighten the point on interaction. Requires the
  38586. * main color to be defined in hex or rgb(a) format.
  38587. *
  38588. * In styled mode, the hover brightness is by default replaced
  38589. * by a fill-opacity given in the `.highcharts-point-hover`
  38590. * class.
  38591. *
  38592. * @sample {highcharts} highcharts/plotoptions/pie-states-hover-brightness/
  38593. * Brightened by 0.5
  38594. *
  38595. * @product highcharts
  38596. */
  38597. brightness: 0.1
  38598. }
  38599. }
  38600. },
  38601. /* eslint-disable valid-jsdoc */
  38602. /**
  38603. * @lends seriesTypes.pie.prototype
  38604. */
  38605. {
  38606. isCartesian: false,
  38607. requireSorting: false,
  38608. directTouch: true,
  38609. noSharedTooltip: true,
  38610. trackerGroups: ['group', 'dataLabelsGroup'],
  38611. axisTypes: [],
  38612. pointAttribs: seriesTypes.column.prototype.pointAttribs,
  38613. /**
  38614. * Animate the pies in
  38615. *
  38616. * @private
  38617. * @function Highcharts.seriesTypes.pie#animate
  38618. *
  38619. * @param {boolean} [init=false]
  38620. */
  38621. animate: function (init) {
  38622. var series = this, points = series.points, startAngleRad = series.startAngleRad;
  38623. if (!init) {
  38624. points.forEach(function (point) {
  38625. var graphic = point.graphic, args = point.shapeArgs;
  38626. if (graphic && args) {
  38627. // start values
  38628. graphic.attr({
  38629. // animate from inner radius (#779)
  38630. r: pick(point.startR, (series.center && series.center[3] / 2)),
  38631. start: startAngleRad,
  38632. end: startAngleRad
  38633. });
  38634. // animate
  38635. graphic.animate({
  38636. r: args.r,
  38637. start: args.start,
  38638. end: args.end
  38639. }, series.options.animation);
  38640. }
  38641. });
  38642. }
  38643. },
  38644. // Define hasData function for non-cartesian series.
  38645. // Returns true if the series has points at all.
  38646. hasData: function () {
  38647. return !!this.processedXData.length; // != 0
  38648. },
  38649. /**
  38650. * Recompute total chart sum and update percentages of points.
  38651. *
  38652. * @private
  38653. * @function Highcharts.seriesTypes.pie#updateTotals
  38654. * @return {void}
  38655. */
  38656. updateTotals: function () {
  38657. var i, total = 0, points = this.points, len = points.length, point, ignoreHiddenPoint = this.options.ignoreHiddenPoint;
  38658. // Get the total sum
  38659. for (i = 0; i < len; i++) {
  38660. point = points[i];
  38661. total += (ignoreHiddenPoint && !point.visible) ?
  38662. 0 :
  38663. point.isNull ?
  38664. 0 :
  38665. point.y;
  38666. }
  38667. this.total = total;
  38668. // Set each point's properties
  38669. for (i = 0; i < len; i++) {
  38670. point = points[i];
  38671. point.percentage =
  38672. (total > 0 && (point.visible || !ignoreHiddenPoint)) ?
  38673. point.y / total * 100 :
  38674. 0;
  38675. point.total = total;
  38676. }
  38677. },
  38678. /**
  38679. * Extend the generatePoints method by adding total and percentage
  38680. * properties to each point
  38681. *
  38682. * @private
  38683. * @function Highcharts.seriesTypes.pie#generatePoints
  38684. * @return {void}
  38685. */
  38686. generatePoints: function () {
  38687. Series.prototype.generatePoints.call(this);
  38688. this.updateTotals();
  38689. },
  38690. /**
  38691. * Utility for getting the x value from a given y, used for
  38692. * anticollision logic in data labels. Added point for using specific
  38693. * points' label distance.
  38694. * @private
  38695. */
  38696. getX: function (y, left, point) {
  38697. var center = this.center,
  38698. // Variable pie has individual radius
  38699. radius = this.radii ?
  38700. this.radii[point.index] :
  38701. center[2] / 2, angle, x;
  38702. angle = Math.asin(clamp((y - center[1]) / (radius + point.labelDistance), -1, 1));
  38703. x = center[0] +
  38704. (left ? -1 : 1) *
  38705. (Math.cos(angle) * (radius + point.labelDistance)) +
  38706. (point.labelDistance > 0 ?
  38707. (left ? -1 : 1) * this.options.dataLabels.padding :
  38708. 0);
  38709. return x;
  38710. },
  38711. /**
  38712. * Do translation for pie slices
  38713. *
  38714. * @private
  38715. * @function Highcharts.seriesTypes.pie#translate
  38716. * @param {Array<number>} [positions]
  38717. * @return {void}
  38718. */
  38719. translate: function (positions) {
  38720. this.generatePoints();
  38721. var series = this, cumulative = 0, precision = 1000, // issue #172
  38722. options = series.options, slicedOffset = options.slicedOffset, connectorOffset = slicedOffset + (options.borderWidth || 0), finalConnectorOffset, start, end, angle, radians = getStartAndEndRadians(options.startAngle, options.endAngle), startAngleRad = series.startAngleRad = radians.start, endAngleRad = series.endAngleRad = radians.end, circ = endAngleRad - startAngleRad, // 2 * Math.PI,
  38723. points = series.points,
  38724. // the x component of the radius vector for a given point
  38725. radiusX, radiusY, labelDistance = options.dataLabels.distance, ignoreHiddenPoint = options.ignoreHiddenPoint, i, len = points.length, point;
  38726. // Get positions - either an integer or a percentage string must be
  38727. // given. If positions are passed as a parameter, we're in a
  38728. // recursive loop for adjusting space for data labels.
  38729. if (!positions) {
  38730. series.center = positions = series.getCenter();
  38731. }
  38732. // Calculate the geometry for each point
  38733. for (i = 0; i < len; i++) {
  38734. point = points[i];
  38735. // set start and end angle
  38736. start = startAngleRad + (cumulative * circ);
  38737. if (!ignoreHiddenPoint || point.visible) {
  38738. cumulative += point.percentage / 100;
  38739. }
  38740. end = startAngleRad + (cumulative * circ);
  38741. // set the shape
  38742. point.shapeType = 'arc';
  38743. point.shapeArgs = {
  38744. x: positions[0],
  38745. y: positions[1],
  38746. r: positions[2] / 2,
  38747. innerR: positions[3] / 2,
  38748. start: Math.round(start * precision) / precision,
  38749. end: Math.round(end * precision) / precision
  38750. };
  38751. // Used for distance calculation for specific point.
  38752. point.labelDistance = pick((point.options.dataLabels &&
  38753. point.options.dataLabels.distance), labelDistance);
  38754. // Compute point.labelDistance if it's defined as percentage
  38755. // of slice radius (#8854)
  38756. point.labelDistance = relativeLength(point.labelDistance, point.shapeArgs.r);
  38757. // Saved for later dataLabels distance calculation.
  38758. series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
  38759. // The angle must stay within -90 and 270 (#2645)
  38760. angle = (end + start) / 2;
  38761. if (angle > 1.5 * Math.PI) {
  38762. angle -= 2 * Math.PI;
  38763. }
  38764. else if (angle < -Math.PI / 2) {
  38765. angle += 2 * Math.PI;
  38766. }
  38767. // Center for the sliced out slice
  38768. point.slicedTranslation = {
  38769. translateX: Math.round(Math.cos(angle) * slicedOffset),
  38770. translateY: Math.round(Math.sin(angle) * slicedOffset)
  38771. };
  38772. // set the anchor point for tooltips
  38773. radiusX = Math.cos(angle) * positions[2] / 2;
  38774. radiusY = Math.sin(angle) * positions[2] / 2;
  38775. point.tooltipPos = [
  38776. positions[0] + radiusX * 0.7,
  38777. positions[1] + radiusY * 0.7
  38778. ];
  38779. point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
  38780. 1 :
  38781. 0;
  38782. point.angle = angle;
  38783. // Set the anchor point for data labels. Use point.labelDistance
  38784. // instead of labelDistance // #1174
  38785. // finalConnectorOffset - not override connectorOffset value.
  38786. finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
  38787. point.labelPosition = {
  38788. natural: {
  38789. // initial position of the data label - it's utilized for
  38790. // finding the final position for the label
  38791. x: positions[0] + radiusX + Math.cos(angle) *
  38792. point.labelDistance,
  38793. y: positions[1] + radiusY + Math.sin(angle) *
  38794. point.labelDistance
  38795. },
  38796. 'final': {
  38797. // used for generating connector path -
  38798. // initialized later in drawDataLabels function
  38799. // x: undefined,
  38800. // y: undefined
  38801. },
  38802. // left - pie on the left side of the data label
  38803. // right - pie on the right side of the data label
  38804. // center - data label overlaps the pie
  38805. alignment: point.labelDistance < 0 ?
  38806. 'center' : point.half ? 'right' : 'left',
  38807. connectorPosition: {
  38808. breakAt: {
  38809. x: positions[0] + radiusX + Math.cos(angle) *
  38810. finalConnectorOffset,
  38811. y: positions[1] + radiusY + Math.sin(angle) *
  38812. finalConnectorOffset
  38813. },
  38814. touchingSliceAt: {
  38815. x: positions[0] + radiusX,
  38816. y: positions[1] + radiusY
  38817. }
  38818. }
  38819. };
  38820. }
  38821. fireEvent(series, 'afterTranslate');
  38822. },
  38823. /**
  38824. * Called internally to draw auxiliary graph in pie-like series in
  38825. * situtation when the default graph is not sufficient enough to present
  38826. * the data well. Auxiliary graph is saved in the same object as
  38827. * regular graph.
  38828. *
  38829. * @private
  38830. * @function Highcharts.seriesTypes.pie#drawEmpty
  38831. */
  38832. drawEmpty: function () {
  38833. var centerX, centerY, start = this.startAngleRad, end = this.endAngleRad, options = this.options;
  38834. // Draw auxiliary graph if there're no visible points.
  38835. if (this.total === 0) {
  38836. centerX = this.center[0];
  38837. centerY = this.center[1];
  38838. if (!this.graph) {
  38839. this.graph = this.chart.renderer
  38840. .arc(centerX, centerY, this.center[1] / 2, 0, start, end)
  38841. .addClass('highcharts-empty-series')
  38842. .add(this.group);
  38843. }
  38844. this.graph.attr({
  38845. d: Highcharts.SVGRenderer.prototype.symbols.arc(centerX, centerY, this.center[2] / 2, 0, {
  38846. start: start,
  38847. end: end,
  38848. innerR: this.center[3] / 2
  38849. })
  38850. });
  38851. if (!this.chart.styledMode) {
  38852. this.graph.attr({
  38853. 'stroke-width': options.borderWidth,
  38854. fill: options.fillColor || 'none',
  38855. stroke: options.color ||
  38856. '#cccccc'
  38857. });
  38858. }
  38859. }
  38860. else if (this.graph) { // Destroy the graph object.
  38861. this.graph = this.graph.destroy();
  38862. }
  38863. },
  38864. /**
  38865. * Draw the data points
  38866. *
  38867. * @private
  38868. * @function Highcharts.seriesTypes.pie#drawPoints
  38869. * @return {void}
  38870. */
  38871. redrawPoints: function () {
  38872. var series = this, chart = series.chart, renderer = chart.renderer, groupTranslation, graphic, pointAttr, shapeArgs, shadow = series.options.shadow;
  38873. this.drawEmpty();
  38874. if (shadow && !series.shadowGroup && !chart.styledMode) {
  38875. series.shadowGroup = renderer.g('shadow')
  38876. .attr({ zIndex: -1 })
  38877. .add(series.group);
  38878. }
  38879. // draw the slices
  38880. series.points.forEach(function (point) {
  38881. var animateTo = {};
  38882. graphic = point.graphic;
  38883. if (!point.isNull && graphic) {
  38884. shapeArgs = point.shapeArgs;
  38885. // If the point is sliced, use special translation, else use
  38886. // plot area translation
  38887. groupTranslation = point.getTranslate();
  38888. if (!chart.styledMode) {
  38889. // Put the shadow behind all points
  38890. var shadowGroup = point.shadowGroup;
  38891. if (shadow && !shadowGroup) {
  38892. shadowGroup = point.shadowGroup = renderer
  38893. .g('shadow')
  38894. .add(series.shadowGroup);
  38895. }
  38896. if (shadowGroup) {
  38897. shadowGroup.attr(groupTranslation);
  38898. }
  38899. pointAttr = series.pointAttribs(point, (point.selected && 'select'));
  38900. }
  38901. // Draw the slice
  38902. if (!point.delayedRendering) {
  38903. graphic
  38904. .setRadialReference(series.center);
  38905. if (!chart.styledMode) {
  38906. merge(true, animateTo, pointAttr);
  38907. }
  38908. merge(true, animateTo, shapeArgs, groupTranslation);
  38909. graphic.animate(animateTo);
  38910. }
  38911. else {
  38912. graphic
  38913. .setRadialReference(series.center)
  38914. .attr(shapeArgs)
  38915. .attr(groupTranslation);
  38916. if (!chart.styledMode) {
  38917. graphic
  38918. .attr(pointAttr)
  38919. .attr({ 'stroke-linejoin': 'round' })
  38920. .shadow(shadow, shadowGroup);
  38921. }
  38922. point.delayedRendering = false;
  38923. }
  38924. graphic.attr({
  38925. visibility: point.visible ? 'inherit' : 'hidden'
  38926. });
  38927. graphic.addClass(point.getClassName());
  38928. }
  38929. else if (graphic) {
  38930. point.graphic = graphic.destroy();
  38931. }
  38932. });
  38933. },
  38934. /**
  38935. * Slices in pie chart are initialized in DOM, but it's shapes and
  38936. * animations are normally run in `drawPoints()`.
  38937. * @private
  38938. */
  38939. drawPoints: function () {
  38940. var renderer = this.chart.renderer;
  38941. this.points.forEach(function (point) {
  38942. // When updating a series between 2d and 3d or cartesian and
  38943. // polar, the shape type changes.
  38944. if (point.graphic && point.hasNewShapeType()) {
  38945. point.graphic = point.graphic.destroy();
  38946. }
  38947. if (!point.graphic) {
  38948. point.graphic = renderer[point.shapeType](point.shapeArgs)
  38949. .add(point.series.group);
  38950. point.delayedRendering = true;
  38951. }
  38952. });
  38953. },
  38954. /**
  38955. * @private
  38956. * @deprecated
  38957. * @function Highcharts.seriesTypes.pie#searchPoint
  38958. */
  38959. searchPoint: noop,
  38960. /**
  38961. * Utility for sorting data labels
  38962. *
  38963. * @private
  38964. * @function Highcharts.seriesTypes.pie#sortByAngle
  38965. * @param {Array<Highcharts.Point>} points
  38966. * @param {number} sign
  38967. * @return {void}
  38968. */
  38969. sortByAngle: function (points, sign) {
  38970. points.sort(function (a, b) {
  38971. return ((typeof a.angle !== 'undefined') &&
  38972. (b.angle - a.angle) * sign);
  38973. });
  38974. },
  38975. /**
  38976. * Use a simple symbol from LegendSymbolMixin.
  38977. *
  38978. * @private
  38979. * @borrows Highcharts.LegendSymbolMixin.drawRectangle as Highcharts.seriesTypes.pie#drawLegendSymbol
  38980. */
  38981. drawLegendSymbol: LegendSymbolMixin.drawRectangle,
  38982. /**
  38983. * Use the getCenter method from drawLegendSymbol.
  38984. *
  38985. * @private
  38986. * @borrows Highcharts.CenteredSeriesMixin.getCenter as Highcharts.seriesTypes.pie#getCenter
  38987. */
  38988. getCenter: CenteredSeriesMixin.getCenter,
  38989. /**
  38990. * Pies don't have point marker symbols.
  38991. *
  38992. * @deprecated
  38993. * @private
  38994. * @function Highcharts.seriesTypes.pie#getSymbol
  38995. */
  38996. getSymbol: noop,
  38997. /**
  38998. * @private
  38999. * @type {null}
  39000. */
  39001. drawGraph: null
  39002. },
  39003. /**
  39004. * @lends seriesTypes.pie.prototype.pointClass.prototype
  39005. */
  39006. {
  39007. /**
  39008. * Initialize the pie slice
  39009. *
  39010. * @private
  39011. * @function Highcharts.seriesTypes.pie#pointClass#init
  39012. * @return {Highcharts.Point}
  39013. */
  39014. init: function () {
  39015. Point.prototype.init.apply(this, arguments);
  39016. var point = this, toggleSlice;
  39017. point.name = pick(point.name, 'Slice');
  39018. // add event listener for select
  39019. toggleSlice = function (e) {
  39020. point.slice(e.type === 'select');
  39021. };
  39022. addEvent(point, 'select', toggleSlice);
  39023. addEvent(point, 'unselect', toggleSlice);
  39024. return point;
  39025. },
  39026. /**
  39027. * Negative points are not valid (#1530, #3623, #5322)
  39028. *
  39029. * @private
  39030. * @function Highcharts.seriesTypes.pie#pointClass#isValid
  39031. * @return {boolean}
  39032. */
  39033. isValid: function () {
  39034. return isNumber(this.y) && this.y >= 0;
  39035. },
  39036. /**
  39037. * Toggle the visibility of the pie slice
  39038. *
  39039. * @private
  39040. * @function Highcharts.seriesTypes.pie#pointClass#setVisible
  39041. * @param {boolean} vis
  39042. * Whether to show the slice or not. If undefined, the visibility
  39043. * is toggled.
  39044. * @param {boolean} [redraw=false]
  39045. * @return {void}
  39046. */
  39047. setVisible: function (vis, redraw) {
  39048. var point = this, series = point.series, chart = series.chart, ignoreHiddenPoint = series.options.ignoreHiddenPoint;
  39049. redraw = pick(redraw, ignoreHiddenPoint);
  39050. if (vis !== point.visible) {
  39051. // If called without an argument, toggle visibility
  39052. point.visible = point.options.visible = vis =
  39053. typeof vis === 'undefined' ? !point.visible : vis;
  39054. // update userOptions.data
  39055. series.options.data[series.data.indexOf(point)] =
  39056. point.options;
  39057. // Show and hide associated elements. This is performed
  39058. // regardless of redraw or not, because chart.redraw only
  39059. // handles full series.
  39060. ['graphic', 'dataLabel', 'connector', 'shadowGroup'].forEach(function (key) {
  39061. if (point[key]) {
  39062. point[key][vis ? 'show' : 'hide'](true);
  39063. }
  39064. });
  39065. if (point.legendItem) {
  39066. chart.legend.colorizeItem(point, vis);
  39067. }
  39068. // #4170, hide halo after hiding point
  39069. if (!vis && point.state === 'hover') {
  39070. point.setState('');
  39071. }
  39072. // Handle ignore hidden slices
  39073. if (ignoreHiddenPoint) {
  39074. series.isDirty = true;
  39075. }
  39076. if (redraw) {
  39077. chart.redraw();
  39078. }
  39079. }
  39080. },
  39081. /**
  39082. * Set or toggle whether the slice is cut out from the pie
  39083. *
  39084. * @private
  39085. * @function Highcharts.seriesTypes.pie#pointClass#slice
  39086. * @param {boolean} sliced
  39087. * When undefined, the slice state is toggled.
  39088. * @param {boolean} redraw
  39089. * Whether to redraw the chart. True by default.
  39090. * @param {boolean|Highcharts.AnimationOptionsObject}
  39091. * Animation options.
  39092. * @return {void}
  39093. */
  39094. slice: function (sliced, redraw, animation) {
  39095. var point = this, series = point.series, chart = series.chart;
  39096. setAnimation(animation, chart);
  39097. // redraw is true by default
  39098. redraw = pick(redraw, true);
  39099. /**
  39100. * Pie series only. Whether to display a slice offset from the
  39101. * center.
  39102. * @name Highcharts.Point#sliced
  39103. * @type {boolean|undefined}
  39104. */
  39105. // if called without an argument, toggle
  39106. point.sliced = point.options.sliced = sliced =
  39107. defined(sliced) ? sliced : !point.sliced;
  39108. // update userOptions.data
  39109. series.options.data[series.data.indexOf(point)] =
  39110. point.options;
  39111. if (point.graphic) {
  39112. point.graphic.animate(this.getTranslate());
  39113. }
  39114. if (point.shadowGroup) {
  39115. point.shadowGroup.animate(this.getTranslate());
  39116. }
  39117. },
  39118. /**
  39119. * @private
  39120. * @function Highcharts.seriesTypes.pie#pointClass#getTranslate
  39121. * @return {Highcharts.TranslationAttributes}
  39122. */
  39123. getTranslate: function () {
  39124. return this.sliced ? this.slicedTranslation : {
  39125. translateX: 0,
  39126. translateY: 0
  39127. };
  39128. },
  39129. /**
  39130. * @private
  39131. * @function Highcharts.seriesTypes.pie#pointClass#haloPath
  39132. * @param {number} size
  39133. * @return {Highcharts.SVGPathArray}
  39134. */
  39135. haloPath: function (size) {
  39136. var shapeArgs = this.shapeArgs;
  39137. return this.sliced || !this.visible ?
  39138. [] :
  39139. this.series.chart.renderer.symbols.arc(shapeArgs.x, shapeArgs.y, shapeArgs.r + size, shapeArgs.r + size, {
  39140. // Substract 1px to ensure the background is not bleeding
  39141. // through between the halo and the slice (#7495).
  39142. innerR: shapeArgs.r - 1,
  39143. start: shapeArgs.start,
  39144. end: shapeArgs.end
  39145. });
  39146. },
  39147. connectorShapes: {
  39148. // only one available before v7.0.0
  39149. fixedOffset: function (labelPosition, connectorPosition, options) {
  39150. var breakAt = connectorPosition.breakAt, touchingSliceAt = connectorPosition.touchingSliceAt, lineSegment = options.softConnector ? [
  39151. 'C',
  39152. // 1st control point (of the curve)
  39153. labelPosition.x +
  39154. // 5 gives the connector a little horizontal bend
  39155. (labelPosition.alignment === 'left' ? -5 : 5),
  39156. labelPosition.y,
  39157. 2 * breakAt.x - touchingSliceAt.x,
  39158. 2 * breakAt.y - touchingSliceAt.y,
  39159. breakAt.x,
  39160. breakAt.y //
  39161. ] : [
  39162. 'L',
  39163. breakAt.x,
  39164. breakAt.y
  39165. ];
  39166. // assemble the path
  39167. return ([
  39168. ['M', labelPosition.x, labelPosition.y],
  39169. lineSegment,
  39170. ['L', touchingSliceAt.x, touchingSliceAt.y]
  39171. ]);
  39172. },
  39173. straight: function (labelPosition, connectorPosition) {
  39174. var touchingSliceAt = connectorPosition.touchingSliceAt;
  39175. // direct line to the slice
  39176. return [
  39177. ['M', labelPosition.x, labelPosition.y],
  39178. ['L', touchingSliceAt.x, touchingSliceAt.y]
  39179. ];
  39180. },
  39181. crookedLine: function (labelPosition, connectorPosition, options) {
  39182. var touchingSliceAt = connectorPosition.touchingSliceAt, series = this.series, pieCenterX = series.center[0], plotWidth = series.chart.plotWidth, plotLeft = series.chart.plotLeft, alignment = labelPosition.alignment, radius = this.shapeArgs.r, crookDistance = relativeLength(// % to fraction
  39183. options.crookDistance, 1), crookX = alignment === 'left' ?
  39184. pieCenterX + radius + (plotWidth + plotLeft -
  39185. pieCenterX - radius) * (1 - crookDistance) :
  39186. plotLeft + (pieCenterX - radius) * crookDistance, segmentWithCrook = [
  39187. 'L',
  39188. crookX,
  39189. labelPosition.y
  39190. ], useCrook = true;
  39191. // crookedLine formula doesn't make sense if the path overlaps
  39192. // the label - use straight line instead in that case
  39193. if (alignment === 'left' ?
  39194. (crookX > labelPosition.x || crookX < touchingSliceAt.x) :
  39195. (crookX < labelPosition.x || crookX > touchingSliceAt.x)) {
  39196. useCrook = false;
  39197. }
  39198. // assemble the path
  39199. var path = [
  39200. ['M', labelPosition.x, labelPosition.y]
  39201. ];
  39202. if (useCrook) {
  39203. path.push(segmentWithCrook);
  39204. }
  39205. path.push(['L', touchingSliceAt.x, touchingSliceAt.y]);
  39206. return path;
  39207. }
  39208. },
  39209. /**
  39210. * Extendable method for getting the path of the connector between the
  39211. * data label and the pie slice.
  39212. */
  39213. getConnectorPath: function () {
  39214. var labelPosition = this.labelPosition, options = this.series.options.dataLabels, connectorShape = options.connectorShape, predefinedShapes = this.connectorShapes;
  39215. // find out whether to use the predefined shape
  39216. if (predefinedShapes[connectorShape]) {
  39217. connectorShape = predefinedShapes[connectorShape];
  39218. }
  39219. return connectorShape.call(this, {
  39220. // pass simplified label position object for user's convenience
  39221. x: labelPosition.final.x,
  39222. y: labelPosition.final.y,
  39223. alignment: labelPosition.alignment
  39224. }, labelPosition.connectorPosition, options);
  39225. }
  39226. }
  39227. /* eslint-enable valid-jsdoc */
  39228. );
  39229. /**
  39230. * A `pie` series. If the [type](#series.pie.type) option is not specified,
  39231. * it is inherited from [chart.type](#chart.type).
  39232. *
  39233. * @extends series,plotOptions.pie
  39234. * @excluding dataParser, dataURL, stack, xAxis, yAxis, dataSorting, step
  39235. * @product highcharts
  39236. * @apioption series.pie
  39237. */
  39238. /**
  39239. * An array of data points for the series. For the `pie` series type,
  39240. * points can be given in the following ways:
  39241. *
  39242. * 1. An array of numerical values. In this case, the numerical values will be
  39243. * interpreted as `y` options. Example:
  39244. * ```js
  39245. * data: [0, 5, 3, 5]
  39246. * ```
  39247. *
  39248. * 2. An array of objects with named values. The following snippet shows only a
  39249. * few settings, see the complete options set below. If the total number of
  39250. * data points exceeds the series'
  39251. * [turboThreshold](#series.pie.turboThreshold),
  39252. * this option is not available.
  39253. * ```js
  39254. * data: [{
  39255. * y: 1,
  39256. * name: "Point2",
  39257. * color: "#00FF00"
  39258. * }, {
  39259. * y: 7,
  39260. * name: "Point1",
  39261. * color: "#FF00FF"
  39262. * }]
  39263. * ```
  39264. *
  39265. * @sample {highcharts} highcharts/chart/reflow-true/
  39266. * Numerical values
  39267. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  39268. * Arrays of numeric x and y
  39269. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  39270. * Arrays of datetime x and y
  39271. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  39272. * Arrays of point.name and y
  39273. * @sample {highcharts} highcharts/series/data-array-of-objects/
  39274. * Config objects
  39275. *
  39276. * @type {Array<number|Array<string,(number|null)>|null|*>}
  39277. * @extends series.line.data
  39278. * @excluding marker, x
  39279. * @product highcharts
  39280. * @apioption series.pie.data
  39281. */
  39282. /**
  39283. * @type {Highcharts.SeriesPieDataLabelsOptionsObject}
  39284. * @product highcharts
  39285. * @apioption series.pie.data.dataLabels
  39286. */
  39287. /**
  39288. * The sequential index of the data point in the legend.
  39289. *
  39290. * @type {number}
  39291. * @product highcharts
  39292. * @apioption series.pie.data.legendIndex
  39293. */
  39294. /**
  39295. * Whether to display a slice offset from the center.
  39296. *
  39297. * @sample {highcharts} highcharts/point/sliced/
  39298. * One sliced point
  39299. *
  39300. * @type {boolean}
  39301. * @product highcharts
  39302. * @apioption series.pie.data.sliced
  39303. */
  39304. /**
  39305. * @excluding legendItemClick
  39306. * @product highcharts
  39307. * @apioption series.pie.events
  39308. */
  39309. ''; // placeholder for transpiled doclets above
  39310. });
  39311. _registerModule(_modules, 'parts/DataLabels.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  39312. /* *
  39313. *
  39314. * (c) 2010-2020 Torstein Honsi
  39315. *
  39316. * License: www.highcharts.com/license
  39317. *
  39318. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39319. *
  39320. * */
  39321. var noop = H.noop, seriesTypes = H.seriesTypes;
  39322. var animObject = U.animObject, arrayMax = U.arrayMax, clamp = U.clamp, defined = U.defined, extend = U.extend, fireEvent = U.fireEvent, format = U.format, isArray = U.isArray, merge = U.merge, objectEach = U.objectEach, pick = U.pick, relativeLength = U.relativeLength, splat = U.splat, stableSort = U.stableSort;
  39323. /**
  39324. * Callback JavaScript function to format the data label as a string. Note that
  39325. * if a `format` is defined, the format takes precedence and the formatter is
  39326. * ignored.
  39327. *
  39328. * @callback Highcharts.DataLabelsFormatterCallbackFunction
  39329. *
  39330. * @param {Highcharts.PointLabelObject} this
  39331. * Data label context to format
  39332. *
  39333. * @param {Highcharts.DataLabelsOptions} options
  39334. * [API options](/highcharts/plotOptions.series.dataLabels) of the data label
  39335. *
  39336. * @return {number|string|null|undefined}
  39337. * Formatted data label text
  39338. */
  39339. /**
  39340. * Values for handling data labels that flow outside the plot area.
  39341. *
  39342. * @typedef {"allow"|"justify"} Highcharts.DataLabelsOverflowValue
  39343. */
  39344. var Series = H.Series;
  39345. /* eslint-disable valid-jsdoc */
  39346. /**
  39347. * General distribution algorithm for distributing labels of differing size
  39348. * along a confined length in two dimensions. The algorithm takes an array of
  39349. * objects containing a size, a target and a rank. It will place the labels as
  39350. * close as possible to their targets, skipping the lowest ranked labels if
  39351. * necessary.
  39352. *
  39353. * @private
  39354. * @function Highcharts.distribute
  39355. * @param {Highcharts.DataLabelsBoxArray} boxes
  39356. * @param {number} len
  39357. * @param {number} [maxDistance]
  39358. * @return {void}
  39359. */
  39360. H.distribute = function (boxes, len, maxDistance) {
  39361. var i, overlapping = true, origBoxes = boxes, // Original array will be altered with added .pos
  39362. restBoxes = [], // The outranked overshoot
  39363. box, target, total = 0, reducedLen = origBoxes.reducedLen || len;
  39364. /**
  39365. * @private
  39366. */
  39367. function sortByTarget(a, b) {
  39368. return a.target - b.target;
  39369. }
  39370. // If the total size exceeds the len, remove those boxes with the lowest
  39371. // rank
  39372. i = boxes.length;
  39373. while (i--) {
  39374. total += boxes[i].size;
  39375. }
  39376. // Sort by rank, then slice away overshoot
  39377. if (total > reducedLen) {
  39378. stableSort(boxes, function (a, b) {
  39379. return (b.rank || 0) - (a.rank || 0);
  39380. });
  39381. i = 0;
  39382. total = 0;
  39383. while (total <= reducedLen) {
  39384. total += boxes[i].size;
  39385. i++;
  39386. }
  39387. restBoxes = boxes.splice(i - 1, boxes.length);
  39388. }
  39389. // Order by target
  39390. stableSort(boxes, sortByTarget);
  39391. // So far we have been mutating the original array. Now
  39392. // create a copy with target arrays
  39393. boxes = boxes.map(function (box) {
  39394. return {
  39395. size: box.size,
  39396. targets: [box.target],
  39397. align: pick(box.align, 0.5)
  39398. };
  39399. });
  39400. while (overlapping) {
  39401. // Initial positions: target centered in box
  39402. i = boxes.length;
  39403. while (i--) {
  39404. box = boxes[i];
  39405. // Composite box, average of targets
  39406. target = (Math.min.apply(0, box.targets) +
  39407. Math.max.apply(0, box.targets)) / 2;
  39408. box.pos = clamp(target - box.size * box.align, 0, len - box.size);
  39409. }
  39410. // Detect overlap and join boxes
  39411. i = boxes.length;
  39412. overlapping = false;
  39413. while (i--) {
  39414. // Overlap
  39415. if (i > 0 &&
  39416. boxes[i - 1].pos + boxes[i - 1].size >
  39417. boxes[i].pos) {
  39418. // Add this size to the previous box
  39419. boxes[i - 1].size += boxes[i].size;
  39420. boxes[i - 1].targets = boxes[i - 1]
  39421. .targets
  39422. .concat(boxes[i].targets);
  39423. boxes[i - 1].align = 0.5;
  39424. // Overlapping right, push left
  39425. if (boxes[i - 1].pos + boxes[i - 1].size > len) {
  39426. boxes[i - 1].pos = len - boxes[i - 1].size;
  39427. }
  39428. boxes.splice(i, 1); // Remove this item
  39429. overlapping = true;
  39430. }
  39431. }
  39432. }
  39433. // Add the rest (hidden boxes)
  39434. origBoxes.push.apply(origBoxes, restBoxes);
  39435. // Now the composite boxes are placed, we need to put the original boxes
  39436. // within them
  39437. i = 0;
  39438. boxes.some(function (box) {
  39439. var posInCompositeBox = 0;
  39440. if (box.targets.some(function () {
  39441. origBoxes[i].pos = box.pos + posInCompositeBox;
  39442. // If the distance between the position and the target exceeds
  39443. // maxDistance, abort the loop and decrease the length in increments
  39444. // of 10% to recursively reduce the number of visible boxes by
  39445. // rank. Once all boxes are within the maxDistance, we're good.
  39446. if (typeof maxDistance !== 'undefined' &&
  39447. Math.abs(origBoxes[i].pos - origBoxes[i].target) > maxDistance) {
  39448. // Reset the positions that are already set
  39449. origBoxes.slice(0, i + 1).forEach(function (box) {
  39450. delete box.pos;
  39451. });
  39452. // Try with a smaller length
  39453. origBoxes.reducedLen =
  39454. (origBoxes.reducedLen || len) - (len * 0.1);
  39455. // Recurse
  39456. if (origBoxes.reducedLen > len * 0.1) {
  39457. H.distribute(origBoxes, len, maxDistance);
  39458. }
  39459. // Exceeded maxDistance => abort
  39460. return true;
  39461. }
  39462. posInCompositeBox += origBoxes[i].size;
  39463. i++;
  39464. })) {
  39465. // Exceeded maxDistance => abort
  39466. return true;
  39467. }
  39468. });
  39469. // Add the rest (hidden) boxes and sort by target
  39470. stableSort(origBoxes, sortByTarget);
  39471. };
  39472. /**
  39473. * Draw the data labels
  39474. *
  39475. * @private
  39476. * @function Highcharts.Series#drawDataLabels
  39477. * @return {void}
  39478. * @fires Highcharts.Series#event:afterDrawDataLabels
  39479. */
  39480. Series.prototype.drawDataLabels = function () {
  39481. var series = this, chart = series.chart, seriesOptions = series.options, seriesDlOptions = seriesOptions.dataLabels, points = series.points, pointOptions, hasRendered = series.hasRendered || 0, dataLabelsGroup, seriesAnimDuration = animObject(seriesOptions.animation).duration, fadeInDuration = Math.min(seriesAnimDuration, 200), defer = !chart.renderer.forExport && pick(seriesDlOptions.defer, fadeInDuration > 0), renderer = chart.renderer;
  39482. /**
  39483. * Handle the dataLabels.filter option.
  39484. * @private
  39485. */
  39486. function applyFilter(point, options) {
  39487. var filter = options.filter, op, prop, val;
  39488. if (filter) {
  39489. op = filter.operator;
  39490. prop = point[filter.property];
  39491. val = filter.value;
  39492. if ((op === '>' && prop > val) ||
  39493. (op === '<' && prop < val) ||
  39494. (op === '>=' && prop >= val) ||
  39495. (op === '<=' && prop <= val) ||
  39496. (op === '==' && prop == val) || // eslint-disable-line eqeqeq
  39497. (op === '===' && prop === val)) {
  39498. return true;
  39499. }
  39500. return false;
  39501. }
  39502. return true;
  39503. }
  39504. /**
  39505. * Merge two objects that can be arrays. If one of them is an array, the
  39506. * other is merged into each element. If both are arrays, each element is
  39507. * merged by index. If neither are arrays, we use normal merge.
  39508. * @private
  39509. */
  39510. function mergeArrays(one, two) {
  39511. var res = [], i;
  39512. if (isArray(one) && !isArray(two)) {
  39513. res = one.map(function (el) {
  39514. return merge(el, two);
  39515. });
  39516. }
  39517. else if (isArray(two) && !isArray(one)) {
  39518. res = two.map(function (el) {
  39519. return merge(one, el);
  39520. });
  39521. }
  39522. else if (!isArray(one) && !isArray(two)) {
  39523. res = merge(one, two);
  39524. }
  39525. else {
  39526. i = Math.max(one.length, two.length);
  39527. while (i--) {
  39528. res[i] = merge(one[i], two[i]);
  39529. }
  39530. }
  39531. return res;
  39532. }
  39533. // Merge in plotOptions.dataLabels for series
  39534. seriesDlOptions = mergeArrays(mergeArrays(chart.options.plotOptions &&
  39535. chart.options.plotOptions.series &&
  39536. chart.options.plotOptions.series.dataLabels, chart.options.plotOptions &&
  39537. chart.options.plotOptions[series.type] &&
  39538. chart.options.plotOptions[series.type].dataLabels), seriesDlOptions);
  39539. fireEvent(this, 'drawDataLabels');
  39540. if (isArray(seriesDlOptions) ||
  39541. seriesDlOptions.enabled ||
  39542. series._hasPointLabels) {
  39543. // Create a separate group for the data labels to avoid rotation
  39544. dataLabelsGroup = series.plotGroup('dataLabelsGroup', 'data-labels', defer && !hasRendered ? 'hidden' : 'inherit', // #5133, #10220
  39545. seriesDlOptions.zIndex || 6);
  39546. if (defer) {
  39547. dataLabelsGroup.attr({ opacity: +hasRendered }); // #3300
  39548. if (!hasRendered) {
  39549. setTimeout(function () {
  39550. var group = series.dataLabelsGroup;
  39551. if (group) {
  39552. if (series.visible) { // #2597, #3023, #3024
  39553. dataLabelsGroup.show(true);
  39554. }
  39555. group[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, { duration: fadeInDuration });
  39556. }
  39557. }, seriesAnimDuration - fadeInDuration);
  39558. }
  39559. }
  39560. // Make the labels for each point
  39561. points.forEach(function (point) {
  39562. // Merge in series options for the point.
  39563. // @note dataLabelAttribs (like pointAttribs) would eradicate
  39564. // the need for dlOptions, and simplify the section below.
  39565. pointOptions = splat(mergeArrays(seriesDlOptions, point.dlOptions || // dlOptions is used in treemaps
  39566. (point.options && point.options.dataLabels)));
  39567. // Handle each individual data label for this point
  39568. pointOptions.forEach(function (labelOptions, i) {
  39569. // Options for one datalabel
  39570. var labelEnabled = (labelOptions.enabled &&
  39571. // #2282, #4641, #7112, #10049
  39572. (!point.isNull || point.dataLabelOnNull) &&
  39573. applyFilter(point, labelOptions)), labelConfig, formatString, labelText, style, rotation, attr, dataLabel = point.dataLabels ? point.dataLabels[i] :
  39574. point.dataLabel, connector = point.connectors ? point.connectors[i] :
  39575. point.connector, labelDistance = pick(labelOptions.distance, point.labelDistance), isNew = !dataLabel;
  39576. if (labelEnabled) {
  39577. // Create individual options structure that can be extended
  39578. // without affecting others
  39579. labelConfig = point.getLabelConfig();
  39580. formatString = pick(labelOptions[point.formatPrefix + 'Format'], labelOptions.format);
  39581. labelText = defined(formatString) ?
  39582. format(formatString, labelConfig, chart) :
  39583. (labelOptions[point.formatPrefix + 'Formatter'] ||
  39584. labelOptions.formatter).call(labelConfig, labelOptions);
  39585. style = labelOptions.style;
  39586. rotation = labelOptions.rotation;
  39587. if (!chart.styledMode) {
  39588. // Determine the color
  39589. style.color = pick(labelOptions.color, style.color, series.color, '#000000');
  39590. // Get automated contrast color
  39591. if (style.color === 'contrast') {
  39592. point.contrastColor = renderer.getContrast((point.color || series.color));
  39593. style.color = (!defined(labelDistance) &&
  39594. labelOptions.inside) ||
  39595. labelDistance < 0 ||
  39596. !!seriesOptions.stacking ?
  39597. point.contrastColor :
  39598. '#000000';
  39599. }
  39600. else {
  39601. delete point.contrastColor;
  39602. }
  39603. if (seriesOptions.cursor) {
  39604. style.cursor = seriesOptions.cursor;
  39605. }
  39606. }
  39607. attr = {
  39608. r: labelOptions.borderRadius || 0,
  39609. rotation: rotation,
  39610. padding: labelOptions.padding,
  39611. zIndex: 1
  39612. };
  39613. if (!chart.styledMode) {
  39614. attr.fill = labelOptions.backgroundColor;
  39615. attr.stroke = labelOptions.borderColor;
  39616. attr['stroke-width'] = labelOptions.borderWidth;
  39617. }
  39618. // Remove unused attributes (#947)
  39619. objectEach(attr, function (val, name) {
  39620. if (typeof val === 'undefined') {
  39621. delete attr[name];
  39622. }
  39623. });
  39624. }
  39625. // If the point is outside the plot area, destroy it. #678, #820
  39626. if (dataLabel && (!labelEnabled || !defined(labelText))) {
  39627. point.dataLabel =
  39628. point.dataLabel && point.dataLabel.destroy();
  39629. if (point.dataLabels) {
  39630. // Remove point.dataLabels if this was the last one
  39631. if (point.dataLabels.length === 1) {
  39632. delete point.dataLabels;
  39633. }
  39634. else {
  39635. delete point.dataLabels[i];
  39636. }
  39637. }
  39638. if (!i) {
  39639. delete point.dataLabel;
  39640. }
  39641. if (connector) {
  39642. point.connector = point.connector.destroy();
  39643. if (point.connectors) {
  39644. // Remove point.connectors if this was the last one
  39645. if (point.connectors.length === 1) {
  39646. delete point.connectors;
  39647. }
  39648. else {
  39649. delete point.connectors[i];
  39650. }
  39651. }
  39652. }
  39653. // Individual labels are disabled if the are explicitly disabled
  39654. // in the point options, or if they fall outside the plot area.
  39655. }
  39656. else if (labelEnabled && defined(labelText)) {
  39657. if (!dataLabel) {
  39658. // Create new label element
  39659. point.dataLabels = point.dataLabels || [];
  39660. dataLabel = point.dataLabels[i] = rotation ?
  39661. // Labels don't rotate, use text element
  39662. renderer.text(labelText, 0, -9999, labelOptions.useHTML)
  39663. .addClass('highcharts-data-label') :
  39664. // We can use label
  39665. renderer.label(labelText, 0, -9999, labelOptions.shape, null, null, labelOptions.useHTML, null, 'data-label');
  39666. // Store for backwards compatibility
  39667. if (!i) {
  39668. point.dataLabel = dataLabel;
  39669. }
  39670. dataLabel.addClass(' highcharts-data-label-color-' + point.colorIndex +
  39671. ' ' + (labelOptions.className || '') +
  39672. ( // #3398
  39673. labelOptions.useHTML ?
  39674. ' highcharts-tracker' :
  39675. ''));
  39676. }
  39677. else {
  39678. // Use old element and just update text
  39679. attr.text = labelText;
  39680. }
  39681. // Store data label options for later access
  39682. dataLabel.options = labelOptions;
  39683. dataLabel.attr(attr);
  39684. if (!chart.styledMode) {
  39685. // Styles must be applied before add in order to read
  39686. // text bounding box
  39687. dataLabel.css(style).shadow(labelOptions.shadow);
  39688. }
  39689. if (!dataLabel.added) {
  39690. dataLabel.add(dataLabelsGroup);
  39691. }
  39692. if (labelOptions.textPath && !labelOptions.useHTML) {
  39693. dataLabel.setTextPath((point.getDataLabelPath &&
  39694. point.getDataLabelPath(dataLabel)) || point.graphic, labelOptions.textPath);
  39695. if (point.dataLabelPath &&
  39696. !labelOptions.textPath.enabled) {
  39697. // clean the DOM
  39698. point.dataLabelPath = point.dataLabelPath.destroy();
  39699. }
  39700. }
  39701. // Now the data label is created and placed at 0,0, so we
  39702. // need to align it
  39703. series.alignDataLabel(point, dataLabel, labelOptions, null, isNew);
  39704. }
  39705. });
  39706. });
  39707. }
  39708. fireEvent(this, 'afterDrawDataLabels');
  39709. };
  39710. /**
  39711. * Align each individual data label.
  39712. *
  39713. * @private
  39714. * @function Highcharts.Series#alignDataLabel
  39715. * @param {Highcharts.Point} point
  39716. * @param {Highcharts.SVGElement} dataLabel
  39717. * @param {Highcharts.DataLabelsOptions} options
  39718. * @param {Highcharts.BBoxObject} alignTo
  39719. * @param {boolean} [isNew]
  39720. * @return {void}
  39721. */
  39722. Series.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
  39723. var series = this, chart = this.chart, inverted = this.isCartesian && chart.inverted, enabledDataSorting = this.enabledDataSorting, plotX = pick(point.dlBox && point.dlBox.centerX, point.plotX, -9999), plotY = pick(point.plotY, -9999), bBox = dataLabel.getBBox(), baseline, rotation = options.rotation, normRotation, negRotation, align = options.align, rotCorr, // rotation correction
  39724. isInsidePlot = chart.isInsidePlot(plotX, Math.round(plotY), inverted),
  39725. // Math.round for rounding errors (#2683), alignTo to allow column
  39726. // labels (#2700)
  39727. alignAttr, // the final position;
  39728. justify = pick(options.overflow, (enabledDataSorting ? 'none' : 'justify')) === 'justify', visible = this.visible &&
  39729. point.visible !== false &&
  39730. (point.series.forceDL ||
  39731. (enabledDataSorting && !justify) ||
  39732. isInsidePlot ||
  39733. (
  39734. // If the data label is inside the align box, it is enough
  39735. // that parts of the align box is inside the plot area
  39736. // (#12370)
  39737. options.inside && alignTo && chart.isInsidePlot(plotX, inverted ?
  39738. alignTo.x + 1 :
  39739. alignTo.y + alignTo.height - 1, inverted))), setStartPos = function (alignOptions) {
  39740. if (enabledDataSorting && series.xAxis && !justify) {
  39741. series.setDataLabelStartPos(point, dataLabel, isNew, isInsidePlot, alignOptions);
  39742. }
  39743. };
  39744. if (visible) {
  39745. baseline = chart.renderer.fontMetrics(chart.styledMode ? void 0 : options.style.fontSize, dataLabel).b;
  39746. // The alignment box is a singular point
  39747. alignTo = extend({
  39748. x: inverted ? this.yAxis.len - plotY : plotX,
  39749. y: Math.round(inverted ? this.xAxis.len - plotX : plotY),
  39750. width: 0,
  39751. height: 0
  39752. }, alignTo);
  39753. // Add the text size for alignment calculation
  39754. extend(options, {
  39755. width: bBox.width,
  39756. height: bBox.height
  39757. });
  39758. // Allow a hook for changing alignment in the last moment, then do the
  39759. // alignment
  39760. if (rotation) {
  39761. justify = false; // Not supported for rotated text
  39762. rotCorr = chart.renderer.rotCorr(baseline, rotation); // #3723
  39763. alignAttr = {
  39764. x: (alignTo.x +
  39765. (options.x || 0) +
  39766. alignTo.width / 2 +
  39767. rotCorr.x),
  39768. y: (alignTo.y +
  39769. (options.y || 0) +
  39770. { top: 0, middle: 0.5, bottom: 1 }[options.verticalAlign] *
  39771. alignTo.height)
  39772. };
  39773. setStartPos(alignAttr); // data sorting
  39774. dataLabel[isNew ? 'attr' : 'animate'](alignAttr)
  39775. .attr({
  39776. align: align
  39777. });
  39778. // Compensate for the rotated label sticking out on the sides
  39779. normRotation = (rotation + 720) % 360;
  39780. negRotation = normRotation > 180 && normRotation < 360;
  39781. if (align === 'left') {
  39782. alignAttr.y -= negRotation ? bBox.height : 0;
  39783. }
  39784. else if (align === 'center') {
  39785. alignAttr.x -= bBox.width / 2;
  39786. alignAttr.y -= bBox.height / 2;
  39787. }
  39788. else if (align === 'right') {
  39789. alignAttr.x -= bBox.width;
  39790. alignAttr.y -= negRotation ? 0 : bBox.height;
  39791. }
  39792. dataLabel.placed = true;
  39793. dataLabel.alignAttr = alignAttr;
  39794. }
  39795. else {
  39796. setStartPos(alignTo); // data sorting
  39797. dataLabel.align(options, null, alignTo);
  39798. alignAttr = dataLabel.alignAttr;
  39799. }
  39800. // Handle justify or crop
  39801. if (justify && alignTo.height >= 0) { // #8830
  39802. this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew);
  39803. // Now check that the data label is within the plot area
  39804. }
  39805. else if (pick(options.crop, true)) {
  39806. visible =
  39807. chart.isInsidePlot(alignAttr.x, alignAttr.y) &&
  39808. chart.isInsidePlot(alignAttr.x + bBox.width, alignAttr.y + bBox.height);
  39809. }
  39810. // When we're using a shape, make it possible with a connector or an
  39811. // arrow pointing to thie point
  39812. if (options.shape && !rotation) {
  39813. dataLabel[isNew ? 'attr' : 'animate']({
  39814. anchorX: inverted ?
  39815. chart.plotWidth - point.plotY :
  39816. point.plotX,
  39817. anchorY: inverted ?
  39818. chart.plotHeight - point.plotX :
  39819. point.plotY
  39820. });
  39821. }
  39822. }
  39823. // To use alignAttr property in hideOverlappingLabels
  39824. if (isNew && enabledDataSorting) {
  39825. dataLabel.placed = false;
  39826. }
  39827. // Show or hide based on the final aligned position
  39828. if (!visible && (!enabledDataSorting || justify)) {
  39829. dataLabel.hide(true);
  39830. dataLabel.placed = false; // don't animate back in
  39831. }
  39832. };
  39833. /**
  39834. * Set starting position for data label sorting animation.
  39835. *
  39836. * @private
  39837. * @function Highcharts.Series#setDataLabelStartPos
  39838. * @param {Highcharts.SVGElement} dataLabel
  39839. * @param {Highcharts.ColumnPoint} point
  39840. * @param {boolean | undefined} [isNew]
  39841. * @param {boolean} [isInside]
  39842. * @param {Highcharts.AlignObject} [alignOptions]
  39843. *
  39844. * @return {void}
  39845. */
  39846. Series.prototype.setDataLabelStartPos = function (point, dataLabel, isNew, isInside, alignOptions) {
  39847. var 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, startXPos, startYPos;
  39848. startXPos = inverted ?
  39849. alignOptions.x :
  39850. (reversed ?
  39851. -labelCenter - halfWidth :
  39852. xAxis.width - labelCenter + halfWidth);
  39853. startYPos = inverted ?
  39854. (reversed ?
  39855. this.yAxis.height - labelCenter + halfWidth :
  39856. -labelCenter - halfWidth) : alignOptions.y;
  39857. dataLabel.startXPos = startXPos;
  39858. dataLabel.startYPos = startYPos;
  39859. // We need to handle visibility in case of sorting point outside plot area
  39860. if (!isInside) {
  39861. dataLabel
  39862. .attr({ opacity: 1 })
  39863. .animate({ opacity: 0 }, void 0, dataLabel.hide);
  39864. }
  39865. else if (dataLabel.visibility === 'hidden') {
  39866. dataLabel.show();
  39867. dataLabel
  39868. .attr({ opacity: 0 })
  39869. .animate({ opacity: 1 });
  39870. }
  39871. // Save start position on first render, but do not change position
  39872. if (!chart.hasRendered) {
  39873. return;
  39874. }
  39875. // Set start position
  39876. if (isNew) {
  39877. dataLabel.attr({ x: dataLabel.startXPos, y: dataLabel.startYPos });
  39878. }
  39879. dataLabel.placed = true;
  39880. };
  39881. /**
  39882. * If data labels fall partly outside the plot area, align them back in, in a
  39883. * way that doesn't hide the point.
  39884. *
  39885. * @private
  39886. * @function Highcharts.Series#justifyDataLabel
  39887. * @param {Highcharts.SVGElement} dataLabel
  39888. * @param {Highcharts.DataLabelsOptions} options
  39889. * @param {Highcharts.SVGAttributes} alignAttr
  39890. * @param {Highcharts.BBoxObject} bBox
  39891. * @param {Highcharts.BBoxObject} [alignTo]
  39892. * @param {boolean} [isNew]
  39893. * @return {boolean|undefined}
  39894. */
  39895. Series.prototype.justifyDataLabel = function (dataLabel, options, alignAttr, bBox, alignTo, isNew) {
  39896. var chart = this.chart, align = options.align, verticalAlign = options.verticalAlign, off, justified, padding = dataLabel.box ? 0 : (dataLabel.padding || 0);
  39897. var _a = options.x, x = _a === void 0 ? 0 : _a, _b = options.y, y = _b === void 0 ? 0 : _b;
  39898. // Off left
  39899. off = alignAttr.x + padding;
  39900. if (off < 0) {
  39901. if (align === 'right' && x >= 0) {
  39902. options.align = 'left';
  39903. options.inside = true;
  39904. }
  39905. else {
  39906. x -= off;
  39907. }
  39908. justified = true;
  39909. }
  39910. // Off right
  39911. off = alignAttr.x + bBox.width - padding;
  39912. if (off > chart.plotWidth) {
  39913. if (align === 'left' && x <= 0) {
  39914. options.align = 'right';
  39915. options.inside = true;
  39916. }
  39917. else {
  39918. x += chart.plotWidth - off;
  39919. }
  39920. justified = true;
  39921. }
  39922. // Off top
  39923. off = alignAttr.y + padding;
  39924. if (off < 0) {
  39925. if (verticalAlign === 'bottom' && y >= 0) {
  39926. options.verticalAlign = 'top';
  39927. options.inside = true;
  39928. }
  39929. else {
  39930. y -= off;
  39931. }
  39932. justified = true;
  39933. }
  39934. // Off bottom
  39935. off = alignAttr.y + bBox.height - padding;
  39936. if (off > chart.plotHeight) {
  39937. if (verticalAlign === 'top' && y <= 0) {
  39938. options.verticalAlign = 'bottom';
  39939. options.inside = true;
  39940. }
  39941. else {
  39942. y += chart.plotHeight - off;
  39943. }
  39944. justified = true;
  39945. }
  39946. if (justified) {
  39947. options.x = x;
  39948. options.y = y;
  39949. dataLabel.placed = !isNew;
  39950. dataLabel.align(options, void 0, alignTo);
  39951. }
  39952. return justified;
  39953. };
  39954. if (seriesTypes.pie) {
  39955. seriesTypes.pie.prototype.dataLabelPositioners = {
  39956. // Based on the value computed in Highcharts' distribute algorithm.
  39957. radialDistributionY: function (point) {
  39958. return point.top + point.distributeBox.pos;
  39959. },
  39960. // get the x - use the natural x position for labels near the
  39961. // top and bottom, to prevent the top and botton slice
  39962. // connectors from touching each other on either side
  39963. // Based on the value computed in Highcharts' distribute algorithm.
  39964. radialDistributionX: function (series, point, y, naturalY) {
  39965. return series.getX(y < point.top + 2 || y > point.bottom - 2 ?
  39966. naturalY :
  39967. y, point.half, point);
  39968. },
  39969. // dataLabels.distance determines the x position of the label
  39970. justify: function (point, radius, seriesCenter) {
  39971. return seriesCenter[0] + (point.half ? -1 : 1) *
  39972. (radius + point.labelDistance);
  39973. },
  39974. // Left edges of the left-half labels touch the left edge of the plot
  39975. // area. Right edges of the right-half labels touch the right edge of
  39976. // the plot area.
  39977. alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
  39978. var dataLabelWidth = dataLabel.getBBox().width;
  39979. return half ? dataLabelWidth + plotLeft :
  39980. plotWidth - dataLabelWidth - plotLeft;
  39981. },
  39982. // Connectors of each side end in the same x position. Labels are
  39983. // aligned to them. Left edge of the widest left-half label touches the
  39984. // left edge of the plot area. Right edge of the widest right-half label
  39985. // touches the right edge of the plot area.
  39986. alignToConnectors: function (points, half, plotWidth, plotLeft) {
  39987. var maxDataLabelWidth = 0, dataLabelWidth;
  39988. // find widest data label
  39989. points.forEach(function (point) {
  39990. dataLabelWidth = point.dataLabel.getBBox().width;
  39991. if (dataLabelWidth > maxDataLabelWidth) {
  39992. maxDataLabelWidth = dataLabelWidth;
  39993. }
  39994. });
  39995. return half ? maxDataLabelWidth + plotLeft :
  39996. plotWidth - maxDataLabelWidth - plotLeft;
  39997. }
  39998. };
  39999. /**
  40000. * Override the base drawDataLabels method by pie specific functionality
  40001. *
  40002. * @private
  40003. * @function Highcharts.seriesTypes.pie#drawDataLabels
  40004. * @return {void}
  40005. */
  40006. seriesTypes.pie.prototype.drawDataLabels = function () {
  40007. var series = this, data = series.data, point, chart = series.chart, options = series.options.dataLabels || {}, connectorPadding = options.connectorPadding, connectorWidth, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotLeft = chart.plotLeft, maxWidth = Math.round(chart.chartWidth / 3), connector, seriesCenter = series.center, radius = seriesCenter[2] / 2, centerY = seriesCenter[1], dataLabel, dataLabelWidth,
  40008. // labelPos,
  40009. labelPosition, labelHeight,
  40010. // divide the points into right and left halves for anti collision
  40011. halves = [
  40012. [],
  40013. [] // left
  40014. ], x, y, visibility, j, overflow = [0, 0, 0, 0], // top, right, bottom, left
  40015. dataLabelPositioners = series.dataLabelPositioners, pointDataLabelsOptions;
  40016. // get out if not enabled
  40017. if (!series.visible ||
  40018. (!options.enabled &&
  40019. !series._hasPointLabels)) {
  40020. return;
  40021. }
  40022. // Reset all labels that have been shortened
  40023. data.forEach(function (point) {
  40024. if (point.dataLabel && point.visible && point.dataLabel.shortened) {
  40025. point.dataLabel
  40026. .attr({
  40027. width: 'auto'
  40028. }).css({
  40029. width: 'auto',
  40030. textOverflow: 'clip'
  40031. });
  40032. point.dataLabel.shortened = false;
  40033. }
  40034. });
  40035. // run parent method
  40036. Series.prototype.drawDataLabels.apply(series);
  40037. data.forEach(function (point) {
  40038. if (point.dataLabel) {
  40039. if (point.visible) { // #407, #2510
  40040. // Arrange points for detection collision
  40041. halves[point.half].push(point);
  40042. // Reset positions (#4905)
  40043. point.dataLabel._pos = null;
  40044. // Avoid long labels squeezing the pie size too far down
  40045. if (!defined(options.style.width) &&
  40046. !defined(point.options.dataLabels &&
  40047. point.options.dataLabels.style &&
  40048. point.options.dataLabels.style.width)) {
  40049. if (point.dataLabel.getBBox().width > maxWidth) {
  40050. point.dataLabel.css({
  40051. // Use a fraction of the maxWidth to avoid
  40052. // wrapping close to the end of the string.
  40053. width: Math.round(maxWidth * 0.7) + 'px'
  40054. });
  40055. point.dataLabel.shortened = true;
  40056. }
  40057. }
  40058. }
  40059. else {
  40060. point.dataLabel = point.dataLabel.destroy();
  40061. // Workaround to make pies destroy multiple datalabels
  40062. // correctly. This logic needs rewriting to support multiple
  40063. // datalabels fully.
  40064. if (point.dataLabels && point.dataLabels.length === 1) {
  40065. delete point.dataLabels;
  40066. }
  40067. }
  40068. }
  40069. });
  40070. /* Loop over the points in each half, starting from the top and bottom
  40071. * of the pie to detect overlapping labels.
  40072. */
  40073. halves.forEach(function (points, i) {
  40074. var top, bottom, length = points.length, positions = [], naturalY, sideOverflow, size, distributionLength;
  40075. if (!length) {
  40076. return;
  40077. }
  40078. // Sort by angle
  40079. series.sortByAngle(points, i - 0.5);
  40080. // Only do anti-collision when we have dataLabels outside the pie
  40081. // and have connectors. (#856)
  40082. if (series.maxLabelDistance > 0) {
  40083. top = Math.max(0, centerY - radius - series.maxLabelDistance);
  40084. bottom = Math.min(centerY + radius + series.maxLabelDistance, chart.plotHeight);
  40085. points.forEach(function (point) {
  40086. // check if specific points' label is outside the pie
  40087. if (point.labelDistance > 0 && point.dataLabel) {
  40088. // point.top depends on point.labelDistance value
  40089. // Used for calculation of y value in getX method
  40090. point.top = Math.max(0, centerY - radius - point.labelDistance);
  40091. point.bottom = Math.min(centerY + radius + point.labelDistance, chart.plotHeight);
  40092. size = point.dataLabel.getBBox().height || 21;
  40093. // point.positionsIndex is needed for getting index of
  40094. // parameter related to specific point inside positions
  40095. // array - not every point is in positions array.
  40096. point.distributeBox = {
  40097. target: point.labelPosition.natural.y -
  40098. point.top + size / 2,
  40099. size: size,
  40100. rank: point.y
  40101. };
  40102. positions.push(point.distributeBox);
  40103. }
  40104. });
  40105. distributionLength = bottom + size - top;
  40106. H.distribute(positions, distributionLength, distributionLength / 5);
  40107. }
  40108. // Now the used slots are sorted, fill them up sequentially
  40109. for (j = 0; j < length; j++) {
  40110. point = points[j];
  40111. // labelPos = point.labelPos;
  40112. labelPosition = point.labelPosition;
  40113. dataLabel = point.dataLabel;
  40114. visibility = point.visible === false ? 'hidden' : 'inherit';
  40115. naturalY = labelPosition.natural.y;
  40116. y = naturalY;
  40117. if (positions && defined(point.distributeBox)) {
  40118. if (typeof point.distributeBox.pos === 'undefined') {
  40119. visibility = 'hidden';
  40120. }
  40121. else {
  40122. labelHeight = point.distributeBox.size;
  40123. // Find label's y position
  40124. y = dataLabelPositioners
  40125. .radialDistributionY(point);
  40126. }
  40127. }
  40128. // It is needed to delete point.positionIndex for
  40129. // dynamically added points etc.
  40130. delete point.positionIndex; // @todo unused
  40131. // Find label's x position
  40132. // justify is undocumented in the API - preserve support for it
  40133. if (options.justify) {
  40134. x = dataLabelPositioners.justify(point, radius, seriesCenter);
  40135. }
  40136. else {
  40137. switch (options.alignTo) {
  40138. case 'connectors':
  40139. x = dataLabelPositioners.alignToConnectors(points, i, plotWidth, plotLeft);
  40140. break;
  40141. case 'plotEdges':
  40142. x = dataLabelPositioners.alignToPlotEdges(dataLabel, i, plotWidth, plotLeft);
  40143. break;
  40144. default:
  40145. x = dataLabelPositioners.radialDistributionX(series, point, y, naturalY);
  40146. }
  40147. }
  40148. // Record the placement and visibility
  40149. dataLabel._attr = {
  40150. visibility: visibility,
  40151. align: labelPosition.alignment
  40152. };
  40153. pointDataLabelsOptions = point.options.dataLabels || {};
  40154. dataLabel._pos = {
  40155. x: (x +
  40156. pick(pointDataLabelsOptions.x, options.x) + // (#12985)
  40157. ({
  40158. left: connectorPadding,
  40159. right: -connectorPadding
  40160. }[labelPosition.alignment] || 0)),
  40161. // 10 is for the baseline (label vs text)
  40162. y: (y +
  40163. pick(pointDataLabelsOptions.y, options.y) - // (#12985)
  40164. 10)
  40165. };
  40166. // labelPos.x = x;
  40167. // labelPos.y = y;
  40168. labelPosition.final.x = x;
  40169. labelPosition.final.y = y;
  40170. // Detect overflowing data labels
  40171. if (pick(options.crop, true)) {
  40172. dataLabelWidth = dataLabel.getBBox().width;
  40173. sideOverflow = null;
  40174. // Overflow left
  40175. if (x - dataLabelWidth < connectorPadding &&
  40176. i === 1 // left half
  40177. ) {
  40178. sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
  40179. overflow[3] = Math.max(sideOverflow, overflow[3]);
  40180. // Overflow right
  40181. }
  40182. else if (x + dataLabelWidth > plotWidth - connectorPadding &&
  40183. i === 0 // right half
  40184. ) {
  40185. sideOverflow = Math.round(x + dataLabelWidth - plotWidth + connectorPadding);
  40186. overflow[1] = Math.max(sideOverflow, overflow[1]);
  40187. }
  40188. // Overflow top
  40189. if (y - labelHeight / 2 < 0) {
  40190. overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
  40191. // Overflow left
  40192. }
  40193. else if (y + labelHeight / 2 > plotHeight) {
  40194. overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
  40195. }
  40196. dataLabel.sideOverflow = sideOverflow;
  40197. }
  40198. } // for each point
  40199. }); // for each half
  40200. // Do not apply the final placement and draw the connectors until we
  40201. // have verified that labels are not spilling over.
  40202. if (arrayMax(overflow) === 0 ||
  40203. this.verifyDataLabelOverflow(overflow)) {
  40204. // Place the labels in the final position
  40205. this.placeDataLabels();
  40206. this.points.forEach(function (point) {
  40207. // #8864: every connector can have individual options
  40208. pointDataLabelsOptions =
  40209. merge(options, point.options.dataLabels);
  40210. connectorWidth =
  40211. pick(pointDataLabelsOptions.connectorWidth, 1);
  40212. // Draw the connector
  40213. if (connectorWidth) {
  40214. var isNew;
  40215. connector = point.connector;
  40216. dataLabel = point.dataLabel;
  40217. if (dataLabel &&
  40218. dataLabel._pos &&
  40219. point.visible &&
  40220. point.labelDistance > 0) {
  40221. visibility = dataLabel._attr.visibility;
  40222. isNew = !connector;
  40223. if (isNew) {
  40224. point.connector = connector = chart.renderer
  40225. .path()
  40226. .addClass('highcharts-data-label-connector ' +
  40227. ' highcharts-color-' + point.colorIndex +
  40228. (point.className ?
  40229. ' ' + point.className :
  40230. ''))
  40231. .add(series.dataLabelsGroup);
  40232. if (!chart.styledMode) {
  40233. connector.attr({
  40234. 'stroke-width': connectorWidth,
  40235. 'stroke': (pointDataLabelsOptions.connectorColor ||
  40236. point.color ||
  40237. '#666666')
  40238. });
  40239. }
  40240. }
  40241. connector[isNew ? 'attr' : 'animate']({
  40242. d: point.getConnectorPath()
  40243. });
  40244. connector.attr('visibility', visibility);
  40245. }
  40246. else if (connector) {
  40247. point.connector = connector.destroy();
  40248. }
  40249. }
  40250. });
  40251. }
  40252. };
  40253. /**
  40254. * Extendable method for getting the path of the connector between the data
  40255. * label and the pie slice.
  40256. *
  40257. * @private
  40258. * @function Highcharts.seriesTypes.pie#connectorPath
  40259. *
  40260. * @param {*} labelPos
  40261. *
  40262. * @return {Highcharts.SVGPathArray}
  40263. */
  40264. // TODO: depracated - remove it
  40265. /*
  40266. seriesTypes.pie.prototype.connectorPath = function (labelPos) {
  40267. var x = labelPos.x,
  40268. y = labelPos.y;
  40269. return pick(this.options.dataLabels.softConnector, true) ? [
  40270. 'M',
  40271. // end of the string at the label
  40272. x + (labelPos[6] === 'left' ? 5 : -5), y,
  40273. 'C',
  40274. x, y, // first break, next to the label
  40275. 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5],
  40276. labelPos[2], labelPos[3], // second break
  40277. 'L',
  40278. labelPos[4], labelPos[5] // base
  40279. ] : [
  40280. 'M',
  40281. // end of the string at the label
  40282. x + (labelPos[6] === 'left' ? 5 : -5), y,
  40283. 'L',
  40284. labelPos[2], labelPos[3], // second break
  40285. 'L',
  40286. labelPos[4], labelPos[5] // base
  40287. ];
  40288. };
  40289. */
  40290. /**
  40291. * Perform the final placement of the data labels after we have verified
  40292. * that they fall within the plot area.
  40293. *
  40294. * @private
  40295. * @function Highcharts.seriesTypes.pie#placeDataLabels
  40296. * @return {void}
  40297. */
  40298. seriesTypes.pie.prototype.placeDataLabels = function () {
  40299. this.points.forEach(function (point) {
  40300. var dataLabel = point.dataLabel, _pos;
  40301. if (dataLabel && point.visible) {
  40302. _pos = dataLabel._pos;
  40303. if (_pos) {
  40304. // Shorten data labels with ellipsis if they still overflow
  40305. // after the pie has reached minSize (#223).
  40306. if (dataLabel.sideOverflow) {
  40307. dataLabel._attr.width =
  40308. Math.max(dataLabel.getBBox().width -
  40309. dataLabel.sideOverflow, 0);
  40310. dataLabel.css({
  40311. width: dataLabel._attr.width + 'px',
  40312. textOverflow: ((this.options.dataLabels.style || {})
  40313. .textOverflow ||
  40314. 'ellipsis')
  40315. });
  40316. dataLabel.shortened = true;
  40317. }
  40318. dataLabel.attr(dataLabel._attr);
  40319. dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
  40320. dataLabel.moved = true;
  40321. }
  40322. else if (dataLabel) {
  40323. dataLabel.attr({ y: -9999 });
  40324. }
  40325. }
  40326. // Clear for update
  40327. delete point.distributeBox;
  40328. }, this);
  40329. };
  40330. seriesTypes.pie.prototype.alignDataLabel = noop;
  40331. /**
  40332. * Verify whether the data labels are allowed to draw, or we should run more
  40333. * translation and data label positioning to keep them inside the plot area.
  40334. * Returns true when data labels are ready to draw.
  40335. *
  40336. * @private
  40337. * @function Highcharts.seriesTypes.pie#verifyDataLabelOverflow
  40338. * @param {Array<number>} overflow
  40339. * @return {boolean}
  40340. */
  40341. seriesTypes.pie.prototype.verifyDataLabelOverflow = function (overflow) {
  40342. var center = this.center, options = this.options, centerOption = options.center, minSize = options.minSize || 80, newSize = minSize,
  40343. // If a size is set, return true and don't try to shrink the pie
  40344. // to fit the labels.
  40345. ret = options.size !== null;
  40346. if (!ret) {
  40347. // Handle horizontal size and center
  40348. if (centerOption[0] !== null) { // Fixed center
  40349. newSize = Math.max(center[2] -
  40350. Math.max(overflow[1], overflow[3]), minSize);
  40351. }
  40352. else { // Auto center
  40353. newSize = Math.max(
  40354. // horizontal overflow
  40355. center[2] - overflow[1] - overflow[3], minSize);
  40356. // horizontal center
  40357. center[0] += (overflow[3] - overflow[1]) / 2;
  40358. }
  40359. // Handle vertical size and center
  40360. if (centerOption[1] !== null) { // Fixed center
  40361. newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
  40362. }
  40363. else { // Auto center
  40364. newSize = clamp(newSize, minSize,
  40365. // vertical overflow
  40366. center[2] - overflow[0] - overflow[2]);
  40367. // vertical center
  40368. center[1] += (overflow[0] - overflow[2]) / 2;
  40369. }
  40370. // If the size must be decreased, we need to run translate and
  40371. // drawDataLabels again
  40372. if (newSize < center[2]) {
  40373. center[2] = newSize;
  40374. center[3] = Math.min(// #3632
  40375. relativeLength(options.innerSize || 0, newSize), newSize);
  40376. this.translate(center);
  40377. if (this.drawDataLabels) {
  40378. this.drawDataLabels();
  40379. }
  40380. // Else, return true to indicate that the pie and its labels is
  40381. // within the plot area
  40382. }
  40383. else {
  40384. ret = true;
  40385. }
  40386. }
  40387. return ret;
  40388. };
  40389. }
  40390. if (seriesTypes.column) {
  40391. /**
  40392. * Override the basic data label alignment by adjusting for the position of
  40393. * the column.
  40394. *
  40395. * @private
  40396. * @function Highcharts.seriesTypes.column#alignDataLabel
  40397. * @param {Highcharts.Point} point
  40398. * @param {Highcharts.SVGElement} dataLabel
  40399. * @param {Highcharts.DataLabelsOptions} options
  40400. * @param {Highcharts.BBoxObject} alignTo
  40401. * @param {boolean} [isNew]
  40402. * @return {void}
  40403. */
  40404. seriesTypes.column.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
  40405. var inverted = this.chart.inverted, series = point.series,
  40406. // data label box for alignment
  40407. dlBox = point.dlBox || point.shapeArgs, below = pick(point.below, // range series
  40408. point.plotY >
  40409. pick(this.translatedThreshold, series.yAxis.len)),
  40410. // draw it inside the box?
  40411. inside = pick(options.inside, !!this.options.stacking), overshoot;
  40412. // Align to the column itself, or the top of it
  40413. if (dlBox) { // Area range uses this method but not alignTo
  40414. alignTo = merge(dlBox);
  40415. if (alignTo.y < 0) {
  40416. alignTo.height += alignTo.y;
  40417. alignTo.y = 0;
  40418. }
  40419. // If parts of the box overshoots outside the plot area, modify the
  40420. // box to center the label inside
  40421. overshoot = alignTo.y + alignTo.height - series.yAxis.len;
  40422. if (overshoot > 0 && overshoot < alignTo.height) {
  40423. alignTo.height -= overshoot;
  40424. }
  40425. if (inverted) {
  40426. alignTo = {
  40427. x: series.yAxis.len - alignTo.y - alignTo.height,
  40428. y: series.xAxis.len - alignTo.x - alignTo.width,
  40429. width: alignTo.height,
  40430. height: alignTo.width
  40431. };
  40432. }
  40433. // Compute the alignment box
  40434. if (!inside) {
  40435. if (inverted) {
  40436. alignTo.x += below ? 0 : alignTo.width;
  40437. alignTo.width = 0;
  40438. }
  40439. else {
  40440. alignTo.y += below ? alignTo.height : 0;
  40441. alignTo.height = 0;
  40442. }
  40443. }
  40444. }
  40445. // When alignment is undefined (typically columns and bars), display the
  40446. // individual point below or above the point depending on the threshold
  40447. options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
  40448. options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
  40449. // Call the parent method
  40450. Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
  40451. // If label was justified and we have contrast, set it:
  40452. if (options.inside && point.contrastColor) {
  40453. dataLabel.css({
  40454. color: point.contrastColor
  40455. });
  40456. }
  40457. };
  40458. }
  40459. });
  40460. _registerModule(_modules, 'modules/overlapping-datalabels.src.js', [_modules['parts/Chart.js'], _modules['parts/Utilities.js']], function (Chart, U) {
  40461. /* *
  40462. *
  40463. * Highcharts module to hide overlapping data labels.
  40464. * This module is included in Highcharts.
  40465. *
  40466. * (c) 2009-2020 Torstein Honsi
  40467. *
  40468. * License: www.highcharts.com/license
  40469. *
  40470. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40471. *
  40472. * */
  40473. var addEvent = U.addEvent, fireEvent = U.fireEvent, isArray = U.isArray, isNumber = U.isNumber, objectEach = U.objectEach, pick = U.pick;
  40474. /* eslint-disable no-invalid-this */
  40475. // Collect potensial overlapping data labels. Stack labels probably don't need
  40476. // to be considered because they are usually accompanied by data labels that lie
  40477. // inside the columns.
  40478. addEvent(Chart, 'render', function collectAndHide() {
  40479. var labels = [];
  40480. // Consider external label collectors
  40481. (this.labelCollectors || []).forEach(function (collector) {
  40482. labels = labels.concat(collector());
  40483. });
  40484. (this.yAxis || []).forEach(function (yAxis) {
  40485. if (yAxis.stacking &&
  40486. yAxis.options.stackLabels &&
  40487. !yAxis.options.stackLabels.allowOverlap) {
  40488. objectEach(yAxis.stacking.stacks, function (stack) {
  40489. objectEach(stack, function (stackItem) {
  40490. labels.push(stackItem.label);
  40491. });
  40492. });
  40493. }
  40494. });
  40495. (this.series || []).forEach(function (series) {
  40496. var dlOptions = series.options.dataLabels;
  40497. if (series.visible &&
  40498. !(dlOptions.enabled === false && !series._hasPointLabels)) { // #3866
  40499. (series.nodes || series.points).forEach(function (point) {
  40500. if (point.visible) {
  40501. var dataLabels = (isArray(point.dataLabels) ?
  40502. point.dataLabels :
  40503. (point.dataLabel ? [point.dataLabel] : []));
  40504. dataLabels.forEach(function (label) {
  40505. var options = label.options;
  40506. label.labelrank = pick(options.labelrank, point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
  40507. if (!options.allowOverlap) {
  40508. labels.push(label);
  40509. }
  40510. });
  40511. }
  40512. });
  40513. }
  40514. });
  40515. this.hideOverlappingLabels(labels);
  40516. });
  40517. /**
  40518. * Hide overlapping labels. Labels are moved and faded in and out on zoom to
  40519. * provide a smooth visual imression.
  40520. *
  40521. * @private
  40522. * @function Highcharts.Chart#hideOverlappingLabels
  40523. * @param {Array<Highcharts.SVGElement>} labels
  40524. * Rendered data labels
  40525. * @requires modules/overlapping-datalabels
  40526. */
  40527. Chart.prototype.hideOverlappingLabels = function (labels) {
  40528. var chart = this, len = labels.length, ren = chart.renderer, label, i, j, label1, label2, box1, box2, isLabelAffected = false, isIntersectRect = function (box1, box2) {
  40529. return !(box2.x > box1.x + box1.width ||
  40530. box2.x + box2.width < box1.x ||
  40531. box2.y > box1.y + box1.height ||
  40532. box2.y + box2.height < box1.y);
  40533. },
  40534. // Get the box with its position inside the chart, as opposed to getBBox
  40535. // that only reports the position relative to the parent.
  40536. getAbsoluteBox = function (label) {
  40537. var pos, parent, bBox,
  40538. // Substract the padding if no background or border (#4333)
  40539. padding = label.box ? 0 : (label.padding || 0), lineHeightCorrection = 0, xOffset = 0, boxWidth, alignValue;
  40540. if (label &&
  40541. (!label.alignAttr || label.placed)) {
  40542. pos = label.alignAttr || {
  40543. x: label.attr('x'),
  40544. y: label.attr('y')
  40545. };
  40546. parent = label.parentGroup;
  40547. // Get width and height if pure text nodes (stack labels)
  40548. if (!label.width) {
  40549. bBox = label.getBBox();
  40550. label.width = bBox.width;
  40551. label.height = bBox.height;
  40552. // Labels positions are computed from top left corner, so
  40553. // we need to substract the text height from text nodes too.
  40554. lineHeightCorrection = ren
  40555. .fontMetrics(null, label.element).h;
  40556. }
  40557. boxWidth = label.width - 2 * padding;
  40558. alignValue = {
  40559. left: '0',
  40560. center: '0.5',
  40561. right: '1'
  40562. }[label.alignValue];
  40563. if (alignValue) {
  40564. xOffset = +alignValue * boxWidth;
  40565. }
  40566. else if (isNumber(label.x) && Math.round(label.x) !== label.translateX) {
  40567. xOffset = label.x - label.translateX;
  40568. }
  40569. return {
  40570. x: pos.x + (parent.translateX || 0) + padding - xOffset,
  40571. y: pos.y + (parent.translateY || 0) + padding -
  40572. lineHeightCorrection,
  40573. width: label.width - 2 * padding,
  40574. height: label.height - 2 * padding
  40575. };
  40576. }
  40577. };
  40578. for (i = 0; i < len; i++) {
  40579. label = labels[i];
  40580. if (label) {
  40581. // Mark with initial opacity
  40582. label.oldOpacity = label.opacity;
  40583. label.newOpacity = 1;
  40584. label.absoluteBox = getAbsoluteBox(label);
  40585. }
  40586. }
  40587. // Prevent a situation in a gradually rising slope, that each label will
  40588. // hide the previous one because the previous one always has lower rank.
  40589. labels.sort(function (a, b) {
  40590. return (b.labelrank || 0) - (a.labelrank || 0);
  40591. });
  40592. // Detect overlapping labels
  40593. for (i = 0; i < len; i++) {
  40594. label1 = labels[i];
  40595. box1 = label1 && label1.absoluteBox;
  40596. for (j = i + 1; j < len; ++j) {
  40597. label2 = labels[j];
  40598. box2 = label2 && label2.absoluteBox;
  40599. if (box1 &&
  40600. box2 &&
  40601. label1 !== label2 && // #6465, polar chart with connectEnds
  40602. label1.newOpacity !== 0 &&
  40603. label2.newOpacity !== 0) {
  40604. if (isIntersectRect(box1, box2)) {
  40605. (label1.labelrank < label2.labelrank ? label1 : label2)
  40606. .newOpacity = 0;
  40607. }
  40608. }
  40609. }
  40610. }
  40611. // Hide or show
  40612. labels.forEach(function (label) {
  40613. var complete, newOpacity;
  40614. if (label) {
  40615. newOpacity = label.newOpacity;
  40616. if (label.oldOpacity !== newOpacity) {
  40617. // Make sure the label is completely hidden to avoid catching
  40618. // clicks (#4362)
  40619. if (label.alignAttr && label.placed) { // data labels
  40620. label[newOpacity ? 'removeClass' : 'addClass']('highcharts-data-label-hidden');
  40621. complete = function () {
  40622. if (!chart.styledMode) {
  40623. label.css({ pointerEvents: newOpacity ? 'auto' : 'none' });
  40624. }
  40625. label.visibility = newOpacity ? 'inherit' : 'hidden';
  40626. label.placed = !!newOpacity;
  40627. };
  40628. isLabelAffected = true;
  40629. // Animate or set the opacity
  40630. label.alignAttr.opacity = newOpacity;
  40631. label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete);
  40632. fireEvent(chart, 'afterHideOverlappingLabel');
  40633. }
  40634. else { // other labels, tick labels
  40635. label.attr({
  40636. opacity: newOpacity
  40637. });
  40638. }
  40639. }
  40640. label.isOld = true;
  40641. }
  40642. });
  40643. if (isLabelAffected) {
  40644. fireEvent(chart, 'afterHideAllOverlappingLabels');
  40645. }
  40646. };
  40647. });
  40648. _registerModule(_modules, 'parts/Interaction.js', [_modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Legend.js'], _modules['parts/Options.js'], _modules['parts/Point.js'], _modules['parts/Utilities.js']], function (Chart, H, Legend, O, Point, U) {
  40649. /* *
  40650. *
  40651. * (c) 2010-2020 Torstein Honsi
  40652. *
  40653. * License: www.highcharts.com/license
  40654. *
  40655. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40656. *
  40657. * */
  40658. var defaultOptions = O.defaultOptions;
  40659. var addEvent = U.addEvent, createElement = U.createElement, css = U.css, defined = U.defined, extend = U.extend, fireEvent = U.fireEvent, isArray = U.isArray, isFunction = U.isFunction, isNumber = U.isNumber, isObject = U.isObject, merge = U.merge, objectEach = U.objectEach, pick = U.pick;
  40660. /**
  40661. * @interface Highcharts.PointEventsOptionsObject
  40662. */ /**
  40663. * Fires when the point is selected either programmatically or following a click
  40664. * on the point. One parameter, `event`, is passed to the function. Returning
  40665. * `false` cancels the operation.
  40666. * @name Highcharts.PointEventsOptionsObject#select
  40667. * @type {Highcharts.PointSelectCallbackFunction|undefined}
  40668. */ /**
  40669. * Fires when the point is unselected either programmatically or following a
  40670. * click on the point. One parameter, `event`, is passed to the function.
  40671. * Returning `false` cancels the operation.
  40672. * @name Highcharts.PointEventsOptionsObject#unselect
  40673. * @type {Highcharts.PointUnselectCallbackFunction|undefined}
  40674. */
  40675. /**
  40676. * Information about the select/unselect event.
  40677. *
  40678. * @interface Highcharts.PointInteractionEventObject
  40679. * @extends global.Event
  40680. */ /**
  40681. * @name Highcharts.PointInteractionEventObject#accumulate
  40682. * @type {boolean}
  40683. */
  40684. /**
  40685. * Gets fired when the point is selected either programmatically or following a
  40686. * click on the point.
  40687. *
  40688. * @callback Highcharts.PointSelectCallbackFunction
  40689. *
  40690. * @param {Highcharts.Point} this
  40691. * Point where the event occured.
  40692. *
  40693. * @param {Highcharts.PointInteractionEventObject} event
  40694. * Event that occured.
  40695. */
  40696. /**
  40697. * Fires when the point is unselected either programmatically or following a
  40698. * click on the point.
  40699. *
  40700. * @callback Highcharts.PointUnselectCallbackFunction
  40701. *
  40702. * @param {Highcharts.Point} this
  40703. * Point where the event occured.
  40704. *
  40705. * @param {Highcharts.PointInteractionEventObject} event
  40706. * Event that occured.
  40707. */
  40708. var hasTouch = H.hasTouch, Series = H.Series, seriesTypes = H.seriesTypes, svg = H.svg, TrackerMixin;
  40709. /* eslint-disable valid-jsdoc */
  40710. /**
  40711. * TrackerMixin for points and graphs.
  40712. *
  40713. * @private
  40714. * @mixin Highcharts.TrackerMixin
  40715. */
  40716. TrackerMixin = H.TrackerMixin = {
  40717. /**
  40718. * Draw the tracker for a point.
  40719. *
  40720. * @private
  40721. * @function Highcharts.TrackerMixin.drawTrackerPoint
  40722. * @param {Highcharts.Series} this
  40723. * @fires Highcharts.Series#event:afterDrawTracker
  40724. */
  40725. drawTrackerPoint: function () {
  40726. var series = this, chart = series.chart, pointer = chart.pointer, onMouseOver = function (e) {
  40727. var point = pointer.getPointFromEvent(e);
  40728. // undefined on graph in scatterchart
  40729. if (typeof point !== 'undefined') {
  40730. pointer.isDirectTouch = true;
  40731. point.onMouseOver(e);
  40732. }
  40733. }, dataLabels;
  40734. // Add reference to the point
  40735. series.points.forEach(function (point) {
  40736. dataLabels = (isArray(point.dataLabels) ?
  40737. point.dataLabels :
  40738. (point.dataLabel ? [point.dataLabel] : []));
  40739. if (point.graphic) {
  40740. point.graphic.element.point = point;
  40741. }
  40742. dataLabels.forEach(function (dataLabel) {
  40743. if (dataLabel.div) {
  40744. dataLabel.div.point = point;
  40745. }
  40746. else {
  40747. dataLabel.element.point = point;
  40748. }
  40749. });
  40750. });
  40751. // Add the event listeners, we need to do this only once
  40752. if (!series._hasTracking) {
  40753. series.trackerGroups.forEach(function (key) {
  40754. if (series[key]) {
  40755. // we don't always have dataLabelsGroup
  40756. series[key]
  40757. .addClass('highcharts-tracker')
  40758. .on('mouseover', onMouseOver)
  40759. .on('mouseout', function (e) {
  40760. pointer.onTrackerMouseOut(e);
  40761. });
  40762. if (hasTouch) {
  40763. series[key].on('touchstart', onMouseOver);
  40764. }
  40765. if (!chart.styledMode && series.options.cursor) {
  40766. series[key]
  40767. .css(css)
  40768. .css({ cursor: series.options.cursor });
  40769. }
  40770. }
  40771. });
  40772. series._hasTracking = true;
  40773. }
  40774. fireEvent(this, 'afterDrawTracker');
  40775. },
  40776. /**
  40777. * Draw the tracker object that sits above all data labels and markers to
  40778. * track mouse events on the graph or points. For the line type charts
  40779. * the tracker uses the same graphPath, but with a greater stroke width
  40780. * for better control.
  40781. *
  40782. * @private
  40783. * @function Highcharts.TrackerMixin.drawTrackerGraph
  40784. * @param {Highcharts.Series} this
  40785. * @fires Highcharts.Series#event:afterDrawTracker
  40786. */
  40787. drawTrackerGraph: function () {
  40788. var series = this, options = series.options, trackByArea = options.trackByArea, trackerPath = [].concat(trackByArea ?
  40789. series.areaPath :
  40790. series.graphPath),
  40791. // trackerPathLength = trackerPath.length,
  40792. chart = series.chart, pointer = chart.pointer, renderer = chart.renderer, snap = chart.options.tooltip.snap, tracker = series.tracker, i, onMouseOver = function (e) {
  40793. if (chart.hoverSeries !== series) {
  40794. series.onMouseOver();
  40795. }
  40796. },
  40797. /*
  40798. * Empirical lowest possible opacities for TRACKER_FILL for an
  40799. * element to stay invisible but clickable
  40800. * IE6: 0.002
  40801. * IE7: 0.002
  40802. * IE8: 0.002
  40803. * IE9: 0.00000000001 (unlimited)
  40804. * IE10: 0.0001 (exporting only)
  40805. * FF: 0.00000000001 (unlimited)
  40806. * Chrome: 0.000001
  40807. * Safari: 0.000001
  40808. * Opera: 0.00000000001 (unlimited)
  40809. */
  40810. TRACKER_FILL = 'rgba(192,192,192,' + (svg ? 0.0001 : 0.002) + ')';
  40811. // Draw the tracker
  40812. if (tracker) {
  40813. tracker.attr({ d: trackerPath });
  40814. }
  40815. else if (series.graph) { // create
  40816. series.tracker = renderer.path(trackerPath)
  40817. .attr({
  40818. visibility: series.visible ? 'visible' : 'hidden',
  40819. zIndex: 2
  40820. })
  40821. .addClass(trackByArea ?
  40822. 'highcharts-tracker-area' :
  40823. 'highcharts-tracker-line')
  40824. .add(series.group);
  40825. if (!chart.styledMode) {
  40826. series.tracker.attr({
  40827. 'stroke-linecap': 'round',
  40828. 'stroke-linejoin': 'round',
  40829. stroke: TRACKER_FILL,
  40830. fill: trackByArea ? TRACKER_FILL : 'none',
  40831. 'stroke-width': series.graph.strokeWidth() +
  40832. (trackByArea ? 0 : 2 * snap)
  40833. });
  40834. }
  40835. // The tracker is added to the series group, which is clipped, but
  40836. // is covered by the marker group. So the marker group also needs to
  40837. // capture events.
  40838. [series.tracker, series.markerGroup].forEach(function (tracker) {
  40839. tracker.addClass('highcharts-tracker')
  40840. .on('mouseover', onMouseOver)
  40841. .on('mouseout', function (e) {
  40842. pointer.onTrackerMouseOut(e);
  40843. });
  40844. if (options.cursor && !chart.styledMode) {
  40845. tracker.css({ cursor: options.cursor });
  40846. }
  40847. if (hasTouch) {
  40848. tracker.on('touchstart', onMouseOver);
  40849. }
  40850. });
  40851. }
  40852. fireEvent(this, 'afterDrawTracker');
  40853. }
  40854. };
  40855. /* End TrackerMixin */
  40856. // Add tracking event listener to the series group, so the point graphics
  40857. // themselves act as trackers
  40858. if (seriesTypes.column) {
  40859. /**
  40860. * @private
  40861. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.column#drawTracker
  40862. */
  40863. seriesTypes.column.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  40864. }
  40865. if (seriesTypes.pie) {
  40866. /**
  40867. * @private
  40868. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.pie#drawTracker
  40869. */
  40870. seriesTypes.pie.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  40871. }
  40872. if (seriesTypes.scatter) {
  40873. /**
  40874. * @private
  40875. * @borrows Highcharts.TrackerMixin.drawTrackerPoint as Highcharts.seriesTypes.scatter#drawTracker
  40876. */
  40877. seriesTypes.scatter.prototype.drawTracker = TrackerMixin.drawTrackerPoint;
  40878. }
  40879. // Extend Legend for item events.
  40880. extend(Legend.prototype, {
  40881. /**
  40882. * @private
  40883. * @function Highcharts.Legend#setItemEvents
  40884. * @param {Highcharts.BubbleLegend|Point|Highcharts.Series} item
  40885. * @param {Highcharts.SVGElement} legendItem
  40886. * @param {boolean} [useHTML=false]
  40887. * @fires Highcharts.Point#event:legendItemClick
  40888. * @fires Highcharts.Series#event:legendItemClick
  40889. */
  40890. setItemEvents: function (item, legendItem, useHTML) {
  40891. var legend = this, boxWrapper = legend.chart.renderer.boxWrapper, isPoint = item instanceof Point, activeClass = 'highcharts-legend-' +
  40892. (isPoint ? 'point' : 'series') + '-active', styledMode = legend.chart.styledMode,
  40893. // When `useHTML`, the symbol is rendered in other group, so
  40894. // we need to apply events listeners to both places
  40895. legendItems = useHTML ?
  40896. [legendItem, item.legendSymbol] :
  40897. [item.legendGroup];
  40898. // Set the events on the item group, or in case of useHTML, the item
  40899. // itself (#1249)
  40900. legendItems.forEach(function (element) {
  40901. if (element) {
  40902. element
  40903. .on('mouseover', function () {
  40904. if (item.visible) {
  40905. legend.allItems.forEach(function (inactiveItem) {
  40906. if (item !== inactiveItem) {
  40907. inactiveItem.setState('inactive', !isPoint);
  40908. }
  40909. });
  40910. }
  40911. item.setState('hover');
  40912. // A CSS class to dim or hide other than the hovered
  40913. // series.
  40914. // Works only if hovered series is visible (#10071).
  40915. if (item.visible) {
  40916. boxWrapper.addClass(activeClass);
  40917. }
  40918. if (!styledMode) {
  40919. legendItem.css(legend.options.itemHoverStyle);
  40920. }
  40921. })
  40922. .on('mouseout', function () {
  40923. if (!legend.chart.styledMode) {
  40924. legendItem.css(merge(item.visible ?
  40925. legend.itemStyle :
  40926. legend.itemHiddenStyle));
  40927. }
  40928. legend.allItems.forEach(function (inactiveItem) {
  40929. if (item !== inactiveItem) {
  40930. inactiveItem.setState('', !isPoint);
  40931. }
  40932. });
  40933. // A CSS class to dim or hide other than the hovered
  40934. // series.
  40935. boxWrapper.removeClass(activeClass);
  40936. item.setState();
  40937. })
  40938. .on('click', function (event) {
  40939. var strLegendItemClick = 'legendItemClick', fnLegendItemClick = function () {
  40940. if (item.setVisible) {
  40941. item.setVisible();
  40942. }
  40943. // Reset inactive state
  40944. legend.allItems.forEach(function (inactiveItem) {
  40945. if (item !== inactiveItem) {
  40946. inactiveItem.setState(item.visible ? 'inactive' : '', !isPoint);
  40947. }
  40948. });
  40949. };
  40950. // A CSS class to dim or hide other than the hovered
  40951. // series. Event handling in iOS causes the activeClass
  40952. // to be added prior to click in some cases (#7418).
  40953. boxWrapper.removeClass(activeClass);
  40954. // Pass over the click/touch event. #4.
  40955. event = {
  40956. browserEvent: event
  40957. };
  40958. // click the name or symbol
  40959. if (item.firePointEvent) { // point
  40960. item.firePointEvent(strLegendItemClick, event, fnLegendItemClick);
  40961. }
  40962. else {
  40963. fireEvent(item, strLegendItemClick, event, fnLegendItemClick);
  40964. }
  40965. });
  40966. }
  40967. });
  40968. },
  40969. /**
  40970. * @private
  40971. * @function Highcharts.Legend#createCheckboxForItem
  40972. * @param {Highcharts.BubbleLegend|Point|Highcharts.Series} item
  40973. * @fires Highcharts.Series#event:checkboxClick
  40974. */
  40975. createCheckboxForItem: function (item) {
  40976. var legend = this;
  40977. item.checkbox = createElement('input', {
  40978. type: 'checkbox',
  40979. className: 'highcharts-legend-checkbox',
  40980. checked: item.selected,
  40981. defaultChecked: item.selected // required by IE7
  40982. }, legend.options.itemCheckboxStyle, legend.chart.container);
  40983. addEvent(item.checkbox, 'click', function (event) {
  40984. var target = event.target;
  40985. fireEvent(item.series || item, 'checkboxClick', {
  40986. checked: target.checked,
  40987. item: item
  40988. }, function () {
  40989. item.select();
  40990. });
  40991. });
  40992. }
  40993. });
  40994. // Extend the Chart object with interaction
  40995. extend(Chart.prototype, /** @lends Chart.prototype */ {
  40996. /**
  40997. * Display the zoom button, so users can reset zoom to the default view
  40998. * settings.
  40999. *
  41000. * @function Highcharts.Chart#showResetZoom
  41001. *
  41002. * @fires Highcharts.Chart#event:afterShowResetZoom
  41003. * @fires Highcharts.Chart#event:beforeShowResetZoom
  41004. */
  41005. showResetZoom: function () {
  41006. var chart = this, lang = defaultOptions.lang, btnOptions = chart.options.chart.resetZoomButton, theme = btnOptions.theme, states = theme.states, alignTo = (btnOptions.relativeTo === 'chart' ||
  41007. btnOptions.relativeTo === 'spaceBox' ?
  41008. null :
  41009. 'plotBox');
  41010. /**
  41011. * @private
  41012. */
  41013. function zoomOut() {
  41014. chart.zoomOut();
  41015. }
  41016. fireEvent(this, 'beforeShowResetZoom', null, function () {
  41017. chart.resetZoomButton = chart.renderer
  41018. .button(lang.resetZoom, null, null, zoomOut, theme, states && states.hover)
  41019. .attr({
  41020. align: btnOptions.position.align,
  41021. title: lang.resetZoomTitle
  41022. })
  41023. .addClass('highcharts-reset-zoom')
  41024. .add()
  41025. .align(btnOptions.position, false, alignTo);
  41026. });
  41027. fireEvent(this, 'afterShowResetZoom');
  41028. },
  41029. /**
  41030. * Zoom the chart out after a user has zoomed in. See also
  41031. * [Axis.setExtremes](/class-reference/Highcharts.Axis#setExtremes).
  41032. *
  41033. * @function Highcharts.Chart#zoomOut
  41034. *
  41035. * @fires Highcharts.Chart#event:selection
  41036. */
  41037. zoomOut: function () {
  41038. fireEvent(this, 'selection', { resetSelection: true }, this.zoom);
  41039. },
  41040. /**
  41041. * Zoom into a given portion of the chart given by axis coordinates.
  41042. *
  41043. * @private
  41044. * @function Highcharts.Chart#zoom
  41045. * @param {Highcharts.SelectEventObject} event
  41046. */
  41047. zoom: function (event) {
  41048. var chart = this, hasZoomed, pointer = chart.pointer, displayButton = false, mouseDownPos = chart.inverted ? pointer.mouseDownX : pointer.mouseDownY, resetZoomButton;
  41049. // If zoom is called with no arguments, reset the axes
  41050. if (!event || event.resetSelection) {
  41051. chart.axes.forEach(function (axis) {
  41052. hasZoomed = axis.zoom();
  41053. });
  41054. pointer.initiated = false; // #6804
  41055. }
  41056. else { // else, zoom in on all axes
  41057. event.xAxis.concat(event.yAxis).forEach(function (axisData) {
  41058. var axis = axisData.axis, axisStartPos = chart.inverted ? axis.left : axis.top, axisEndPos = chart.inverted ?
  41059. axisStartPos + axis.width : axisStartPos + axis.height, isXAxis = axis.isXAxis, isWithinPane = false;
  41060. // Check if zoomed area is within the pane (#1289).
  41061. // In case of multiple panes only one pane should be zoomed.
  41062. if ((!isXAxis &&
  41063. mouseDownPos >= axisStartPos &&
  41064. mouseDownPos <= axisEndPos) ||
  41065. isXAxis ||
  41066. !defined(mouseDownPos)) {
  41067. isWithinPane = true;
  41068. }
  41069. // don't zoom more than minRange
  41070. if (pointer[isXAxis ? 'zoomX' : 'zoomY'] && isWithinPane) {
  41071. hasZoomed = axis.zoom(axisData.min, axisData.max);
  41072. if (axis.displayBtn) {
  41073. displayButton = true;
  41074. }
  41075. }
  41076. });
  41077. }
  41078. // Show or hide the Reset zoom button
  41079. resetZoomButton = chart.resetZoomButton;
  41080. if (displayButton && !resetZoomButton) {
  41081. chart.showResetZoom();
  41082. }
  41083. else if (!displayButton && isObject(resetZoomButton)) {
  41084. chart.resetZoomButton = resetZoomButton.destroy();
  41085. }
  41086. // Redraw
  41087. if (hasZoomed) {
  41088. chart.redraw(pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100));
  41089. }
  41090. },
  41091. /**
  41092. * Pan the chart by dragging the mouse across the pane. This function is
  41093. * called on mouse move, and the distance to pan is computed from chartX
  41094. * compared to the first chartX position in the dragging operation.
  41095. *
  41096. * @private
  41097. * @function Highcharts.Chart#pan
  41098. * @param {Highcharts.PointerEventObject} e
  41099. * @param {string} panning
  41100. */
  41101. pan: function (e, panning) {
  41102. var chart = this, hoverPoints = chart.hoverPoints, panningOptions, chartOptions = chart.options.chart, hasMapNavigation = chart.options.mapNavigation &&
  41103. chart.options.mapNavigation.enabled, doRedraw, type;
  41104. if (typeof panning === 'object') {
  41105. panningOptions = panning;
  41106. }
  41107. else {
  41108. panningOptions = {
  41109. enabled: panning,
  41110. type: 'x'
  41111. };
  41112. }
  41113. if (chartOptions && chartOptions.panning) {
  41114. chartOptions.panning = panningOptions;
  41115. }
  41116. type = panningOptions.type;
  41117. fireEvent(this, 'pan', { originalEvent: e }, function () {
  41118. // remove active points for shared tooltip
  41119. if (hoverPoints) {
  41120. hoverPoints.forEach(function (point) {
  41121. point.setState();
  41122. });
  41123. }
  41124. // panning axis mapping
  41125. var xy = [1]; // x
  41126. if (type === 'xy') {
  41127. xy = [1, 0];
  41128. }
  41129. else if (type === 'y') {
  41130. xy = [0];
  41131. }
  41132. xy.forEach(function (isX) {
  41133. var axis = chart[isX ? 'xAxis' : 'yAxis'][0], horiz = axis.horiz, mousePos = e[horiz ? 'chartX' : 'chartY'], mouseDown = horiz ? 'mouseDownX' : 'mouseDownY', startPos = chart[mouseDown], halfPointRange = (axis.pointRange || 0) / 2, pointRangeDirection = (axis.reversed && !chart.inverted) ||
  41134. (!axis.reversed && chart.inverted) ?
  41135. -1 :
  41136. 1, extremes = axis.getExtremes(), panMin = axis.toValue(startPos - mousePos, true) +
  41137. halfPointRange * pointRangeDirection, panMax = axis.toValue(startPos + axis.len - mousePos, true) -
  41138. halfPointRange * pointRangeDirection, flipped = panMax < panMin, newMin = flipped ? panMax : panMin, newMax = flipped ? panMin : panMax, hasVerticalPanning = axis.hasVerticalPanning(), paddedMin, paddedMax, spill, panningState = axis.panningState;
  41139. // General calculations of panning state.
  41140. // This is related to using vertical panning. (#11315).
  41141. axis.series.forEach(function (series) {
  41142. if (hasVerticalPanning &&
  41143. !isX && (!panningState || panningState.isDirty)) {
  41144. var processedData = series.getProcessedData(true), dataExtremes = series.getExtremes(processedData.yData, true);
  41145. if (!panningState) {
  41146. panningState = {
  41147. startMin: Number.MAX_VALUE,
  41148. startMax: -Number.MAX_VALUE
  41149. };
  41150. }
  41151. if (isNumber(dataExtremes.dataMin) &&
  41152. isNumber(dataExtremes.dataMax)) {
  41153. panningState.startMin = Math.min(dataExtremes.dataMin, panningState.startMin);
  41154. panningState.startMax = Math.max(dataExtremes.dataMax, panningState.startMax);
  41155. }
  41156. }
  41157. });
  41158. paddedMin = Math.min(H.pick(panningState === null || panningState === void 0 ? void 0 : panningState.startMin, extremes.dataMin), halfPointRange ?
  41159. extremes.min :
  41160. axis.toValue(axis.toPixels(extremes.min) -
  41161. axis.minPixelPadding));
  41162. paddedMax = Math.max(H.pick(panningState === null || panningState === void 0 ? void 0 : panningState.startMax, extremes.dataMax), halfPointRange ?
  41163. extremes.max :
  41164. axis.toValue(axis.toPixels(extremes.max) +
  41165. axis.minPixelPadding));
  41166. axis.panningState = panningState;
  41167. // It is not necessary to calculate extremes on ordinal axis,
  41168. // because they are already calculated, so we don't want to
  41169. // override them.
  41170. if (!axis.isOrdinal) {
  41171. // If the new range spills over, either to the min or max,
  41172. // adjust the new range.
  41173. spill = paddedMin - newMin;
  41174. if (spill > 0) {
  41175. newMax += spill;
  41176. newMin = paddedMin;
  41177. }
  41178. spill = newMax - paddedMax;
  41179. if (spill > 0) {
  41180. newMax = paddedMax;
  41181. newMin -= spill;
  41182. }
  41183. // Set new extremes if they are actually new
  41184. if (axis.series.length &&
  41185. newMin !== extremes.min &&
  41186. newMax !== extremes.max &&
  41187. newMin >= paddedMin &&
  41188. newMax <= paddedMax) {
  41189. axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' });
  41190. if (!chart.resetZoomButton &&
  41191. !hasMapNavigation &&
  41192. // Show reset zoom button only when both newMin and
  41193. // newMax values are between padded axis range.
  41194. newMin !== paddedMin &&
  41195. newMax !== paddedMax &&
  41196. type.match('y')) {
  41197. chart.showResetZoom();
  41198. axis.displayBtn = false;
  41199. }
  41200. doRedraw = true;
  41201. }
  41202. // set new reference for next run:
  41203. chart[mouseDown] = mousePos;
  41204. }
  41205. });
  41206. if (doRedraw) {
  41207. chart.redraw(false);
  41208. }
  41209. css(chart.container, { cursor: 'move' });
  41210. });
  41211. }
  41212. });
  41213. // Extend the Point object with interaction
  41214. extend(Point.prototype, /** @lends Highcharts.Point.prototype */ {
  41215. /**
  41216. * Toggle the selection status of a point.
  41217. *
  41218. * @see Highcharts.Chart#getSelectedPoints
  41219. *
  41220. * @sample highcharts/members/point-select/
  41221. * Select a point from a button
  41222. * @sample highcharts/chart/events-selection-points/
  41223. * Select a range of points through a drag selection
  41224. * @sample maps/series/data-id/
  41225. * Select a point in Highmaps
  41226. *
  41227. * @function Highcharts.Point#select
  41228. *
  41229. * @param {boolean} [selected]
  41230. * When `true`, the point is selected. When `false`, the point is
  41231. * unselected. When `null` or `undefined`, the selection state is toggled.
  41232. *
  41233. * @param {boolean} [accumulate=false]
  41234. * When `true`, the selection is added to other selected points.
  41235. * When `false`, other selected points are deselected. Internally in
  41236. * Highcharts, when
  41237. * [allowPointSelect](https://api.highcharts.com/highcharts/plotOptions.series.allowPointSelect)
  41238. * is `true`, selected points are accumulated on Control, Shift or Cmd
  41239. * clicking the point.
  41240. *
  41241. * @fires Highcharts.Point#event:select
  41242. * @fires Highcharts.Point#event:unselect
  41243. */
  41244. select: function (selected, accumulate) {
  41245. var point = this, series = point.series, chart = series.chart;
  41246. selected = pick(selected, !point.selected);
  41247. this.selectedStaging = selected;
  41248. // fire the event with the default handler
  41249. point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () {
  41250. /**
  41251. * Whether the point is selected or not.
  41252. *
  41253. * @see Point#select
  41254. * @see Chart#getSelectedPoints
  41255. *
  41256. * @name Highcharts.Point#selected
  41257. * @type {boolean}
  41258. */
  41259. point.selected = point.options.selected = selected;
  41260. series.options.data[series.data.indexOf(point)] =
  41261. point.options;
  41262. point.setState(selected && 'select');
  41263. // unselect all other points unless Ctrl or Cmd + click
  41264. if (!accumulate) {
  41265. chart.getSelectedPoints().forEach(function (loopPoint) {
  41266. var loopSeries = loopPoint.series;
  41267. if (loopPoint.selected && loopPoint !== point) {
  41268. loopPoint.selected = loopPoint.options.selected =
  41269. false;
  41270. loopSeries.options.data[loopSeries.data.indexOf(loopPoint)] = loopPoint.options;
  41271. // Programatically selecting a point should restore
  41272. // normal state, but when click happened on other
  41273. // point, set inactive state to match other points
  41274. loopPoint.setState(chart.hoverPoints &&
  41275. loopSeries.options.inactiveOtherPoints ?
  41276. 'inactive' : '');
  41277. loopPoint.firePointEvent('unselect');
  41278. }
  41279. });
  41280. }
  41281. });
  41282. delete this.selectedStaging;
  41283. },
  41284. /**
  41285. * Runs on mouse over the point. Called internally from mouse and touch
  41286. * events.
  41287. *
  41288. * @function Highcharts.Point#onMouseOver
  41289. *
  41290. * @param {Highcharts.PointerEventObject} [e]
  41291. * The event arguments.
  41292. */
  41293. onMouseOver: function (e) {
  41294. var point = this, series = point.series, chart = series.chart, pointer = chart.pointer;
  41295. e = e ?
  41296. pointer.normalize(e) :
  41297. // In cases where onMouseOver is called directly without an event
  41298. pointer.getChartCoordinatesFromPoint(point, chart.inverted);
  41299. pointer.runPointActions(e, point);
  41300. },
  41301. /**
  41302. * Runs on mouse out from the point. Called internally from mouse and touch
  41303. * events.
  41304. *
  41305. * @function Highcharts.Point#onMouseOut
  41306. * @fires Highcharts.Point#event:mouseOut
  41307. */
  41308. onMouseOut: function () {
  41309. var point = this, chart = point.series.chart;
  41310. point.firePointEvent('mouseOut');
  41311. if (!point.series.options.inactiveOtherPoints) {
  41312. (chart.hoverPoints || []).forEach(function (p) {
  41313. p.setState();
  41314. });
  41315. }
  41316. chart.hoverPoints = chart.hoverPoint = null;
  41317. },
  41318. /**
  41319. * Import events from the series' and point's options. Only do it on
  41320. * demand, to save processing time on hovering.
  41321. *
  41322. * @private
  41323. * @function Highcharts.Point#importEvents
  41324. */
  41325. importEvents: function () {
  41326. if (!this.hasImportedEvents) {
  41327. var point = this, options = merge(point.series.options.point, point.options), events = options.events;
  41328. point.events = events;
  41329. objectEach(events, function (event, eventType) {
  41330. if (isFunction(event)) {
  41331. addEvent(point, eventType, event);
  41332. }
  41333. });
  41334. this.hasImportedEvents = true;
  41335. }
  41336. },
  41337. /**
  41338. * Set the point's state.
  41339. *
  41340. * @function Highcharts.Point#setState
  41341. *
  41342. * @param {Highcharts.PointStateValue|""} [state]
  41343. * The new state, can be one of `'hover'`, `'select'`, `'inactive'`,
  41344. * or `''` (an empty string), `'normal'` or `undefined` to set to
  41345. * normal state.
  41346. * @param {boolean} [move]
  41347. * State for animation.
  41348. *
  41349. * @fires Highcharts.Point#event:afterSetState
  41350. */
  41351. setState: function (state, move) {
  41352. var point = this, series = point.series, previousState = point.state, stateOptions = (series.options.states[state || 'normal'] ||
  41353. {}), markerOptions = (defaultOptions.plotOptions[series.type].marker &&
  41354. series.options.marker), normalDisabled = (markerOptions && markerOptions.enabled === false), markerStateOptions = ((markerOptions &&
  41355. markerOptions.states &&
  41356. markerOptions.states[state || 'normal']) || {}), stateDisabled = markerStateOptions.enabled === false, stateMarkerGraphic = series.stateMarkerGraphic, pointMarker = point.marker || {}, chart = series.chart, halo = series.halo, haloOptions, markerAttribs, pointAttribs, pointAttribsAnimation, hasMarkers = (markerOptions && series.markerAttribs), newSymbol;
  41357. state = state || ''; // empty string
  41358. if (
  41359. // already has this state
  41360. (state === point.state && !move) ||
  41361. // selected points don't respond to hover
  41362. (point.selected && state !== 'select') ||
  41363. // series' state options is disabled
  41364. (stateOptions.enabled === false) ||
  41365. // general point marker's state options is disabled
  41366. (state && (stateDisabled ||
  41367. (normalDisabled &&
  41368. markerStateOptions.enabled === false))) ||
  41369. // individual point marker's state options is disabled
  41370. (state &&
  41371. pointMarker.states &&
  41372. pointMarker.states[state] &&
  41373. pointMarker.states[state].enabled === false) // #1610
  41374. ) {
  41375. return;
  41376. }
  41377. point.state = state;
  41378. if (hasMarkers) {
  41379. markerAttribs = series.markerAttribs(point, state);
  41380. }
  41381. // Apply hover styles to the existing point
  41382. if (point.graphic) {
  41383. if (previousState) {
  41384. point.graphic.removeClass('highcharts-point-' + previousState);
  41385. }
  41386. if (state) {
  41387. point.graphic.addClass('highcharts-point-' + state);
  41388. }
  41389. if (!chart.styledMode) {
  41390. pointAttribs = series.pointAttribs(point, state);
  41391. pointAttribsAnimation = pick(chart.options.chart.animation, stateOptions.animation);
  41392. // Some inactive points (e.g. slices in pie) should apply
  41393. // oppacity also for it's labels
  41394. if (series.options.inactiveOtherPoints && pointAttribs.opacity) {
  41395. (point.dataLabels || []).forEach(function (label) {
  41396. if (label) {
  41397. label.animate({
  41398. opacity: pointAttribs.opacity
  41399. }, pointAttribsAnimation);
  41400. }
  41401. });
  41402. if (point.connector) {
  41403. point.connector.animate({
  41404. opacity: pointAttribs.opacity
  41405. }, pointAttribsAnimation);
  41406. }
  41407. }
  41408. point.graphic.animate(pointAttribs, pointAttribsAnimation);
  41409. }
  41410. if (markerAttribs) {
  41411. point.graphic.animate(markerAttribs, pick(
  41412. // Turn off globally:
  41413. chart.options.chart.animation, markerStateOptions.animation, markerOptions.animation));
  41414. }
  41415. // Zooming in from a range with no markers to a range with markers
  41416. if (stateMarkerGraphic) {
  41417. stateMarkerGraphic.hide();
  41418. }
  41419. }
  41420. else {
  41421. // if a graphic is not applied to each point in the normal state,
  41422. // create a shared graphic for the hover state
  41423. if (state && markerStateOptions) {
  41424. newSymbol = pointMarker.symbol || series.symbol;
  41425. // If the point has another symbol than the previous one, throw
  41426. // away the state marker graphic and force a new one (#1459)
  41427. if (stateMarkerGraphic &&
  41428. stateMarkerGraphic.currentSymbol !== newSymbol) {
  41429. stateMarkerGraphic = stateMarkerGraphic.destroy();
  41430. }
  41431. // Add a new state marker graphic
  41432. if (markerAttribs) {
  41433. if (!stateMarkerGraphic) {
  41434. if (newSymbol) {
  41435. series.stateMarkerGraphic = stateMarkerGraphic =
  41436. chart.renderer
  41437. .symbol(newSymbol, markerAttribs.x, markerAttribs.y, markerAttribs.width, markerAttribs.height)
  41438. .add(series.markerGroup);
  41439. stateMarkerGraphic.currentSymbol = newSymbol;
  41440. }
  41441. // Move the existing graphic
  41442. }
  41443. else {
  41444. stateMarkerGraphic[move ? 'animate' : 'attr']({
  41445. x: markerAttribs.x,
  41446. y: markerAttribs.y
  41447. });
  41448. }
  41449. }
  41450. if (!chart.styledMode && stateMarkerGraphic) {
  41451. stateMarkerGraphic.attr(series.pointAttribs(point, state));
  41452. }
  41453. }
  41454. if (stateMarkerGraphic) {
  41455. stateMarkerGraphic[state && point.isInside ? 'show' : 'hide'](); // #2450
  41456. stateMarkerGraphic.element.point = point; // #4310
  41457. }
  41458. }
  41459. // Show me your halo
  41460. haloOptions = stateOptions.halo;
  41461. var markerGraphic = (point.graphic || stateMarkerGraphic);
  41462. var markerVisibility = (markerGraphic && markerGraphic.visibility || 'inherit');
  41463. if (haloOptions &&
  41464. haloOptions.size &&
  41465. markerGraphic &&
  41466. markerVisibility !== 'hidden' &&
  41467. !point.isCluster) {
  41468. if (!halo) {
  41469. series.halo = halo = chart.renderer.path()
  41470. // #5818, #5903, #6705
  41471. .add(markerGraphic.parentGroup);
  41472. }
  41473. halo.show()[move ? 'animate' : 'attr']({
  41474. d: point.haloPath(haloOptions.size)
  41475. });
  41476. halo.attr({
  41477. 'class': 'highcharts-halo highcharts-color-' +
  41478. pick(point.colorIndex, series.colorIndex) +
  41479. (point.className ? ' ' + point.className : ''),
  41480. 'visibility': markerVisibility,
  41481. 'zIndex': -1 // #4929, #8276
  41482. });
  41483. halo.point = point; // #6055
  41484. if (!chart.styledMode) {
  41485. halo.attr(extend({
  41486. 'fill': point.color || series.color,
  41487. 'fill-opacity': haloOptions.opacity
  41488. }, haloOptions.attributes));
  41489. }
  41490. }
  41491. else if (halo && halo.point && halo.point.haloPath) {
  41492. // Animate back to 0 on the current halo point (#6055)
  41493. halo.animate({ d: halo.point.haloPath(0) }, null,
  41494. // Hide after unhovering. The `complete` callback runs in the
  41495. // halo's context (#7681).
  41496. halo.hide);
  41497. }
  41498. fireEvent(point, 'afterSetState');
  41499. },
  41500. /**
  41501. * Get the path definition for the halo, which is usually a shadow-like
  41502. * circle around the currently hovered point.
  41503. *
  41504. * @function Highcharts.Point#haloPath
  41505. *
  41506. * @param {number} size
  41507. * The radius of the circular halo.
  41508. *
  41509. * @return {Highcharts.SVGPathArray}
  41510. * The path definition.
  41511. */
  41512. haloPath: function (size) {
  41513. var series = this.series, chart = series.chart;
  41514. return chart.renderer.symbols.circle(Math.floor(this.plotX) - size, this.plotY - size, size * 2, size * 2);
  41515. }
  41516. });
  41517. // Extend the Series object with interaction
  41518. extend(Series.prototype, /** @lends Highcharts.Series.prototype */ {
  41519. /**
  41520. * Runs on mouse over the series graphical items.
  41521. *
  41522. * @function Highcharts.Series#onMouseOver
  41523. * @fires Highcharts.Series#event:mouseOver
  41524. */
  41525. onMouseOver: function () {
  41526. var series = this, chart = series.chart, hoverSeries = chart.hoverSeries, pointer = chart.pointer;
  41527. pointer.setHoverChartIndex();
  41528. // set normal state to previous series
  41529. if (hoverSeries && hoverSeries !== series) {
  41530. hoverSeries.onMouseOut();
  41531. }
  41532. // trigger the event, but to save processing time,
  41533. // only if defined
  41534. if (series.options.events.mouseOver) {
  41535. fireEvent(series, 'mouseOver');
  41536. }
  41537. // hover this
  41538. series.setState('hover');
  41539. /**
  41540. * Contains the original hovered series.
  41541. *
  41542. * @name Highcharts.Chart#hoverSeries
  41543. * @type {Highcharts.Series|null}
  41544. */
  41545. chart.hoverSeries = series;
  41546. },
  41547. /**
  41548. * Runs on mouse out of the series graphical items.
  41549. *
  41550. * @function Highcharts.Series#onMouseOut
  41551. *
  41552. * @fires Highcharts.Series#event:mouseOut
  41553. */
  41554. onMouseOut: function () {
  41555. // trigger the event only if listeners exist
  41556. var series = this, options = series.options, chart = series.chart, tooltip = chart.tooltip, hoverPoint = chart.hoverPoint;
  41557. // #182, set to null before the mouseOut event fires
  41558. chart.hoverSeries = null;
  41559. // trigger mouse out on the point, which must be in this series
  41560. if (hoverPoint) {
  41561. hoverPoint.onMouseOut();
  41562. }
  41563. // fire the mouse out event
  41564. if (series && options.events.mouseOut) {
  41565. fireEvent(series, 'mouseOut');
  41566. }
  41567. // hide the tooltip
  41568. if (tooltip &&
  41569. !series.stickyTracking &&
  41570. (!tooltip.shared || series.noSharedTooltip)) {
  41571. tooltip.hide();
  41572. }
  41573. // Reset all inactive states
  41574. chart.series.forEach(function (s) {
  41575. s.setState('', true);
  41576. });
  41577. },
  41578. /**
  41579. * Set the state of the series. Called internally on mouse interaction
  41580. * operations, but it can also be called directly to visually
  41581. * highlight a series.
  41582. *
  41583. * @function Highcharts.Series#setState
  41584. *
  41585. * @param {Highcharts.SeriesStateValue|""} [state]
  41586. * The new state, can be either `'hover'`, `'inactive'`, `'select'`,
  41587. * or `''` (an empty string), `'normal'` or `undefined` to set to
  41588. * normal state.
  41589. * @param {boolean} [inherit]
  41590. * Determines if state should be inherited by points too.
  41591. */
  41592. setState: function (state, inherit) {
  41593. var series = this, options = series.options, graph = series.graph, inactiveOtherPoints = options.inactiveOtherPoints, stateOptions = options.states, lineWidth = options.lineWidth, opacity = options.opacity,
  41594. // By default a quick animation to hover/inactive,
  41595. // slower to un-hover
  41596. stateAnimation = pick((stateOptions[state || 'normal'] &&
  41597. stateOptions[state || 'normal'].animation), series.chart.options.chart.animation), attribs, i = 0;
  41598. state = state || '';
  41599. if (series.state !== state) {
  41600. // Toggle class names
  41601. [
  41602. series.group,
  41603. series.markerGroup,
  41604. series.dataLabelsGroup
  41605. ].forEach(function (group) {
  41606. if (group) {
  41607. // Old state
  41608. if (series.state) {
  41609. group.removeClass('highcharts-series-' + series.state);
  41610. }
  41611. // New state
  41612. if (state) {
  41613. group.addClass('highcharts-series-' + state);
  41614. }
  41615. }
  41616. });
  41617. series.state = state;
  41618. if (!series.chart.styledMode) {
  41619. if (stateOptions[state] &&
  41620. stateOptions[state].enabled === false) {
  41621. return;
  41622. }
  41623. if (state) {
  41624. lineWidth = (stateOptions[state].lineWidth ||
  41625. lineWidth + (stateOptions[state].lineWidthPlus || 0)); // #4035
  41626. opacity = pick(stateOptions[state].opacity, opacity);
  41627. }
  41628. if (graph && !graph.dashstyle) {
  41629. attribs = {
  41630. 'stroke-width': lineWidth
  41631. };
  41632. // Animate the graph stroke-width.
  41633. graph.animate(attribs, stateAnimation);
  41634. while (series['zone-graph-' + i]) {
  41635. series['zone-graph-' + i].attr(attribs);
  41636. i = i + 1;
  41637. }
  41638. }
  41639. // For some types (pie, networkgraph, sankey) opacity is
  41640. // resolved on a point level
  41641. if (!inactiveOtherPoints) {
  41642. [
  41643. series.group,
  41644. series.markerGroup,
  41645. series.dataLabelsGroup,
  41646. series.labelBySeries
  41647. ].forEach(function (group) {
  41648. if (group) {
  41649. group.animate({
  41650. opacity: opacity
  41651. }, stateAnimation);
  41652. }
  41653. });
  41654. }
  41655. }
  41656. }
  41657. // Don't loop over points on a series that doesn't apply inactive state
  41658. // to siblings markers (e.g. line, column)
  41659. if (inherit && inactiveOtherPoints && series.points) {
  41660. series.setAllPointsToState(state);
  41661. }
  41662. },
  41663. /**
  41664. * Set the state for all points in the series.
  41665. *
  41666. * @function Highcharts.Series#setAllPointsToState
  41667. *
  41668. * @private
  41669. *
  41670. * @param {string} [state]
  41671. * Can be either `hover` or undefined to set to normal state.
  41672. */
  41673. setAllPointsToState: function (state) {
  41674. this.points.forEach(function (point) {
  41675. if (point.setState) {
  41676. point.setState(state);
  41677. }
  41678. });
  41679. },
  41680. /**
  41681. * Show or hide the series.
  41682. *
  41683. * @function Highcharts.Series#setVisible
  41684. *
  41685. * @param {boolean} [visible]
  41686. * True to show the series, false to hide. If undefined, the visibility is
  41687. * toggled.
  41688. *
  41689. * @param {boolean} [redraw=true]
  41690. * Whether to redraw the chart after the series is altered. If doing more
  41691. * operations on the chart, it is a good idea to set redraw to false and
  41692. * call {@link Chart#redraw|chart.redraw()} after.
  41693. *
  41694. * @fires Highcharts.Series#event:hide
  41695. * @fires Highcharts.Series#event:show
  41696. */
  41697. setVisible: function (vis, redraw) {
  41698. var series = this, chart = series.chart, legendItem = series.legendItem, showOrHide, ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, oldVisibility = series.visible;
  41699. // if called without an argument, toggle visibility
  41700. series.visible =
  41701. vis =
  41702. series.options.visible =
  41703. series.userOptions.visible =
  41704. typeof vis === 'undefined' ? !oldVisibility : vis; // #5618
  41705. showOrHide = vis ? 'show' : 'hide';
  41706. // show or hide elements
  41707. [
  41708. 'group',
  41709. 'dataLabelsGroup',
  41710. 'markerGroup',
  41711. 'tracker',
  41712. 'tt'
  41713. ].forEach(function (key) {
  41714. if (series[key]) {
  41715. series[key][showOrHide]();
  41716. }
  41717. });
  41718. // hide tooltip (#1361)
  41719. if (chart.hoverSeries === series ||
  41720. (chart.hoverPoint && chart.hoverPoint.series) === series) {
  41721. series.onMouseOut();
  41722. }
  41723. if (legendItem) {
  41724. chart.legend.colorizeItem(series, vis);
  41725. }
  41726. // rescale or adapt to resized chart
  41727. series.isDirty = true;
  41728. // in a stack, all other series are affected
  41729. if (series.options.stacking) {
  41730. chart.series.forEach(function (otherSeries) {
  41731. if (otherSeries.options.stacking && otherSeries.visible) {
  41732. otherSeries.isDirty = true;
  41733. }
  41734. });
  41735. }
  41736. // show or hide linked series
  41737. series.linkedSeries.forEach(function (otherSeries) {
  41738. otherSeries.setVisible(vis, false);
  41739. });
  41740. if (ignoreHiddenSeries) {
  41741. chart.isDirtyBox = true;
  41742. }
  41743. fireEvent(series, showOrHide);
  41744. if (redraw !== false) {
  41745. chart.redraw();
  41746. }
  41747. },
  41748. /**
  41749. * Show the series if hidden.
  41750. *
  41751. * @sample highcharts/members/series-hide/
  41752. * Toggle visibility from a button
  41753. *
  41754. * @function Highcharts.Series#show
  41755. * @fires Highcharts.Series#event:show
  41756. */
  41757. show: function () {
  41758. this.setVisible(true);
  41759. },
  41760. /**
  41761. * Hide the series if visible. If the
  41762. * [chart.ignoreHiddenSeries](https://api.highcharts.com/highcharts/chart.ignoreHiddenSeries)
  41763. * option is true, the chart is redrawn without this series.
  41764. *
  41765. * @sample highcharts/members/series-hide/
  41766. * Toggle visibility from a button
  41767. *
  41768. * @function Highcharts.Series#hide
  41769. * @fires Highcharts.Series#event:hide
  41770. */
  41771. hide: function () {
  41772. this.setVisible(false);
  41773. },
  41774. /**
  41775. * Select or unselect the series. This means its
  41776. * {@link Highcharts.Series.selected|selected}
  41777. * property is set, the checkbox in the legend is toggled and when selected,
  41778. * the series is returned by the {@link Highcharts.Chart#getSelectedSeries}
  41779. * function.
  41780. *
  41781. * @sample highcharts/members/series-select/
  41782. * Select a series from a button
  41783. *
  41784. * @function Highcharts.Series#select
  41785. *
  41786. * @param {boolean} [selected]
  41787. * True to select the series, false to unselect. If undefined, the selection
  41788. * state is toggled.
  41789. *
  41790. * @fires Highcharts.Series#event:select
  41791. * @fires Highcharts.Series#event:unselect
  41792. */
  41793. select: function (selected) {
  41794. var series = this;
  41795. series.selected =
  41796. selected =
  41797. this.options.selected = (typeof selected === 'undefined' ?
  41798. !series.selected :
  41799. selected);
  41800. if (series.checkbox) {
  41801. series.checkbox.checked = selected;
  41802. }
  41803. fireEvent(series, selected ? 'select' : 'unselect');
  41804. },
  41805. /**
  41806. * @private
  41807. * @borrows Highcharts.TrackerMixin.drawTrackerGraph as Highcharts.Series#drawTracker
  41808. */
  41809. drawTracker: TrackerMixin.drawTrackerGraph
  41810. });
  41811. });
  41812. _registerModule(_modules, 'parts/Responsive.js', [_modules['parts/Chart.js'], _modules['parts/Utilities.js']], function (Chart, U) {
  41813. /* *
  41814. *
  41815. * (c) 2010-2020 Torstein Honsi
  41816. *
  41817. * License: www.highcharts.com/license
  41818. *
  41819. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41820. *
  41821. * */
  41822. var find = U.find, isArray = U.isArray, isObject = U.isObject, merge = U.merge, objectEach = U.objectEach, pick = U.pick, splat = U.splat, uniqueKey = U.uniqueKey;
  41823. /**
  41824. * A callback function to gain complete control on when the responsive rule
  41825. * applies.
  41826. *
  41827. * @callback Highcharts.ResponsiveCallbackFunction
  41828. *
  41829. * @param {Highcharts.Chart} this
  41830. * Chart context.
  41831. *
  41832. * @return {boolean}
  41833. * Return `true` if it applies.
  41834. */
  41835. /**
  41836. * Allows setting a set of rules to apply for different screen or chart
  41837. * sizes. Each rule specifies additional chart options.
  41838. *
  41839. * @sample {highstock} stock/demo/responsive/
  41840. * Stock chart
  41841. * @sample highcharts/responsive/axis/
  41842. * Axis
  41843. * @sample highcharts/responsive/legend/
  41844. * Legend
  41845. * @sample highcharts/responsive/classname/
  41846. * Class name
  41847. *
  41848. * @since 5.0.0
  41849. * @apioption responsive
  41850. */
  41851. /**
  41852. * A set of rules for responsive settings. The rules are executed from
  41853. * the top down.
  41854. *
  41855. * @sample {highcharts} highcharts/responsive/axis/
  41856. * Axis changes
  41857. * @sample {highstock} highcharts/responsive/axis/
  41858. * Axis changes
  41859. * @sample {highmaps} highcharts/responsive/axis/
  41860. * Axis changes
  41861. *
  41862. * @type {Array<*>}
  41863. * @since 5.0.0
  41864. * @apioption responsive.rules
  41865. */
  41866. /**
  41867. * A full set of chart options to apply as overrides to the general
  41868. * chart options. The chart options are applied when the given rule
  41869. * is active.
  41870. *
  41871. * A special case is configuration objects that take arrays, for example
  41872. * [xAxis](#xAxis), [yAxis](#yAxis) or [series](#series). For these
  41873. * collections, an `id` option is used to map the new option set to
  41874. * an existing object. If an existing object of the same id is not found,
  41875. * the item of the same indexupdated. So for example, setting `chartOptions`
  41876. * with two series items without an `id`, will cause the existing chart's
  41877. * two series to be updated with respective options.
  41878. *
  41879. * @sample {highstock} stock/demo/responsive/
  41880. * Stock chart
  41881. * @sample highcharts/responsive/axis/
  41882. * Axis
  41883. * @sample highcharts/responsive/legend/
  41884. * Legend
  41885. * @sample highcharts/responsive/classname/
  41886. * Class name
  41887. *
  41888. * @type {Highcharts.Options}
  41889. * @since 5.0.0
  41890. * @apioption responsive.rules.chartOptions
  41891. */
  41892. /**
  41893. * Under which conditions the rule applies.
  41894. *
  41895. * @since 5.0.0
  41896. * @apioption responsive.rules.condition
  41897. */
  41898. /**
  41899. * A callback function to gain complete control on when the responsive
  41900. * rule applies. Return `true` if it applies. This opens for checking
  41901. * against other metrics than the chart size, for example the document
  41902. * size or other elements.
  41903. *
  41904. * @type {Highcharts.ResponsiveCallbackFunction}
  41905. * @since 5.0.0
  41906. * @context Highcharts.Chart
  41907. * @apioption responsive.rules.condition.callback
  41908. */
  41909. /**
  41910. * The responsive rule applies if the chart height is less than this.
  41911. *
  41912. * @type {number}
  41913. * @since 5.0.0
  41914. * @apioption responsive.rules.condition.maxHeight
  41915. */
  41916. /**
  41917. * The responsive rule applies if the chart width is less than this.
  41918. *
  41919. * @sample highcharts/responsive/axis/
  41920. * Max width is 500
  41921. *
  41922. * @type {number}
  41923. * @since 5.0.0
  41924. * @apioption responsive.rules.condition.maxWidth
  41925. */
  41926. /**
  41927. * The responsive rule applies if the chart height is greater than this.
  41928. *
  41929. * @type {number}
  41930. * @default 0
  41931. * @since 5.0.0
  41932. * @apioption responsive.rules.condition.minHeight
  41933. */
  41934. /**
  41935. * The responsive rule applies if the chart width is greater than this.
  41936. *
  41937. * @type {number}
  41938. * @default 0
  41939. * @since 5.0.0
  41940. * @apioption responsive.rules.condition.minWidth
  41941. */
  41942. /* eslint-disable no-invalid-this, valid-jsdoc */
  41943. /**
  41944. * Update the chart based on the current chart/document size and options for
  41945. * responsiveness.
  41946. *
  41947. * @private
  41948. * @function Highcharts.Chart#setResponsive
  41949. * @param {boolean} [redraw=true]
  41950. * @param {boolean} [reset=false]
  41951. * Reset by un-applying all rules. Chart.update resets all rules before applying
  41952. * updated options.
  41953. */
  41954. Chart.prototype.setResponsive = function (redraw, reset) {
  41955. var options = this.options.responsive, ruleIds = [], currentResponsive = this.currentResponsive, currentRuleIds, undoOptions;
  41956. if (!reset && options && options.rules) {
  41957. options.rules.forEach(function (rule) {
  41958. if (typeof rule._id === 'undefined') {
  41959. rule._id = uniqueKey();
  41960. }
  41961. this.matchResponsiveRule(rule, ruleIds /* , redraw */);
  41962. }, this);
  41963. }
  41964. // Merge matching rules
  41965. var mergedOptions = merge.apply(0, ruleIds.map(function (ruleId) {
  41966. return find(options.rules, function (rule) {
  41967. return rule._id === ruleId;
  41968. }).chartOptions;
  41969. }));
  41970. mergedOptions.isResponsiveOptions = true;
  41971. // Stringified key for the rules that currently apply.
  41972. ruleIds = (ruleIds.toString() || void 0);
  41973. currentRuleIds = currentResponsive && currentResponsive.ruleIds;
  41974. // Changes in what rules apply
  41975. if (ruleIds !== currentRuleIds) {
  41976. // Undo previous rules. Before we apply a new set of rules, we need to
  41977. // roll back completely to base options (#6291).
  41978. if (currentResponsive) {
  41979. this.update(currentResponsive.undoOptions, redraw, true);
  41980. }
  41981. if (ruleIds) {
  41982. // Get undo-options for matching rules
  41983. undoOptions = this.currentOptions(mergedOptions);
  41984. undoOptions.isResponsiveOptions = true;
  41985. this.currentResponsive = {
  41986. ruleIds: ruleIds,
  41987. mergedOptions: mergedOptions,
  41988. undoOptions: undoOptions
  41989. };
  41990. this.update(mergedOptions, redraw, true);
  41991. }
  41992. else {
  41993. this.currentResponsive = void 0;
  41994. }
  41995. }
  41996. };
  41997. /**
  41998. * Handle a single responsiveness rule.
  41999. *
  42000. * @private
  42001. * @function Highcharts.Chart#matchResponsiveRule
  42002. * @param {Highcharts.ResponsiveRulesOptions} rule
  42003. * @param {Array<string>} matches
  42004. */
  42005. Chart.prototype.matchResponsiveRule = function (rule, matches) {
  42006. var condition = rule.condition, fn = condition.callback || function () {
  42007. return (this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
  42008. this.chartHeight <=
  42009. pick(condition.maxHeight, Number.MAX_VALUE) &&
  42010. this.chartWidth >= pick(condition.minWidth, 0) &&
  42011. this.chartHeight >= pick(condition.minHeight, 0));
  42012. };
  42013. if (fn.call(this)) {
  42014. matches.push(rule._id);
  42015. }
  42016. };
  42017. /**
  42018. * Get the current values for a given set of options. Used before we update
  42019. * the chart with a new responsiveness rule.
  42020. *
  42021. * @todo Restore axis options (by id?). The matching of items in collections
  42022. * bears resemblance to the oneToOne matching in Chart.update. Probably we can
  42023. * refactor out that matching and reuse it in both functions.
  42024. *
  42025. * @private
  42026. * @function Highcharts.Chart#currentOptions
  42027. * @param {Highcharts.Options} options
  42028. * @return {Highcharts.Options}
  42029. */
  42030. Chart.prototype.currentOptions = function (options) {
  42031. var chart = this, ret = {};
  42032. /**
  42033. * Recurse over a set of options and its current values,
  42034. * and store the current values in the ret object.
  42035. */
  42036. function getCurrent(options, curr, ret, depth) {
  42037. var i;
  42038. objectEach(options, function (val, key) {
  42039. if (!depth &&
  42040. chart.collectionsWithUpdate.indexOf(key) > -1) {
  42041. val = splat(val);
  42042. ret[key] = [];
  42043. // Iterate over collections like series, xAxis or yAxis and map
  42044. // the items by index.
  42045. for (i = 0; i < Math.max(val.length, curr[key].length); i++) {
  42046. // Item exists in current data (#6347)
  42047. if (curr[key][i]) {
  42048. // If the item is missing from the new data, we need to
  42049. // save the whole config structure. Like when
  42050. // responsively updating from a dual axis layout to a
  42051. // single axis and back (#13544).
  42052. if (val[i] === void 0) {
  42053. ret[key][i] = curr[key][i];
  42054. // Otherwise, proceed
  42055. }
  42056. else {
  42057. ret[key][i] = {};
  42058. getCurrent(val[i], curr[key][i], ret[key][i], depth + 1);
  42059. }
  42060. }
  42061. }
  42062. }
  42063. else if (isObject(val)) {
  42064. ret[key] = isArray(val) ? [] : {};
  42065. getCurrent(val, curr[key] || {}, ret[key], depth + 1);
  42066. }
  42067. else if (typeof curr[key] === 'undefined') { // #10286
  42068. ret[key] = null;
  42069. }
  42070. else {
  42071. ret[key] = curr[key];
  42072. }
  42073. });
  42074. }
  42075. getCurrent(options, this.options, ret, 0);
  42076. return ret;
  42077. };
  42078. });
  42079. _registerModule(_modules, 'masters/highcharts.src.js', [_modules['parts/Globals.js']], function (Highcharts) {
  42080. return Highcharts;
  42081. });
  42082. _registerModule(_modules, 'parts/NavigatorAxis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  42083. /* *
  42084. *
  42085. * (c) 2010-2020 Torstein Honsi
  42086. *
  42087. * License: www.highcharts.com/license
  42088. *
  42089. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42090. *
  42091. * */
  42092. var isTouchDevice = H.isTouchDevice;
  42093. var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, isNumber = U.isNumber, pick = U.pick;
  42094. /* eslint-disable valid-jsdoc */
  42095. /**
  42096. * @private
  42097. * @class
  42098. */
  42099. var NavigatorAxisAdditions = /** @class */ (function () {
  42100. /* *
  42101. *
  42102. * Constructors
  42103. *
  42104. * */
  42105. function NavigatorAxisAdditions(axis) {
  42106. this.axis = axis;
  42107. }
  42108. /* *
  42109. *
  42110. * Functions
  42111. *
  42112. * */
  42113. /**
  42114. * @private
  42115. */
  42116. NavigatorAxisAdditions.prototype.destroy = function () {
  42117. this.axis = void 0;
  42118. };
  42119. /**
  42120. * Add logic to normalize the zoomed range in order to preserve the pressed
  42121. * state of range selector buttons
  42122. *
  42123. * @private
  42124. * @function Highcharts.Axis#toFixedRange
  42125. * @param {number} [pxMin]
  42126. * @param {number} [pxMax]
  42127. * @param {number} [fixedMin]
  42128. * @param {number} [fixedMax]
  42129. * @return {*}
  42130. */
  42131. NavigatorAxisAdditions.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
  42132. var navigator = this;
  42133. var axis = navigator.axis;
  42134. var chart = axis.chart;
  42135. var fixedRange = chart && chart.fixedRange, halfPointRange = (axis.pointRange || 0) / 2, newMin = pick(fixedMin, axis.translate(pxMin, true, !axis.horiz)), newMax = pick(fixedMax, axis.translate(pxMax, true, !axis.horiz)), changeRatio = fixedRange && (newMax - newMin) / fixedRange;
  42136. // Add/remove half point range to/from the extremes (#1172)
  42137. if (!defined(fixedMin)) {
  42138. newMin = correctFloat(newMin + halfPointRange);
  42139. }
  42140. if (!defined(fixedMax)) {
  42141. newMax = correctFloat(newMax - halfPointRange);
  42142. }
  42143. // If the difference between the fixed range and the actual requested
  42144. // range is too great, the user is dragging across an ordinal gap, and
  42145. // we need to release the range selector button.
  42146. if (changeRatio > 0.7 && changeRatio < 1.3) {
  42147. if (fixedMax) {
  42148. newMin = newMax - fixedRange;
  42149. }
  42150. else {
  42151. newMax = newMin + fixedRange;
  42152. }
  42153. }
  42154. if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
  42155. newMin = newMax = void 0;
  42156. }
  42157. return {
  42158. min: newMin,
  42159. max: newMax
  42160. };
  42161. };
  42162. return NavigatorAxisAdditions;
  42163. }());
  42164. /**
  42165. * @private
  42166. * @class
  42167. */
  42168. var NavigatorAxis = /** @class */ (function () {
  42169. function NavigatorAxis() {
  42170. }
  42171. /* *
  42172. *
  42173. * Static Functions
  42174. *
  42175. * */
  42176. /**
  42177. * @private
  42178. */
  42179. NavigatorAxis.compose = function (AxisClass) {
  42180. AxisClass.keepProps.push('navigatorAxis');
  42181. /* eslint-disable no-invalid-this */
  42182. addEvent(AxisClass, 'init', function () {
  42183. var axis = this;
  42184. if (!axis.navigatorAxis) {
  42185. axis.navigatorAxis = new NavigatorAxisAdditions(axis);
  42186. }
  42187. });
  42188. // For Stock charts, override selection zooming with some special
  42189. // features because X axis zooming is already allowed by the Navigator
  42190. // and Range selector.
  42191. addEvent(AxisClass, 'zoom', function (e) {
  42192. var axis = this;
  42193. var chart = axis.chart;
  42194. var chartOptions = chart.options;
  42195. var navigator = chartOptions.navigator;
  42196. var navigatorAxis = axis.navigatorAxis;
  42197. var pinchType = chartOptions.chart.pinchType;
  42198. var rangeSelector = chartOptions.rangeSelector;
  42199. var zoomType = chartOptions.chart.zoomType;
  42200. var previousZoom;
  42201. if (axis.isXAxis && ((navigator && navigator.enabled) ||
  42202. (rangeSelector && rangeSelector.enabled))) {
  42203. // For y only zooming, ignore the X axis completely
  42204. if (zoomType === 'y') {
  42205. e.zoomed = false;
  42206. // For xy zooming, record the state of the zoom before zoom
  42207. // selection, then when the reset button is pressed, revert to
  42208. // this state. This should apply only if the chart is
  42209. // initialized with a range (#6612), otherwise zoom all the way
  42210. // out.
  42211. }
  42212. else if (((!isTouchDevice && zoomType === 'xy') ||
  42213. (isTouchDevice && pinchType === 'xy')) &&
  42214. axis.options.range) {
  42215. previousZoom = navigatorAxis.previousZoom;
  42216. if (defined(e.newMin)) {
  42217. navigatorAxis.previousZoom = [axis.min, axis.max];
  42218. }
  42219. else if (previousZoom) {
  42220. e.newMin = previousZoom[0];
  42221. e.newMax = previousZoom[1];
  42222. navigatorAxis.previousZoom = void 0;
  42223. }
  42224. }
  42225. }
  42226. if (typeof e.zoomed !== 'undefined') {
  42227. e.preventDefault();
  42228. }
  42229. });
  42230. /* eslint-enable no-invalid-this */
  42231. };
  42232. /* *
  42233. *
  42234. * Static Properties
  42235. *
  42236. * */
  42237. /**
  42238. * @private
  42239. */
  42240. NavigatorAxis.AdditionsClass = NavigatorAxisAdditions;
  42241. return NavigatorAxis;
  42242. }());
  42243. return NavigatorAxis;
  42244. });
  42245. _registerModule(_modules, 'parts/ScrollbarAxis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  42246. /* *
  42247. *
  42248. * (c) 2010-2020 Torstein Honsi
  42249. *
  42250. * License: www.highcharts.com/license
  42251. *
  42252. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42253. *
  42254. * */
  42255. var addEvent = U.addEvent, defined = U.defined, pick = U.pick;
  42256. /* eslint-disable no-invalid-this, valid-jsdoc */
  42257. /**
  42258. * Creates scrollbars if enabled.
  42259. *
  42260. * @private
  42261. */
  42262. var ScrollbarAxis = /** @class */ (function () {
  42263. function ScrollbarAxis() {
  42264. }
  42265. /**
  42266. * Attaches to axis events to create scrollbars if enabled.
  42267. *
  42268. * @private
  42269. *
  42270. * @param AxisClass
  42271. * Axis class to extend.
  42272. *
  42273. * @param ScrollbarClass
  42274. * Scrollbar class to use.
  42275. */
  42276. ScrollbarAxis.compose = function (AxisClass, ScrollbarClass) {
  42277. // Wrap axis initialization and create scrollbar if enabled:
  42278. addEvent(AxisClass, 'afterInit', function () {
  42279. var axis = this;
  42280. if (axis.options &&
  42281. axis.options.scrollbar &&
  42282. axis.options.scrollbar.enabled) {
  42283. // Predefined options:
  42284. axis.options.scrollbar.vertical = !axis.horiz;
  42285. axis.options.startOnTick = axis.options.endOnTick = false;
  42286. axis.scrollbar = new ScrollbarClass(axis.chart.renderer, axis.options.scrollbar, axis.chart);
  42287. addEvent(axis.scrollbar, 'changed', function (e) {
  42288. var axisMin = pick(axis.options && axis.options.min, axis.min), axisMax = pick(axis.options && axis.options.max, axis.max), unitedMin = defined(axis.dataMin) ?
  42289. Math.min(axisMin, axis.min, axis.dataMin) : axisMin, unitedMax = defined(axis.dataMax) ?
  42290. Math.max(axisMax, axis.max, axis.dataMax) : axisMax, range = unitedMax - unitedMin, to, from;
  42291. // #12834, scroll when show/hide series, wrong extremes
  42292. if (!defined(axisMin) || !defined(axisMax)) {
  42293. return;
  42294. }
  42295. if ((axis.horiz && !axis.reversed) ||
  42296. (!axis.horiz && axis.reversed)) {
  42297. to = unitedMin + range * this.to;
  42298. from = unitedMin + range * this.from;
  42299. }
  42300. else {
  42301. // y-values in browser are reversed, but this also
  42302. // applies for reversed horizontal axis:
  42303. to = unitedMin + range * (1 - this.from);
  42304. from = unitedMin + range * (1 - this.to);
  42305. }
  42306. if (pick(this.options.liveRedraw, H.svg && !H.isTouchDevice && !this.chart.isBoosting) ||
  42307. // Mouseup always should change extremes
  42308. e.DOMType === 'mouseup' ||
  42309. // Internal events
  42310. !defined(e.DOMType)) {
  42311. axis.setExtremes(from, to, true, e.DOMType !== 'mousemove', e);
  42312. }
  42313. else {
  42314. // When live redraw is disabled, don't change extremes
  42315. // Only change the position of the scollbar thumb
  42316. this.setRange(this.from, this.to);
  42317. }
  42318. });
  42319. }
  42320. });
  42321. // Wrap rendering axis, and update scrollbar if one is created:
  42322. addEvent(AxisClass, 'afterRender', function () {
  42323. var axis = this, scrollMin = Math.min(pick(axis.options.min, axis.min), axis.min, pick(axis.dataMin, axis.min) // #6930
  42324. ), scrollMax = Math.max(pick(axis.options.max, axis.max), axis.max, pick(axis.dataMax, axis.max) // #6930
  42325. ), scrollbar = axis.scrollbar, offset = axis.axisTitleMargin + (axis.titleOffset || 0), scrollbarsOffsets = axis.chart.scrollbarsOffsets, axisMargin = axis.options.margin || 0, offsetsIndex, from, to;
  42326. if (scrollbar) {
  42327. if (axis.horiz) {
  42328. // Reserve space for labels/title
  42329. if (!axis.opposite) {
  42330. scrollbarsOffsets[1] += offset;
  42331. }
  42332. scrollbar.position(axis.left, axis.top + axis.height + 2 + scrollbarsOffsets[1] -
  42333. (axis.opposite ? axisMargin : 0), axis.width, axis.height);
  42334. // Next scrollbar should reserve space for margin (if set)
  42335. if (!axis.opposite) {
  42336. scrollbarsOffsets[1] += axisMargin;
  42337. }
  42338. offsetsIndex = 1;
  42339. }
  42340. else {
  42341. // Reserve space for labels/title
  42342. if (axis.opposite) {
  42343. scrollbarsOffsets[0] += offset;
  42344. }
  42345. scrollbar.position(axis.left + axis.width + 2 + scrollbarsOffsets[0] -
  42346. (axis.opposite ? 0 : axisMargin), axis.top, axis.width, axis.height);
  42347. // Next scrollbar should reserve space for margin (if set)
  42348. if (axis.opposite) {
  42349. scrollbarsOffsets[0] += axisMargin;
  42350. }
  42351. offsetsIndex = 0;
  42352. }
  42353. scrollbarsOffsets[offsetsIndex] += scrollbar.size +
  42354. scrollbar.options.margin;
  42355. if (isNaN(scrollMin) ||
  42356. isNaN(scrollMax) ||
  42357. !defined(axis.min) ||
  42358. !defined(axis.max) ||
  42359. axis.min === axis.max // #10733
  42360. ) {
  42361. // default action: when extremes are the same or there is
  42362. // not extremes on the axis, but scrollbar exists, make it
  42363. // full size
  42364. scrollbar.setRange(0, 1);
  42365. }
  42366. else {
  42367. from =
  42368. (axis.min - scrollMin) / (scrollMax - scrollMin);
  42369. to =
  42370. (axis.max - scrollMin) / (scrollMax - scrollMin);
  42371. if ((axis.horiz && !axis.reversed) ||
  42372. (!axis.horiz && axis.reversed)) {
  42373. scrollbar.setRange(from, to);
  42374. }
  42375. else {
  42376. // inverse vertical axis
  42377. scrollbar.setRange(1 - to, 1 - from);
  42378. }
  42379. }
  42380. }
  42381. });
  42382. // Make space for a scrollbar:
  42383. addEvent(AxisClass, 'afterGetOffset', function () {
  42384. var axis = this, index = axis.horiz ? 2 : 1, scrollbar = axis.scrollbar;
  42385. if (scrollbar) {
  42386. axis.chart.scrollbarsOffsets = [0, 0]; // reset scrollbars offsets
  42387. axis.chart.axisOffset[index] +=
  42388. scrollbar.size + scrollbar.options.margin;
  42389. }
  42390. });
  42391. };
  42392. return ScrollbarAxis;
  42393. }());
  42394. return ScrollbarAxis;
  42395. });
  42396. _registerModule(_modules, 'parts/Scrollbar.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/ScrollbarAxis.js'], _modules['parts/Utilities.js'], _modules['parts/Options.js']], function (Axis, H, ScrollbarAxis, U, O) {
  42397. /* *
  42398. *
  42399. * (c) 2010-2020 Torstein Honsi
  42400. *
  42401. * License: www.highcharts.com/license
  42402. *
  42403. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  42404. *
  42405. * */
  42406. var addEvent = U.addEvent, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, fireEvent = U.fireEvent, merge = U.merge, pick = U.pick, removeEvent = U.removeEvent;
  42407. var defaultOptions = O.defaultOptions;
  42408. var hasTouch = H.hasTouch, isTouchDevice = H.isTouchDevice;
  42409. /**
  42410. * When we have vertical scrollbar, rifles and arrow in buttons should be
  42411. * rotated. The same method is used in Navigator's handles, to rotate them.
  42412. *
  42413. * @function Highcharts.swapXY
  42414. *
  42415. * @param {Highcharts.SVGPathArray} path
  42416. * Path to be rotated.
  42417. *
  42418. * @param {boolean} [vertical]
  42419. * If vertical scrollbar, swap x-y values.
  42420. *
  42421. * @return {Highcharts.SVGPathArray}
  42422. * Rotated path.
  42423. *
  42424. * @requires modules/stock
  42425. */
  42426. var swapXY = H.swapXY = function (path, vertical) {
  42427. if (vertical) {
  42428. path.forEach(function (seg) {
  42429. var len = seg.length;
  42430. var temp;
  42431. for (var i = 0; i < len; i += 2) {
  42432. temp = seg[i + 1];
  42433. if (typeof temp === 'number') {
  42434. seg[i + 1] = seg[i + 2];
  42435. seg[i + 2] = temp;
  42436. }
  42437. }
  42438. });
  42439. }
  42440. return path;
  42441. };
  42442. /* eslint-disable no-invalid-this, valid-jsdoc */
  42443. /**
  42444. * A reusable scrollbar, internally used in Highstock's navigator and optionally
  42445. * on individual axes.
  42446. *
  42447. * @private
  42448. * @class
  42449. * @name Highcharts.Scrollbar
  42450. * @param {Highcharts.SVGRenderer} renderer
  42451. * @param {Highcharts.ScrollbarOptions} options
  42452. * @param {Highcharts.Chart} chart
  42453. */
  42454. var Scrollbar = /** @class */ (function () {
  42455. /* *
  42456. *
  42457. * Constructors
  42458. *
  42459. * */
  42460. function Scrollbar(renderer, options, chart) {
  42461. /* *
  42462. *
  42463. * Properties
  42464. *
  42465. * */
  42466. this._events = [];
  42467. this.chartX = 0;
  42468. this.chartY = 0;
  42469. this.from = 0;
  42470. this.group = void 0;
  42471. this.scrollbar = void 0;
  42472. this.scrollbarButtons = [];
  42473. this.scrollbarGroup = void 0;
  42474. this.scrollbarLeft = 0;
  42475. this.scrollbarRifles = void 0;
  42476. this.scrollbarStrokeWidth = 1;
  42477. this.scrollbarTop = 0;
  42478. this.size = 0;
  42479. this.to = 0;
  42480. this.track = void 0;
  42481. this.trackBorderWidth = 1;
  42482. this.userOptions = {};
  42483. this.x = 0;
  42484. this.y = 0;
  42485. this.chart = chart;
  42486. this.options = options;
  42487. this.renderer = chart.renderer;
  42488. this.init(renderer, options, chart);
  42489. }
  42490. /* *
  42491. *
  42492. * Functions
  42493. *
  42494. * */
  42495. /**
  42496. * Set up the mouse and touch events for the Scrollbar
  42497. *
  42498. * @private
  42499. * @function Highcharts.Scrollbar#addEvents
  42500. * @return {void}
  42501. */
  42502. Scrollbar.prototype.addEvents = function () {
  42503. var 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), _events;
  42504. // Mouse events
  42505. _events = [
  42506. [buttons[buttonsOrder[0]].element, 'click', this.buttonToMinClick.bind(this)],
  42507. [buttons[buttonsOrder[1]].element, 'click', this.buttonToMaxClick.bind(this)],
  42508. [track, 'click', this.trackClick.bind(this)],
  42509. [bar, 'mousedown', mouseDownHandler],
  42510. [bar.ownerDocument, 'mousemove', mouseMoveHandler],
  42511. [bar.ownerDocument, 'mouseup', mouseUpHandler]
  42512. ];
  42513. // Touch events
  42514. if (hasTouch) {
  42515. _events.push([bar, 'touchstart', mouseDownHandler], [bar.ownerDocument, 'touchmove', mouseMoveHandler], [bar.ownerDocument, 'touchend', mouseUpHandler]);
  42516. }
  42517. // Add them all
  42518. _events.forEach(function (args) {
  42519. addEvent.apply(null, args);
  42520. });
  42521. this._events = _events;
  42522. };
  42523. Scrollbar.prototype.buttonToMaxClick = function (e) {
  42524. var scroller = this;
  42525. var range = (scroller.to - scroller.from) * pick(scroller.options.step, 0.2);
  42526. scroller.updatePosition(scroller.from + range, scroller.to + range);
  42527. fireEvent(scroller, 'changed', {
  42528. from: scroller.from,
  42529. to: scroller.to,
  42530. trigger: 'scrollbar',
  42531. DOMEvent: e
  42532. });
  42533. };
  42534. Scrollbar.prototype.buttonToMinClick = function (e) {
  42535. var scroller = this;
  42536. var range = correctFloat(scroller.to - scroller.from) *
  42537. pick(scroller.options.step, 0.2);
  42538. scroller.updatePosition(correctFloat(scroller.from - range), correctFloat(scroller.to - range));
  42539. fireEvent(scroller, 'changed', {
  42540. from: scroller.from,
  42541. to: scroller.to,
  42542. trigger: 'scrollbar',
  42543. DOMEvent: e
  42544. });
  42545. };
  42546. /**
  42547. * Get normalized (0-1) cursor position over the scrollbar
  42548. *
  42549. * @private
  42550. * @function Highcharts.Scrollbar#cursorToScrollbarPosition
  42551. *
  42552. * @param {*} normalizedEvent
  42553. * normalized event, with chartX and chartY values
  42554. *
  42555. * @return {Highcharts.Dictionary<number>}
  42556. * Local position {chartX, chartY}
  42557. */
  42558. Scrollbar.prototype.cursorToScrollbarPosition = function (normalizedEvent) {
  42559. var scroller = this, options = scroller.options, minWidthDifference = options.minWidth > scroller.calculatedWidth ?
  42560. options.minWidth :
  42561. 0; // minWidth distorts translation
  42562. return {
  42563. chartX: (normalizedEvent.chartX - scroller.x -
  42564. scroller.xOffset) /
  42565. (scroller.barWidth - minWidthDifference),
  42566. chartY: (normalizedEvent.chartY - scroller.y -
  42567. scroller.yOffset) /
  42568. (scroller.barWidth - minWidthDifference)
  42569. };
  42570. };
  42571. /**
  42572. * Destroys allocated elements.
  42573. *
  42574. * @private
  42575. * @function Highcharts.Scrollbar#destroy
  42576. * @return {void}
  42577. */
  42578. Scrollbar.prototype.destroy = function () {
  42579. var scroller = this.chart.scroller;
  42580. // Disconnect events added in addEvents
  42581. this.removeEvents();
  42582. // Destroy properties
  42583. [
  42584. 'track',
  42585. 'scrollbarRifles',
  42586. 'scrollbar',
  42587. 'scrollbarGroup',
  42588. 'group'
  42589. ].forEach(function (prop) {
  42590. if (this[prop] && this[prop].destroy) {
  42591. this[prop] = this[prop].destroy();
  42592. }
  42593. }, this);
  42594. // #6421, chart may have more scrollbars
  42595. if (scroller && this === scroller.scrollbar) {
  42596. scroller.scrollbar = null;
  42597. // Destroy elements in collection
  42598. destroyObjectProperties(scroller.scrollbarButtons);
  42599. }
  42600. };
  42601. /**
  42602. * Draw the scrollbar buttons with arrows
  42603. *
  42604. * @private
  42605. * @function Highcharts.Scrollbar#drawScrollbarButton
  42606. * @param {number} index
  42607. * 0 is left, 1 is right
  42608. * @return {void}
  42609. */
  42610. Scrollbar.prototype.drawScrollbarButton = function (index) {
  42611. var scroller = this, renderer = scroller.renderer, scrollbarButtons = scroller.scrollbarButtons, options = scroller.options, size = scroller.size, group, tempElem;
  42612. group = renderer.g().add(scroller.group);
  42613. scrollbarButtons.push(group);
  42614. // Create a rectangle for the scrollbar button
  42615. tempElem = renderer.rect()
  42616. .addClass('highcharts-scrollbar-button')
  42617. .add(group);
  42618. // Presentational attributes
  42619. if (!this.chart.styledMode) {
  42620. tempElem.attr({
  42621. stroke: options.buttonBorderColor,
  42622. 'stroke-width': options.buttonBorderWidth,
  42623. fill: options.buttonBackgroundColor
  42624. });
  42625. }
  42626. // Place the rectangle based on the rendered stroke width
  42627. tempElem.attr(tempElem.crisp({
  42628. x: -0.5,
  42629. y: -0.5,
  42630. width: size + 1,
  42631. height: size + 1,
  42632. r: options.buttonBorderRadius
  42633. }, tempElem.strokeWidth()));
  42634. // Button arrow
  42635. tempElem = renderer
  42636. .path(swapXY([[
  42637. 'M',
  42638. size / 2 + (index ? -1 : 1),
  42639. size / 2 - 3
  42640. ], [
  42641. 'L',
  42642. size / 2 + (index ? -1 : 1),
  42643. size / 2 + 3
  42644. ], [
  42645. 'L',
  42646. size / 2 + (index ? 2 : -2),
  42647. size / 2
  42648. ]], options.vertical))
  42649. .addClass('highcharts-scrollbar-arrow')
  42650. .add(scrollbarButtons[index]);
  42651. if (!this.chart.styledMode) {
  42652. tempElem.attr({
  42653. fill: options.buttonArrowColor
  42654. });
  42655. }
  42656. };
  42657. /**
  42658. * @private
  42659. * @function Highcharts.Scrollbar#init
  42660. * @param {Highcharts.SVGRenderer} renderer
  42661. * @param {Highcharts.ScrollbarOptions} options
  42662. * @param {Highcharts.Chart} chart
  42663. */
  42664. Scrollbar.prototype.init = function (renderer, options, chart) {
  42665. this.scrollbarButtons = [];
  42666. this.renderer = renderer;
  42667. this.userOptions = options;
  42668. this.options = merge(Scrollbar.defaultOptions, options);
  42669. this.chart = chart;
  42670. // backward compatibility
  42671. this.size = pick(this.options.size, this.options.height);
  42672. // Init
  42673. if (options.enabled) {
  42674. this.render();
  42675. this.addEvents();
  42676. }
  42677. };
  42678. Scrollbar.prototype.mouseDownHandler = function (e) {
  42679. var scroller = this;
  42680. var normalizedEvent = scroller.chart.pointer.normalize(e), mousePosition = scroller.cursorToScrollbarPosition(normalizedEvent);
  42681. scroller.chartX = mousePosition.chartX;
  42682. scroller.chartY = mousePosition.chartY;
  42683. scroller.initPositions = [scroller.from, scroller.to];
  42684. scroller.grabbedCenter = true;
  42685. };
  42686. /**
  42687. * Event handler for the mouse move event.
  42688. * @private
  42689. */
  42690. Scrollbar.prototype.mouseMoveHandler = function (e) {
  42691. var scroller = this;
  42692. var normalizedEvent = scroller.chart.pointer.normalize(e), options = scroller.options, direction = options.vertical ? 'chartY' : 'chartX', initPositions = scroller.initPositions || [], scrollPosition, chartPosition, change;
  42693. // In iOS, a mousemove event with e.pageX === 0 is fired when
  42694. // holding the finger down in the center of the scrollbar. This
  42695. // should be ignored.
  42696. if (scroller.grabbedCenter &&
  42697. // #4696, scrollbar failed on Android
  42698. (!e.touches || e.touches[0][direction] !== 0)) {
  42699. chartPosition = scroller.cursorToScrollbarPosition(normalizedEvent)[direction];
  42700. scrollPosition = scroller[direction];
  42701. change = chartPosition - scrollPosition;
  42702. scroller.hasDragged = true;
  42703. scroller.updatePosition(initPositions[0] + change, initPositions[1] + change);
  42704. if (scroller.hasDragged) {
  42705. fireEvent(scroller, 'changed', {
  42706. from: scroller.from,
  42707. to: scroller.to,
  42708. trigger: 'scrollbar',
  42709. DOMType: e.type,
  42710. DOMEvent: e
  42711. });
  42712. }
  42713. }
  42714. };
  42715. /**
  42716. * Event handler for the mouse up event.
  42717. * @private
  42718. */
  42719. Scrollbar.prototype.mouseUpHandler = function (e) {
  42720. var scroller = this;
  42721. if (scroller.hasDragged) {
  42722. fireEvent(scroller, 'changed', {
  42723. from: scroller.from,
  42724. to: scroller.to,
  42725. trigger: 'scrollbar',
  42726. DOMType: e.type,
  42727. DOMEvent: e
  42728. });
  42729. }
  42730. scroller.grabbedCenter =
  42731. scroller.hasDragged =
  42732. scroller.chartX =
  42733. scroller.chartY = null;
  42734. };
  42735. /**
  42736. * Position the scrollbar, method called from a parent with defined
  42737. * dimensions.
  42738. *
  42739. * @private
  42740. * @function Highcharts.Scrollbar#position
  42741. * @param {number} x
  42742. * x-position on the chart
  42743. * @param {number} y
  42744. * y-position on the chart
  42745. * @param {number} width
  42746. * width of the scrollbar
  42747. * @param {number} height
  42748. * height of the scorllbar
  42749. * @return {void}
  42750. */
  42751. Scrollbar.prototype.position = function (x, y, width, height) {
  42752. var scroller = this, options = scroller.options, vertical = options.vertical, xOffset = height, yOffset = 0, method = scroller.rendered ? 'animate' : 'attr';
  42753. scroller.x = x;
  42754. scroller.y = y + this.trackBorderWidth;
  42755. scroller.width = width; // width with buttons
  42756. scroller.height = height;
  42757. scroller.xOffset = xOffset;
  42758. scroller.yOffset = yOffset;
  42759. // If Scrollbar is a vertical type, swap options:
  42760. if (vertical) {
  42761. scroller.width = scroller.yOffset = width = yOffset = scroller.size;
  42762. scroller.xOffset = xOffset = 0;
  42763. scroller.barWidth = height - width * 2; // width without buttons
  42764. scroller.x = x = x + scroller.options.margin;
  42765. }
  42766. else {
  42767. scroller.height = scroller.xOffset = height = xOffset =
  42768. scroller.size;
  42769. scroller.barWidth = width - height * 2; // width without buttons
  42770. scroller.y = scroller.y + scroller.options.margin;
  42771. }
  42772. // Set general position for a group:
  42773. scroller.group[method]({
  42774. translateX: x,
  42775. translateY: scroller.y
  42776. });
  42777. // Resize background/track:
  42778. scroller.track[method]({
  42779. width: width,
  42780. height: height
  42781. });
  42782. // Move right/bottom button ot it's place:
  42783. scroller.scrollbarButtons[1][method]({
  42784. translateX: vertical ? 0 : width - xOffset,
  42785. translateY: vertical ? height - yOffset : 0
  42786. });
  42787. };
  42788. /**
  42789. * Removes the event handlers attached previously with addEvents.
  42790. *
  42791. * @private
  42792. * @function Highcharts.Scrollbar#removeEvents
  42793. * @return {void}
  42794. */
  42795. Scrollbar.prototype.removeEvents = function () {
  42796. this._events.forEach(function (args) {
  42797. removeEvent.apply(null, args);
  42798. });
  42799. this._events.length = 0;
  42800. };
  42801. /**
  42802. * Render scrollbar with all required items.
  42803. *
  42804. * @private
  42805. * @function Highcharts.Scrollbar#render
  42806. */
  42807. Scrollbar.prototype.render = function () {
  42808. var scroller = this, renderer = scroller.renderer, options = scroller.options, size = scroller.size, styledMode = this.chart.styledMode, group;
  42809. // Draw the scrollbar group
  42810. scroller.group = group = renderer.g('scrollbar').attr({
  42811. zIndex: options.zIndex,
  42812. translateY: -99999
  42813. }).add();
  42814. // Draw the scrollbar track:
  42815. scroller.track = renderer.rect()
  42816. .addClass('highcharts-scrollbar-track')
  42817. .attr({
  42818. x: 0,
  42819. r: options.trackBorderRadius || 0,
  42820. height: size,
  42821. width: size
  42822. }).add(group);
  42823. if (!styledMode) {
  42824. scroller.track.attr({
  42825. fill: options.trackBackgroundColor,
  42826. stroke: options.trackBorderColor,
  42827. 'stroke-width': options.trackBorderWidth
  42828. });
  42829. }
  42830. this.trackBorderWidth = scroller.track.strokeWidth();
  42831. scroller.track.attr({
  42832. y: -this.trackBorderWidth % 2 / 2
  42833. });
  42834. // Draw the scrollbar itself
  42835. scroller.scrollbarGroup = renderer.g().add(group);
  42836. scroller.scrollbar = renderer.rect()
  42837. .addClass('highcharts-scrollbar-thumb')
  42838. .attr({
  42839. height: size,
  42840. width: size,
  42841. r: options.barBorderRadius || 0
  42842. }).add(scroller.scrollbarGroup);
  42843. scroller.scrollbarRifles = renderer
  42844. .path(swapXY([
  42845. ['M', -3, size / 4],
  42846. ['L', -3, 2 * size / 3],
  42847. ['M', 0, size / 4],
  42848. ['L', 0, 2 * size / 3],
  42849. ['M', 3, size / 4],
  42850. ['L', 3, 2 * size / 3]
  42851. ], options.vertical))
  42852. .addClass('highcharts-scrollbar-rifles')
  42853. .add(scroller.scrollbarGroup);
  42854. if (!styledMode) {
  42855. scroller.scrollbar.attr({
  42856. fill: options.barBackgroundColor,
  42857. stroke: options.barBorderColor,
  42858. 'stroke-width': options.barBorderWidth
  42859. });
  42860. scroller.scrollbarRifles.attr({
  42861. stroke: options.rifleColor,
  42862. 'stroke-width': 1
  42863. });
  42864. }
  42865. scroller.scrollbarStrokeWidth = scroller.scrollbar.strokeWidth();
  42866. scroller.scrollbarGroup.translate(-scroller.scrollbarStrokeWidth % 2 / 2, -scroller.scrollbarStrokeWidth % 2 / 2);
  42867. // Draw the buttons:
  42868. scroller.drawScrollbarButton(0);
  42869. scroller.drawScrollbarButton(1);
  42870. };
  42871. /**
  42872. * Set scrollbar size, with a given scale.
  42873. *
  42874. * @private
  42875. * @function Highcharts.Scrollbar#setRange
  42876. * @param {number} from
  42877. * scale (0-1) where bar should start
  42878. * @param {number} to
  42879. * scale (0-1) where bar should end
  42880. * @return {void}
  42881. */
  42882. Scrollbar.prototype.setRange = function (from, to) {
  42883. var scroller = this, options = scroller.options, vertical = options.vertical, minWidth = options.minWidth, fullWidth = scroller.barWidth, fromPX, toPX, newPos, newSize, newRiflesPos, method = (this.rendered &&
  42884. !this.hasDragged &&
  42885. !(this.chart.navigator && this.chart.navigator.hasDragged)) ? 'animate' : 'attr';
  42886. if (!defined(fullWidth)) {
  42887. return;
  42888. }
  42889. from = Math.max(from, 0);
  42890. fromPX = Math.ceil(fullWidth * from);
  42891. toPX = fullWidth * Math.min(to, 1);
  42892. scroller.calculatedWidth = newSize = correctFloat(toPX - fromPX);
  42893. // We need to recalculate position, if minWidth is used
  42894. if (newSize < minWidth) {
  42895. fromPX = (fullWidth - minWidth + newSize) * from;
  42896. newSize = minWidth;
  42897. }
  42898. newPos = Math.floor(fromPX + scroller.xOffset + scroller.yOffset);
  42899. newRiflesPos = newSize / 2 - 0.5; // -0.5 -> rifle line width / 2
  42900. // Store current position:
  42901. scroller.from = from;
  42902. scroller.to = to;
  42903. if (!vertical) {
  42904. scroller.scrollbarGroup[method]({
  42905. translateX: newPos
  42906. });
  42907. scroller.scrollbar[method]({
  42908. width: newSize
  42909. });
  42910. scroller.scrollbarRifles[method]({
  42911. translateX: newRiflesPos
  42912. });
  42913. scroller.scrollbarLeft = newPos;
  42914. scroller.scrollbarTop = 0;
  42915. }
  42916. else {
  42917. scroller.scrollbarGroup[method]({
  42918. translateY: newPos
  42919. });
  42920. scroller.scrollbar[method]({
  42921. height: newSize
  42922. });
  42923. scroller.scrollbarRifles[method]({
  42924. translateY: newRiflesPos
  42925. });
  42926. scroller.scrollbarTop = newPos;
  42927. scroller.scrollbarLeft = 0;
  42928. }
  42929. if (newSize <= 12) {
  42930. scroller.scrollbarRifles.hide();
  42931. }
  42932. else {
  42933. scroller.scrollbarRifles.show(true);
  42934. }
  42935. // Show or hide the scrollbar based on the showFull setting
  42936. if (options.showFull === false) {
  42937. if (from <= 0 && to >= 1) {
  42938. scroller.group.hide();
  42939. }
  42940. else {
  42941. scroller.group.show();
  42942. }
  42943. }
  42944. scroller.rendered = true;
  42945. };
  42946. Scrollbar.prototype.trackClick = function (e) {
  42947. var scroller = this;
  42948. var normalizedEvent = scroller.chart.pointer.normalize(e), range = scroller.to - scroller.from, top = scroller.y + scroller.scrollbarTop, left = scroller.x + scroller.scrollbarLeft;
  42949. if ((scroller.options.vertical && normalizedEvent.chartY > top) ||
  42950. (!scroller.options.vertical && normalizedEvent.chartX > left)) {
  42951. // On the top or on the left side of the track:
  42952. scroller.updatePosition(scroller.from + range, scroller.to + range);
  42953. }
  42954. else {
  42955. // On the bottom or the right side of the track:
  42956. scroller.updatePosition(scroller.from - range, scroller.to - range);
  42957. }
  42958. fireEvent(scroller, 'changed', {
  42959. from: scroller.from,
  42960. to: scroller.to,
  42961. trigger: 'scrollbar',
  42962. DOMEvent: e
  42963. });
  42964. };
  42965. /**
  42966. * Update the scrollbar with new options
  42967. *
  42968. * @private
  42969. * @function Highcharts.Scrollbar#update
  42970. * @param {Highcharts.ScrollbarOptions} options
  42971. * @return {void}
  42972. */
  42973. Scrollbar.prototype.update = function (options) {
  42974. this.destroy();
  42975. this.init(this.chart.renderer, merge(true, this.options, options), this.chart);
  42976. };
  42977. /**
  42978. * Update position option in the Scrollbar, with normalized 0-1 scale
  42979. *
  42980. * @private
  42981. * @function Highcharts.Scrollbar#updatePosition
  42982. * @param {number} from
  42983. * @param {number} to
  42984. * @return {void}
  42985. */
  42986. Scrollbar.prototype.updatePosition = function (from, to) {
  42987. if (to > 1) {
  42988. from = correctFloat(1 - correctFloat(to - from));
  42989. to = 1;
  42990. }
  42991. if (from < 0) {
  42992. to = correctFloat(to - from);
  42993. from = 0;
  42994. }
  42995. this.from = from;
  42996. this.to = to;
  42997. };
  42998. /* *
  42999. *
  43000. * Static Properties
  43001. *
  43002. * */
  43003. /**
  43004. *
  43005. * The scrollbar is a means of panning over the X axis of a stock chart.
  43006. * Scrollbars can also be applied to other types of axes.
  43007. *
  43008. * Another approach to scrollable charts is the [chart.scrollablePlotArea](
  43009. * https://api.highcharts.com/highcharts/chart.scrollablePlotArea) option that
  43010. * is especially suitable for simpler cartesian charts on mobile.
  43011. *
  43012. * In styled mode, all the presentational options for the
  43013. * scrollbar are replaced by the classes `.highcharts-scrollbar-thumb`,
  43014. * `.highcharts-scrollbar-arrow`, `.highcharts-scrollbar-button`,
  43015. * `.highcharts-scrollbar-rifles` and `.highcharts-scrollbar-track`.
  43016. *
  43017. * @sample stock/yaxis/inverted-bar-scrollbar/
  43018. * A scrollbar on a simple bar chart
  43019. *
  43020. * @product highstock gantt
  43021. * @optionparent scrollbar
  43022. *
  43023. * @private
  43024. */
  43025. Scrollbar.defaultOptions = {
  43026. /**
  43027. * The height of the scrollbar. The height also applies to the width
  43028. * of the scroll arrows so that they are always squares. Defaults to
  43029. * 20 for touch devices and 14 for mouse devices.
  43030. *
  43031. * @sample stock/scrollbar/height/
  43032. * A 30px scrollbar
  43033. *
  43034. * @type {number}
  43035. * @default 20/14
  43036. */
  43037. height: isTouchDevice ? 20 : 14,
  43038. /**
  43039. * The border rounding radius of the bar.
  43040. *
  43041. * @sample stock/scrollbar/style/
  43042. * Scrollbar styling
  43043. */
  43044. barBorderRadius: 0,
  43045. /**
  43046. * The corner radius of the scrollbar buttons.
  43047. *
  43048. * @sample stock/scrollbar/style/
  43049. * Scrollbar styling
  43050. */
  43051. buttonBorderRadius: 0,
  43052. /**
  43053. * Enable or disable the scrollbar.
  43054. *
  43055. * @sample stock/scrollbar/enabled/
  43056. * Disable the scrollbar, only use navigator
  43057. *
  43058. * @type {boolean}
  43059. * @default true
  43060. * @apioption scrollbar.enabled
  43061. */
  43062. /**
  43063. * Whether to redraw the main chart as the scrollbar or the navigator
  43064. * zoomed window is moved. Defaults to `true` for modern browsers and
  43065. * `false` for legacy IE browsers as well as mobile devices.
  43066. *
  43067. * @sample stock/scrollbar/liveredraw
  43068. * Setting live redraw to false
  43069. *
  43070. * @type {boolean}
  43071. * @since 1.3
  43072. */
  43073. liveRedraw: void 0,
  43074. /**
  43075. * The margin between the scrollbar and its axis when the scrollbar is
  43076. * applied directly to an axis.
  43077. */
  43078. margin: 10,
  43079. /**
  43080. * The minimum width of the scrollbar.
  43081. *
  43082. * @since 1.2.5
  43083. */
  43084. minWidth: 6,
  43085. /**
  43086. * Whether to show or hide the scrollbar when the scrolled content is
  43087. * zoomed out to it full extent.
  43088. *
  43089. * @type {boolean}
  43090. * @default true
  43091. * @apioption scrollbar.showFull
  43092. */
  43093. step: 0.2,
  43094. /**
  43095. * The z index of the scrollbar group.
  43096. */
  43097. zIndex: 3,
  43098. /**
  43099. * The background color of the scrollbar itself.
  43100. *
  43101. * @sample stock/scrollbar/style/
  43102. * Scrollbar styling
  43103. *
  43104. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43105. */
  43106. barBackgroundColor: '#cccccc',
  43107. /**
  43108. * The width of the bar's border.
  43109. *
  43110. * @sample stock/scrollbar/style/
  43111. * Scrollbar styling
  43112. */
  43113. barBorderWidth: 1,
  43114. /**
  43115. * The color of the scrollbar's border.
  43116. *
  43117. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43118. */
  43119. barBorderColor: '#cccccc',
  43120. /**
  43121. * The color of the small arrow inside the scrollbar buttons.
  43122. *
  43123. * @sample stock/scrollbar/style/
  43124. * Scrollbar styling
  43125. *
  43126. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43127. */
  43128. buttonArrowColor: '#333333',
  43129. /**
  43130. * The color of scrollbar buttons.
  43131. *
  43132. * @sample stock/scrollbar/style/
  43133. * Scrollbar styling
  43134. *
  43135. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43136. */
  43137. buttonBackgroundColor: '#e6e6e6',
  43138. /**
  43139. * The color of the border of the scrollbar buttons.
  43140. *
  43141. * @sample stock/scrollbar/style/
  43142. * Scrollbar styling
  43143. *
  43144. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43145. */
  43146. buttonBorderColor: '#cccccc',
  43147. /**
  43148. * The border width of the scrollbar buttons.
  43149. *
  43150. * @sample stock/scrollbar/style/
  43151. * Scrollbar styling
  43152. */
  43153. buttonBorderWidth: 1,
  43154. /**
  43155. * The color of the small rifles in the middle of the scrollbar.
  43156. *
  43157. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43158. */
  43159. rifleColor: '#333333',
  43160. /**
  43161. * The color of the track background.
  43162. *
  43163. * @sample stock/scrollbar/style/
  43164. * Scrollbar styling
  43165. *
  43166. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43167. */
  43168. trackBackgroundColor: '#f2f2f2',
  43169. /**
  43170. * The color of the border of the scrollbar track.
  43171. *
  43172. * @sample stock/scrollbar/style/
  43173. * Scrollbar styling
  43174. *
  43175. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43176. */
  43177. trackBorderColor: '#f2f2f2',
  43178. /**
  43179. * The corner radius of the border of the scrollbar track.
  43180. *
  43181. * @sample stock/scrollbar/style/
  43182. * Scrollbar styling
  43183. *
  43184. * @type {number}
  43185. * @default 0
  43186. * @apioption scrollbar.trackBorderRadius
  43187. */
  43188. /**
  43189. * The width of the border of the scrollbar track.
  43190. *
  43191. * @sample stock/scrollbar/style/
  43192. * Scrollbar styling
  43193. */
  43194. trackBorderWidth: 1
  43195. };
  43196. return Scrollbar;
  43197. }());
  43198. if (!H.Scrollbar) {
  43199. defaultOptions.scrollbar = merge(true, Scrollbar.defaultOptions, defaultOptions.scrollbar);
  43200. H.Scrollbar = Scrollbar;
  43201. ScrollbarAxis.compose(Axis, Scrollbar);
  43202. }
  43203. return H.Scrollbar;
  43204. });
  43205. _registerModule(_modules, 'parts/Navigator.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Color.js'], _modules['parts/Globals.js'], _modules['parts/NavigatorAxis.js'], _modules['parts/Options.js'], _modules['parts/Scrollbar.js'], _modules['parts/Utilities.js']], function (Axis, Chart, Color, H, NavigatorAxis, O, Scrollbar, U) {
  43206. /* *
  43207. *
  43208. * (c) 2010-2020 Torstein Honsi
  43209. *
  43210. * License: www.highcharts.com/license
  43211. *
  43212. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  43213. *
  43214. * */
  43215. var color = Color.parse;
  43216. var defaultOptions = O.defaultOptions;
  43217. var addEvent = U.addEvent, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, erase = U.erase, extend = U.extend, find = U.find, isArray = U.isArray, isNumber = U.isNumber, merge = U.merge, pick = U.pick, removeEvent = U.removeEvent, splat = U.splat;
  43218. var hasTouch = H.hasTouch, isTouchDevice = H.isTouchDevice, Series = H.Series, seriesTypes = H.seriesTypes, defaultSeriesType,
  43219. // Finding the min or max of a set of variables where we don't know if they
  43220. // are defined, is a pattern that is repeated several places in Highcharts.
  43221. // Consider making this a global utility method.
  43222. numExt = function (extreme) {
  43223. var args = [];
  43224. for (var _i = 1; _i < arguments.length; _i++) {
  43225. args[_i - 1] = arguments[_i];
  43226. }
  43227. var numbers = [].filter.call(args, isNumber);
  43228. if (numbers.length) {
  43229. return Math[extreme].apply(0, numbers);
  43230. }
  43231. };
  43232. defaultSeriesType = typeof seriesTypes.areaspline === 'undefined' ?
  43233. 'line' :
  43234. 'areaspline';
  43235. extend(defaultOptions, {
  43236. /**
  43237. * Maximum range which can be set using the navigator's handles.
  43238. * Opposite of [xAxis.minRange](#xAxis.minRange).
  43239. *
  43240. * @sample {highstock} stock/navigator/maxrange/
  43241. * Defined max and min range
  43242. *
  43243. * @type {number}
  43244. * @since 6.0.0
  43245. * @product highstock gantt
  43246. * @apioption xAxis.maxRange
  43247. */
  43248. /**
  43249. * The navigator is a small series below the main series, displaying
  43250. * a view of the entire data set. It provides tools to zoom in and
  43251. * out on parts of the data as well as panning across the dataset.
  43252. *
  43253. * @product highstock gantt
  43254. * @optionparent navigator
  43255. */
  43256. navigator: {
  43257. /**
  43258. * Whether the navigator and scrollbar should adapt to updated data
  43259. * in the base X axis. When loading data async, as in the demo below,
  43260. * this should be `false`. Otherwise new data will trigger navigator
  43261. * redraw, which will cause unwanted looping. In the demo below, the
  43262. * data in the navigator is set only once. On navigating, only the main
  43263. * chart content is updated.
  43264. *
  43265. * @sample {highstock} stock/demo/lazy-loading/
  43266. * Set to false with async data loading
  43267. *
  43268. * @type {boolean}
  43269. * @default true
  43270. * @apioption navigator.adaptToUpdatedData
  43271. */
  43272. /**
  43273. * An integer identifying the index to use for the base series, or a
  43274. * string representing the id of the series.
  43275. *
  43276. * **Note**: As of Highcharts 5.0, this is now a deprecated option.
  43277. * Prefer [series.showInNavigator](#plotOptions.series.showInNavigator).
  43278. *
  43279. * @see [series.showInNavigator](#plotOptions.series.showInNavigator)
  43280. *
  43281. * @deprecated
  43282. * @type {number|string}
  43283. * @default 0
  43284. * @apioption navigator.baseSeries
  43285. */
  43286. /**
  43287. * Enable or disable the navigator.
  43288. *
  43289. * @sample {highstock} stock/navigator/enabled/
  43290. * Disable the navigator
  43291. *
  43292. * @type {boolean}
  43293. * @default true
  43294. * @apioption navigator.enabled
  43295. */
  43296. /**
  43297. * When the chart is inverted, whether to draw the navigator on the
  43298. * opposite side.
  43299. *
  43300. * @type {boolean}
  43301. * @default false
  43302. * @since 5.0.8
  43303. * @apioption navigator.opposite
  43304. */
  43305. /**
  43306. * The height of the navigator.
  43307. *
  43308. * @sample {highstock} stock/navigator/height/
  43309. * A higher navigator
  43310. */
  43311. height: 40,
  43312. /**
  43313. * The distance from the nearest element, the X axis or X axis labels.
  43314. *
  43315. * @sample {highstock} stock/navigator/margin/
  43316. * A margin of 2 draws the navigator closer to the X axis labels
  43317. */
  43318. margin: 25,
  43319. /**
  43320. * Whether the mask should be inside the range marking the zoomed
  43321. * range, or outside. In Highstock 1.x it was always `false`.
  43322. *
  43323. * @sample {highstock} stock/navigator/maskinside-false/
  43324. * False, mask outside
  43325. *
  43326. * @since 2.0
  43327. */
  43328. maskInside: true,
  43329. /**
  43330. * Options for the handles for dragging the zoomed area.
  43331. *
  43332. * @sample {highstock} stock/navigator/handles/
  43333. * Colored handles
  43334. */
  43335. handles: {
  43336. /**
  43337. * Width for handles.
  43338. *
  43339. * @sample {highstock} stock/navigator/styled-handles/
  43340. * Styled handles
  43341. *
  43342. * @since 6.0.0
  43343. */
  43344. width: 7,
  43345. /**
  43346. * Height for handles.
  43347. *
  43348. * @sample {highstock} stock/navigator/styled-handles/
  43349. * Styled handles
  43350. *
  43351. * @since 6.0.0
  43352. */
  43353. height: 15,
  43354. /**
  43355. * Array to define shapes of handles. 0-index for left, 1-index for
  43356. * right.
  43357. *
  43358. * Additionally, the URL to a graphic can be given on this form:
  43359. * `url(graphic.png)`. Note that for the image to be applied to
  43360. * exported charts, its URL needs to be accessible by the export
  43361. * server.
  43362. *
  43363. * Custom callbacks for symbol path generation can also be added to
  43364. * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
  43365. * used by its method name, as shown in the demo.
  43366. *
  43367. * @sample {highstock} stock/navigator/styled-handles/
  43368. * Styled handles
  43369. *
  43370. * @type {Array<string>}
  43371. * @default ["navigator-handle", "navigator-handle"]
  43372. * @since 6.0.0
  43373. */
  43374. symbols: ['navigator-handle', 'navigator-handle'],
  43375. /**
  43376. * Allows to enable/disable handles.
  43377. *
  43378. * @since 6.0.0
  43379. */
  43380. enabled: true,
  43381. /**
  43382. * The width for the handle border and the stripes inside.
  43383. *
  43384. * @sample {highstock} stock/navigator/styled-handles/
  43385. * Styled handles
  43386. *
  43387. * @since 6.0.0
  43388. * @apioption navigator.handles.lineWidth
  43389. */
  43390. lineWidth: 1,
  43391. /**
  43392. * The fill for the handle.
  43393. *
  43394. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43395. */
  43396. backgroundColor: '#f2f2f2',
  43397. /**
  43398. * The stroke for the handle border and the stripes inside.
  43399. *
  43400. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43401. */
  43402. borderColor: '#999999'
  43403. },
  43404. /**
  43405. * The color of the mask covering the areas of the navigator series
  43406. * that are currently not visible in the main series. The default
  43407. * color is bluish with an opacity of 0.3 to see the series below.
  43408. *
  43409. * @see In styled mode, the mask is styled with the
  43410. * `.highcharts-navigator-mask` and
  43411. * `.highcharts-navigator-mask-inside` classes.
  43412. *
  43413. * @sample {highstock} stock/navigator/maskfill/
  43414. * Blue, semi transparent mask
  43415. *
  43416. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43417. * @default rgba(102,133,194,0.3)
  43418. */
  43419. maskFill: color('#6685c2').setOpacity(0.3).get(),
  43420. /**
  43421. * The color of the line marking the currently zoomed area in the
  43422. * navigator.
  43423. *
  43424. * @sample {highstock} stock/navigator/outline/
  43425. * 2px blue outline
  43426. *
  43427. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43428. * @default #cccccc
  43429. */
  43430. outlineColor: '#cccccc',
  43431. /**
  43432. * The width of the line marking the currently zoomed area in the
  43433. * navigator.
  43434. *
  43435. * @see In styled mode, the outline stroke width is set with the
  43436. * `.highcharts-navigator-outline` class.
  43437. *
  43438. * @sample {highstock} stock/navigator/outline/
  43439. * 2px blue outline
  43440. *
  43441. * @type {number}
  43442. */
  43443. outlineWidth: 1,
  43444. /**
  43445. * Options for the navigator series. Available options are the same
  43446. * as any series, documented at [plotOptions](#plotOptions.series)
  43447. * and [series](#series).
  43448. *
  43449. * Unless data is explicitly defined on navigator.series, the data
  43450. * is borrowed from the first series in the chart.
  43451. *
  43452. * Default series options for the navigator series are:
  43453. * ```js
  43454. * series: {
  43455. * type: 'areaspline',
  43456. * fillOpacity: 0.05,
  43457. * dataGrouping: {
  43458. * smoothed: true
  43459. * },
  43460. * lineWidth: 1,
  43461. * marker: {
  43462. * enabled: false
  43463. * }
  43464. * }
  43465. * ```
  43466. *
  43467. * @see In styled mode, the navigator series is styled with the
  43468. * `.highcharts-navigator-series` class.
  43469. *
  43470. * @sample {highstock} stock/navigator/series-data/
  43471. * Using a separate data set for the navigator
  43472. * @sample {highstock} stock/navigator/series/
  43473. * A green navigator series
  43474. *
  43475. * @type {*|Array<*>|Highcharts.SeriesOptionsType|Array<Highcharts.SeriesOptionsType>}
  43476. */
  43477. series: {
  43478. /**
  43479. * The type of the navigator series. Defaults to `areaspline` if
  43480. * defined, otherwise `line`.
  43481. *
  43482. * Heads up:
  43483. * In column-type navigator, zooming is limited to at least one
  43484. * point with its `pointRange`.
  43485. *
  43486. * @sample {highstock} stock/navigator/column/
  43487. * Column type navigator
  43488. *
  43489. * @type {string}
  43490. * @default areaspline
  43491. */
  43492. type: defaultSeriesType,
  43493. /**
  43494. * The fill opacity of the navigator series.
  43495. */
  43496. fillOpacity: 0.05,
  43497. /**
  43498. * The pixel line width of the navigator series.
  43499. */
  43500. lineWidth: 1,
  43501. /**
  43502. * @ignore-option
  43503. */
  43504. compare: null,
  43505. /**
  43506. * Unless data is explicitly defined, the data is borrowed from the
  43507. * first series in the chart.
  43508. *
  43509. * @type {Array<number|Array<number|string|null>|object|null>}
  43510. * @product highstock
  43511. * @apioption navigator.series.data
  43512. */
  43513. /**
  43514. * Data grouping options for the navigator series.
  43515. *
  43516. * @extends plotOptions.series.dataGrouping
  43517. */
  43518. dataGrouping: {
  43519. approximation: 'average',
  43520. enabled: true,
  43521. groupPixelWidth: 2,
  43522. smoothed: true,
  43523. // Day and week differs from plotOptions.series.dataGrouping
  43524. units: [
  43525. ['millisecond', [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]],
  43526. ['second', [1, 2, 5, 10, 15, 30]],
  43527. ['minute', [1, 2, 5, 10, 15, 30]],
  43528. ['hour', [1, 2, 3, 4, 6, 8, 12]],
  43529. ['day', [1, 2, 3, 4]],
  43530. ['week', [1, 2, 3]],
  43531. ['month', [1, 3, 6]],
  43532. ['year', null]
  43533. ]
  43534. },
  43535. /**
  43536. * Data label options for the navigator series. Data labels are
  43537. * disabled by default on the navigator series.
  43538. *
  43539. * @extends plotOptions.series.dataLabels
  43540. */
  43541. dataLabels: {
  43542. enabled: false,
  43543. zIndex: 2 // #1839
  43544. },
  43545. id: 'highcharts-navigator-series',
  43546. className: 'highcharts-navigator-series',
  43547. /**
  43548. * Sets the fill color of the navigator series.
  43549. *
  43550. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  43551. * @apioption navigator.series.color
  43552. */
  43553. /**
  43554. * Line color for the navigator series. Allows setting the color
  43555. * while disallowing the default candlestick setting.
  43556. *
  43557. * @type {Highcharts.ColorString|null}
  43558. */
  43559. lineColor: null,
  43560. marker: {
  43561. enabled: false
  43562. },
  43563. /**
  43564. * Since Highstock v8, default value is the same as default
  43565. * `pointRange` defined for a specific type (e.g. `null` for
  43566. * column type).
  43567. *
  43568. * In Highstock version < 8, defaults to 0.
  43569. *
  43570. * @extends plotOptions.series.pointRange
  43571. * @type {number|null}
  43572. * @apioption navigator.series.pointRange
  43573. */
  43574. /**
  43575. * The threshold option. Setting it to 0 will make the default
  43576. * navigator area series draw its area from the 0 value and up.
  43577. *
  43578. * @type {number|null}
  43579. */
  43580. threshold: null
  43581. },
  43582. /**
  43583. * Options for the navigator X axis. Default series options for the
  43584. * navigator xAxis are:
  43585. * ```js
  43586. * xAxis: {
  43587. * tickWidth: 0,
  43588. * lineWidth: 0,
  43589. * gridLineWidth: 1,
  43590. * tickPixelInterval: 200,
  43591. * labels: {
  43592. * align: 'left',
  43593. * style: {
  43594. * color: '#888'
  43595. * },
  43596. * x: 3,
  43597. * y: -4
  43598. * }
  43599. * }
  43600. * ```
  43601. *
  43602. * @extends xAxis
  43603. * @excluding linkedTo, maxZoom, minRange, opposite, range, scrollbar,
  43604. * showEmpty, maxRange
  43605. */
  43606. xAxis: {
  43607. /**
  43608. * Additional range on the right side of the xAxis. Works similar to
  43609. * xAxis.maxPadding, but value is set in milliseconds.
  43610. * Can be set for both, main xAxis and navigator's xAxis.
  43611. *
  43612. * @since 6.0.0
  43613. */
  43614. overscroll: 0,
  43615. className: 'highcharts-navigator-xaxis',
  43616. tickLength: 0,
  43617. lineWidth: 0,
  43618. gridLineColor: '#e6e6e6',
  43619. gridLineWidth: 1,
  43620. tickPixelInterval: 200,
  43621. labels: {
  43622. align: 'left',
  43623. /**
  43624. * @type {Highcharts.CSSObject}
  43625. */
  43626. style: {
  43627. /** @ignore */
  43628. color: '#999999'
  43629. },
  43630. x: 3,
  43631. y: -4
  43632. },
  43633. crosshair: false
  43634. },
  43635. /**
  43636. * Options for the navigator Y axis. Default series options for the
  43637. * navigator yAxis are:
  43638. * ```js
  43639. * yAxis: {
  43640. * gridLineWidth: 0,
  43641. * startOnTick: false,
  43642. * endOnTick: false,
  43643. * minPadding: 0.1,
  43644. * maxPadding: 0.1,
  43645. * labels: {
  43646. * enabled: false
  43647. * },
  43648. * title: {
  43649. * text: null
  43650. * },
  43651. * tickWidth: 0
  43652. * }
  43653. * ```
  43654. *
  43655. * @extends yAxis
  43656. * @excluding height, linkedTo, maxZoom, minRange, ordinal, range,
  43657. * showEmpty, scrollbar, top, units, maxRange, minLength,
  43658. * maxLength, resize
  43659. */
  43660. yAxis: {
  43661. className: 'highcharts-navigator-yaxis',
  43662. gridLineWidth: 0,
  43663. startOnTick: false,
  43664. endOnTick: false,
  43665. minPadding: 0.1,
  43666. maxPadding: 0.1,
  43667. labels: {
  43668. enabled: false
  43669. },
  43670. crosshair: false,
  43671. title: {
  43672. text: null
  43673. },
  43674. tickLength: 0,
  43675. tickWidth: 0
  43676. }
  43677. }
  43678. });
  43679. /* eslint-disable no-invalid-this, valid-jsdoc */
  43680. /**
  43681. * Draw one of the handles on the side of the zoomed range in the navigator
  43682. *
  43683. * @private
  43684. * @function Highcharts.Renderer#symbols.navigator-handle
  43685. * @param {number} x
  43686. * @param {number} y
  43687. * @param {number} w
  43688. * @param {number} h
  43689. * @param {Highcharts.NavigatorHandlesOptions} options
  43690. * @return {Highcharts.SVGPathArray}
  43691. * Path to be used in a handle
  43692. */
  43693. H.Renderer.prototype.symbols['navigator-handle'] = function (x, y, w, h, options) {
  43694. var halfWidth = (options && options.width || 0) / 2, markerPosition = Math.round(halfWidth / 3) + 0.5, height = options && options.height || 0;
  43695. return [
  43696. ['M', -halfWidth - 1, 0.5],
  43697. ['L', halfWidth, 0.5],
  43698. ['L', halfWidth, height + 0.5],
  43699. ['L', -halfWidth - 1, height + 0.5],
  43700. ['L', -halfWidth - 1, 0.5],
  43701. ['M', -markerPosition, 4],
  43702. ['L', -markerPosition, height - 3],
  43703. ['M', markerPosition - 1, 4],
  43704. ['L', markerPosition - 1, height - 3]
  43705. ];
  43706. };
  43707. /**
  43708. * The Navigator class
  43709. *
  43710. * @private
  43711. * @class
  43712. * @name Highcharts.Navigator
  43713. *
  43714. * @param {Highcharts.Chart} chart
  43715. * Chart object
  43716. */
  43717. var Navigator = /** @class */ (function () {
  43718. function Navigator(chart) {
  43719. this.baseSeries = void 0;
  43720. this.chart = void 0;
  43721. this.handles = void 0;
  43722. this.height = void 0;
  43723. this.left = void 0;
  43724. this.navigatorEnabled = void 0;
  43725. this.navigatorGroup = void 0;
  43726. this.navigatorOptions = void 0;
  43727. this.navigatorSeries = void 0;
  43728. this.navigatorSize = void 0;
  43729. this.opposite = void 0;
  43730. this.outline = void 0;
  43731. this.outlineHeight = void 0;
  43732. this.range = void 0;
  43733. this.rendered = void 0;
  43734. this.shades = void 0;
  43735. this.size = void 0;
  43736. this.top = void 0;
  43737. this.xAxis = void 0;
  43738. this.yAxis = void 0;
  43739. this.zoomedMax = void 0;
  43740. this.zoomedMin = void 0;
  43741. this.init(chart);
  43742. }
  43743. /**
  43744. * Draw one of the handles on the side of the zoomed range in the navigator
  43745. *
  43746. * @private
  43747. * @function Highcharts.Navigator#drawHandle
  43748. *
  43749. * @param {number} x
  43750. * The x center for the handle
  43751. *
  43752. * @param {number} index
  43753. * 0 for left and 1 for right
  43754. *
  43755. * @param {boolean|undefined} inverted
  43756. * flag for chart.inverted
  43757. *
  43758. * @param {string} verb
  43759. * use 'animate' or 'attr'
  43760. */
  43761. Navigator.prototype.drawHandle = function (x, index, inverted, verb) {
  43762. var navigator = this, height = navigator.navigatorOptions.handles.height;
  43763. // Place it
  43764. navigator.handles[index][verb](inverted ? {
  43765. translateX: Math.round(navigator.left + navigator.height / 2),
  43766. translateY: Math.round(navigator.top + parseInt(x, 10) + 0.5 - height)
  43767. } : {
  43768. translateX: Math.round(navigator.left + parseInt(x, 10)),
  43769. translateY: Math.round(navigator.top + navigator.height / 2 - height / 2 - 1)
  43770. });
  43771. };
  43772. /**
  43773. * Render outline around the zoomed range
  43774. *
  43775. * @private
  43776. * @function Highcharts.Navigator#drawOutline
  43777. *
  43778. * @param {number} zoomedMin
  43779. * in pixels position where zoomed range starts
  43780. *
  43781. * @param {number} zoomedMax
  43782. * in pixels position where zoomed range ends
  43783. *
  43784. * @param {boolean|undefined} inverted
  43785. * flag if chart is inverted
  43786. *
  43787. * @param {string} verb
  43788. * use 'animate' or 'attr'
  43789. */
  43790. Navigator.prototype.drawOutline = function (zoomedMin, zoomedMax, inverted, verb) {
  43791. var navigator = this, maskInside = navigator.navigatorOptions.maskInside, outlineWidth = navigator.outline.strokeWidth(), halfOutline = outlineWidth / 2, outlineCorrection = (outlineWidth % 2) / 2, // #5800
  43792. outlineHeight = navigator.outlineHeight, scrollbarHeight = navigator.scrollbarHeight || 0, navigatorSize = navigator.size, left = navigator.left - scrollbarHeight, navigatorTop = navigator.top, verticalMin, path;
  43793. if (inverted) {
  43794. left -= halfOutline;
  43795. verticalMin = navigatorTop + zoomedMax + outlineCorrection;
  43796. zoomedMax = navigatorTop + zoomedMin + outlineCorrection;
  43797. path = [
  43798. ['M', left + outlineHeight, navigatorTop - scrollbarHeight - outlineCorrection],
  43799. ['L', left + outlineHeight, verticalMin],
  43800. ['L', left, verticalMin],
  43801. ['L', left, zoomedMax],
  43802. ['L', left + outlineHeight, zoomedMax],
  43803. ['L', left + outlineHeight, navigatorTop + navigatorSize + scrollbarHeight]
  43804. ];
  43805. if (maskInside) {
  43806. path.push(['M', left + outlineHeight, verticalMin - halfOutline], // upper left of zoomed range
  43807. ['L', left + outlineHeight, zoomedMax + halfOutline] // upper right of z.r.
  43808. );
  43809. }
  43810. }
  43811. else {
  43812. zoomedMin += left + scrollbarHeight - outlineCorrection;
  43813. zoomedMax += left + scrollbarHeight - outlineCorrection;
  43814. navigatorTop += halfOutline;
  43815. path = [
  43816. ['M', left, navigatorTop],
  43817. ['L', zoomedMin, navigatorTop],
  43818. ['L', zoomedMin, navigatorTop + outlineHeight],
  43819. ['L', zoomedMax, navigatorTop + outlineHeight],
  43820. ['L', zoomedMax, navigatorTop],
  43821. ['L', left + navigatorSize + scrollbarHeight * 2, navigatorTop] // right
  43822. ];
  43823. if (maskInside) {
  43824. path.push(['M', zoomedMin - halfOutline, navigatorTop], // upper left of zoomed range
  43825. ['L', zoomedMax + halfOutline, navigatorTop] // upper right of z.r.
  43826. );
  43827. }
  43828. }
  43829. navigator.outline[verb]({
  43830. d: path
  43831. });
  43832. };
  43833. /**
  43834. * Render outline around the zoomed range
  43835. *
  43836. * @private
  43837. * @function Highcharts.Navigator#drawMasks
  43838. *
  43839. * @param {number} zoomedMin
  43840. * in pixels position where zoomed range starts
  43841. *
  43842. * @param {number} zoomedMax
  43843. * in pixels position where zoomed range ends
  43844. *
  43845. * @param {boolean|undefined} inverted
  43846. * flag if chart is inverted
  43847. *
  43848. * @param {string} verb
  43849. * use 'animate' or 'attr'
  43850. */
  43851. Navigator.prototype.drawMasks = function (zoomedMin, zoomedMax, inverted, verb) {
  43852. var navigator = this, left = navigator.left, top = navigator.top, navigatorHeight = navigator.height, height, width, x, y;
  43853. // Determine rectangle position & size
  43854. // According to (non)inverted position:
  43855. if (inverted) {
  43856. x = [left, left, left];
  43857. y = [top, top + zoomedMin, top + zoomedMax];
  43858. width = [navigatorHeight, navigatorHeight, navigatorHeight];
  43859. height = [
  43860. zoomedMin,
  43861. zoomedMax - zoomedMin,
  43862. navigator.size - zoomedMax
  43863. ];
  43864. }
  43865. else {
  43866. x = [left, left + zoomedMin, left + zoomedMax];
  43867. y = [top, top, top];
  43868. width = [
  43869. zoomedMin,
  43870. zoomedMax - zoomedMin,
  43871. navigator.size - zoomedMax
  43872. ];
  43873. height = [navigatorHeight, navigatorHeight, navigatorHeight];
  43874. }
  43875. navigator.shades.forEach(function (shade, i) {
  43876. shade[verb]({
  43877. x: x[i],
  43878. y: y[i],
  43879. width: width[i],
  43880. height: height[i]
  43881. });
  43882. });
  43883. };
  43884. /**
  43885. * Generate DOM elements for a navigator:
  43886. *
  43887. * - main navigator group
  43888. *
  43889. * - all shades
  43890. *
  43891. * - outline
  43892. *
  43893. * - handles
  43894. *
  43895. * @private
  43896. * @function Highcharts.Navigator#renderElements
  43897. */
  43898. Navigator.prototype.renderElements = function () {
  43899. var navigator = this, navigatorOptions = navigator.navigatorOptions, maskInside = navigatorOptions.maskInside, chart = navigator.chart, inverted = chart.inverted, renderer = chart.renderer, navigatorGroup, mouseCursor = {
  43900. cursor: inverted ? 'ns-resize' : 'ew-resize'
  43901. };
  43902. // Create the main navigator group
  43903. navigator.navigatorGroup = navigatorGroup = renderer.g('navigator')
  43904. .attr({
  43905. zIndex: 8,
  43906. visibility: 'hidden'
  43907. })
  43908. .add();
  43909. // Create masks, each mask will get events and fill:
  43910. [
  43911. !maskInside,
  43912. maskInside,
  43913. !maskInside
  43914. ].forEach(function (hasMask, index) {
  43915. navigator.shades[index] = renderer.rect()
  43916. .addClass('highcharts-navigator-mask' +
  43917. (index === 1 ? '-inside' : '-outside'))
  43918. .add(navigatorGroup);
  43919. if (!chart.styledMode) {
  43920. navigator.shades[index]
  43921. .attr({
  43922. fill: hasMask ?
  43923. navigatorOptions.maskFill :
  43924. 'rgba(0,0,0,0)'
  43925. })
  43926. .css((index === 1) && mouseCursor);
  43927. }
  43928. });
  43929. // Create the outline:
  43930. navigator.outline = renderer.path()
  43931. .addClass('highcharts-navigator-outline')
  43932. .add(navigatorGroup);
  43933. if (!chart.styledMode) {
  43934. navigator.outline.attr({
  43935. 'stroke-width': navigatorOptions.outlineWidth,
  43936. stroke: navigatorOptions.outlineColor
  43937. });
  43938. }
  43939. // Create the handlers:
  43940. if (navigatorOptions.handles.enabled) {
  43941. [0, 1].forEach(function (index) {
  43942. navigatorOptions.handles.inverted = chart.inverted;
  43943. navigator.handles[index] = renderer.symbol(navigatorOptions.handles.symbols[index], -navigatorOptions.handles.width / 2 - 1, 0, navigatorOptions.handles.width, navigatorOptions.handles.height, navigatorOptions.handles);
  43944. // zIndex = 6 for right handle, 7 for left.
  43945. // Can't be 10, because of the tooltip in inverted chart #2908
  43946. navigator.handles[index].attr({ zIndex: 7 - index })
  43947. .addClass('highcharts-navigator-handle ' +
  43948. 'highcharts-navigator-handle-' +
  43949. ['left', 'right'][index]).add(navigatorGroup);
  43950. if (!chart.styledMode) {
  43951. var handlesOptions = navigatorOptions.handles;
  43952. navigator.handles[index]
  43953. .attr({
  43954. fill: handlesOptions.backgroundColor,
  43955. stroke: handlesOptions.borderColor,
  43956. 'stroke-width': handlesOptions.lineWidth
  43957. })
  43958. .css(mouseCursor);
  43959. }
  43960. });
  43961. }
  43962. };
  43963. /**
  43964. * Update navigator
  43965. *
  43966. * @private
  43967. * @function Highcharts.Navigator#update
  43968. *
  43969. * @param {Highcharts.NavigatorOptions} options
  43970. * Options to merge in when updating navigator
  43971. */
  43972. Navigator.prototype.update = function (options) {
  43973. // Remove references to old navigator series in base series
  43974. (this.series || []).forEach(function (series) {
  43975. if (series.baseSeries) {
  43976. delete series.baseSeries.navigatorSeries;
  43977. }
  43978. });
  43979. // Destroy and rebuild navigator
  43980. this.destroy();
  43981. var chartOptions = this.chart.options;
  43982. merge(true, chartOptions.navigator, this.options, options);
  43983. this.init(this.chart);
  43984. };
  43985. /**
  43986. * Render the navigator
  43987. *
  43988. * @private
  43989. * @function Highcharts.Navigator#render
  43990. * @param {number} min
  43991. * X axis value minimum
  43992. * @param {number} max
  43993. * X axis value maximum
  43994. * @param {number} [pxMin]
  43995. * Pixel value minimum
  43996. * @param {number} [pxMax]
  43997. * Pixel value maximum
  43998. * @return {void}
  43999. */
  44000. Navigator.prototype.render = function (min, max, pxMin, pxMax) {
  44001. var navigator = this, chart = navigator.chart, navigatorWidth, scrollbarLeft, scrollbarTop, scrollbarHeight = navigator.scrollbarHeight, navigatorSize, xAxis = navigator.xAxis, pointRange = xAxis.pointRange || 0, scrollbarXAxis = xAxis.navigatorAxis.fake ? chart.xAxis[0] : xAxis, navigatorEnabled = navigator.navigatorEnabled, zoomedMin, zoomedMax, rendered = navigator.rendered, inverted = chart.inverted, verb, newMin, newMax, currentRange, minRange = chart.xAxis[0].minRange, maxRange = chart.xAxis[0].options.maxRange;
  44002. // Don't redraw while moving the handles (#4703).
  44003. if (this.hasDragged && !defined(pxMin)) {
  44004. return;
  44005. }
  44006. min = correctFloat(min - pointRange / 2);
  44007. max = correctFloat(max + pointRange / 2);
  44008. // Don't render the navigator until we have data (#486, #4202, #5172).
  44009. if (!isNumber(min) || !isNumber(max)) {
  44010. // However, if navigator was already rendered, we may need to resize
  44011. // it. For example hidden series, but visible navigator (#6022).
  44012. if (rendered) {
  44013. pxMin = 0;
  44014. pxMax = pick(xAxis.width, scrollbarXAxis.width);
  44015. }
  44016. else {
  44017. return;
  44018. }
  44019. }
  44020. navigator.left = pick(xAxis.left,
  44021. // in case of scrollbar only, without navigator
  44022. chart.plotLeft + scrollbarHeight +
  44023. (inverted ? chart.plotWidth : 0));
  44024. navigator.size = zoomedMax = navigatorSize = pick(xAxis.len, (inverted ? chart.plotHeight : chart.plotWidth) -
  44025. 2 * scrollbarHeight);
  44026. if (inverted) {
  44027. navigatorWidth = scrollbarHeight;
  44028. }
  44029. else {
  44030. navigatorWidth = navigatorSize + 2 * scrollbarHeight;
  44031. }
  44032. // Get the pixel position of the handles
  44033. pxMin = pick(pxMin, xAxis.toPixels(min, true));
  44034. pxMax = pick(pxMax, xAxis.toPixels(max, true));
  44035. // Verify (#1851, #2238)
  44036. if (!isNumber(pxMin) || Math.abs(pxMin) === Infinity) {
  44037. pxMin = 0;
  44038. pxMax = navigatorWidth;
  44039. }
  44040. // Are we below the minRange? (#2618, #6191)
  44041. newMin = xAxis.toValue(pxMin, true);
  44042. newMax = xAxis.toValue(pxMax, true);
  44043. currentRange = Math.abs(correctFloat(newMax - newMin));
  44044. if (currentRange < minRange) {
  44045. if (this.grabbedLeft) {
  44046. pxMin = xAxis.toPixels(newMax - minRange - pointRange, true);
  44047. }
  44048. else if (this.grabbedRight) {
  44049. pxMax = xAxis.toPixels(newMin + minRange + pointRange, true);
  44050. }
  44051. }
  44052. else if (defined(maxRange) &&
  44053. correctFloat(currentRange - pointRange) > maxRange) {
  44054. if (this.grabbedLeft) {
  44055. pxMin = xAxis.toPixels(newMax - maxRange - pointRange, true);
  44056. }
  44057. else if (this.grabbedRight) {
  44058. pxMax = xAxis.toPixels(newMin + maxRange + pointRange, true);
  44059. }
  44060. }
  44061. // Handles are allowed to cross, but never exceed the plot area
  44062. navigator.zoomedMax = clamp(Math.max(pxMin, pxMax), 0, zoomedMax);
  44063. navigator.zoomedMin = clamp(navigator.fixedWidth ?
  44064. navigator.zoomedMax - navigator.fixedWidth :
  44065. Math.min(pxMin, pxMax), 0, zoomedMax);
  44066. navigator.range = navigator.zoomedMax - navigator.zoomedMin;
  44067. zoomedMax = Math.round(navigator.zoomedMax);
  44068. zoomedMin = Math.round(navigator.zoomedMin);
  44069. if (navigatorEnabled) {
  44070. navigator.navigatorGroup.attr({
  44071. visibility: 'visible'
  44072. });
  44073. // Place elements
  44074. verb = rendered && !navigator.hasDragged ? 'animate' : 'attr';
  44075. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  44076. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  44077. if (navigator.navigatorOptions.handles.enabled) {
  44078. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  44079. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  44080. }
  44081. }
  44082. if (navigator.scrollbar) {
  44083. if (inverted) {
  44084. scrollbarTop = navigator.top - scrollbarHeight;
  44085. scrollbarLeft = navigator.left - scrollbarHeight +
  44086. (navigatorEnabled || !scrollbarXAxis.opposite ? 0 :
  44087. // Multiple axes has offsets:
  44088. (scrollbarXAxis.titleOffset || 0) +
  44089. // Self margin from the axis.title
  44090. scrollbarXAxis.axisTitleMargin);
  44091. scrollbarHeight = navigatorSize + 2 * scrollbarHeight;
  44092. }
  44093. else {
  44094. scrollbarTop = navigator.top + (navigatorEnabled ?
  44095. navigator.height :
  44096. -scrollbarHeight);
  44097. scrollbarLeft = navigator.left - scrollbarHeight;
  44098. }
  44099. // Reposition scrollbar
  44100. navigator.scrollbar.position(scrollbarLeft, scrollbarTop, navigatorWidth, scrollbarHeight);
  44101. // Keep scale 0-1
  44102. navigator.scrollbar.setRange(
  44103. // Use real value, not rounded because range can be very small
  44104. // (#1716)
  44105. navigator.zoomedMin / (navigatorSize || 1), navigator.zoomedMax / (navigatorSize || 1));
  44106. }
  44107. navigator.rendered = true;
  44108. };
  44109. /**
  44110. * Set up the mouse and touch events for the navigator
  44111. *
  44112. * @private
  44113. * @function Highcharts.Navigator#addMouseEvents
  44114. */
  44115. Navigator.prototype.addMouseEvents = function () {
  44116. var navigator = this, chart = navigator.chart, container = chart.container, eventsToUnbind = [], mouseMoveHandler, mouseUpHandler;
  44117. /**
  44118. * Create mouse events' handlers.
  44119. * Make them as separate functions to enable wrapping them:
  44120. */
  44121. navigator.mouseMoveHandler = mouseMoveHandler = function (e) {
  44122. navigator.onMouseMove(e);
  44123. };
  44124. navigator.mouseUpHandler = mouseUpHandler = function (e) {
  44125. navigator.onMouseUp(e);
  44126. };
  44127. // Add shades and handles mousedown events
  44128. eventsToUnbind = navigator.getPartsEvents('mousedown');
  44129. // Add mouse move and mouseup events. These are bind to doc/container,
  44130. // because Navigator.grabbedSomething flags are stored in mousedown
  44131. // events
  44132. eventsToUnbind.push(addEvent(chart.renderTo, 'mousemove', mouseMoveHandler), addEvent(container.ownerDocument, 'mouseup', mouseUpHandler));
  44133. // Touch events
  44134. if (hasTouch) {
  44135. eventsToUnbind.push(addEvent(chart.renderTo, 'touchmove', mouseMoveHandler), addEvent(container.ownerDocument, 'touchend', mouseUpHandler));
  44136. eventsToUnbind.concat(navigator.getPartsEvents('touchstart'));
  44137. }
  44138. navigator.eventsToUnbind = eventsToUnbind;
  44139. // Data events
  44140. if (navigator.series && navigator.series[0]) {
  44141. eventsToUnbind.push(addEvent(navigator.series[0].xAxis, 'foundExtremes', function () {
  44142. chart.navigator.modifyNavigatorAxisExtremes();
  44143. }));
  44144. }
  44145. };
  44146. /**
  44147. * Generate events for handles and masks
  44148. *
  44149. * @private
  44150. * @function Highcharts.Navigator#getPartsEvents
  44151. *
  44152. * @param {string} eventName
  44153. * Event name handler, 'mousedown' or 'touchstart'
  44154. *
  44155. * @return {Array<Function>}
  44156. * An array of functions to remove navigator functions from the
  44157. * events again.
  44158. */
  44159. Navigator.prototype.getPartsEvents = function (eventName) {
  44160. var navigator = this, events = [];
  44161. ['shades', 'handles'].forEach(function (name) {
  44162. navigator[name].forEach(function (navigatorItem, index) {
  44163. events.push(addEvent(navigatorItem.element, eventName, function (e) {
  44164. navigator[name + 'Mousedown'](e, index);
  44165. }));
  44166. });
  44167. });
  44168. return events;
  44169. };
  44170. /**
  44171. * Mousedown on a shaded mask, either:
  44172. *
  44173. * - will be stored for future drag&drop
  44174. *
  44175. * - will directly shift to a new range
  44176. *
  44177. * @private
  44178. * @function Highcharts.Navigator#shadesMousedown
  44179. *
  44180. * @param {Highcharts.PointerEventObject} e
  44181. * Mouse event
  44182. *
  44183. * @param {number} index
  44184. * Index of a mask in Navigator.shades array
  44185. */
  44186. Navigator.prototype.shadesMousedown = function (e, index) {
  44187. e = this.chart.pointer.normalize(e);
  44188. var navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, zoomedMin = navigator.zoomedMin, navigatorPosition = navigator.left, navigatorSize = navigator.size, range = navigator.range, chartX = e.chartX, fixedMax, fixedMin, ext, left;
  44189. // For inverted chart, swap some options:
  44190. if (chart.inverted) {
  44191. chartX = e.chartY;
  44192. navigatorPosition = navigator.top;
  44193. }
  44194. if (index === 1) {
  44195. // Store information for drag&drop
  44196. navigator.grabbedCenter = chartX;
  44197. navigator.fixedWidth = range;
  44198. navigator.dragOffset = chartX - zoomedMin;
  44199. }
  44200. else {
  44201. // Shift the range by clicking on shaded areas
  44202. left = chartX - navigatorPosition - range / 2;
  44203. if (index === 0) {
  44204. left = Math.max(0, left);
  44205. }
  44206. else if (index === 2 && left + range >= navigatorSize) {
  44207. left = navigatorSize - range;
  44208. if (navigator.reversedExtremes) {
  44209. // #7713
  44210. left -= range;
  44211. fixedMin = navigator.getUnionExtremes().dataMin;
  44212. }
  44213. else {
  44214. // #2293, #3543
  44215. fixedMax = navigator.getUnionExtremes().dataMax;
  44216. }
  44217. }
  44218. if (left !== zoomedMin) { // it has actually moved
  44219. navigator.fixedWidth = range; // #1370
  44220. ext = xAxis.navigatorAxis.toFixedRange(left, left + range, fixedMin, fixedMax);
  44221. if (defined(ext.min)) { // #7411
  44222. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true, null, // auto animation
  44223. { trigger: 'navigator' });
  44224. }
  44225. }
  44226. }
  44227. };
  44228. /**
  44229. * Mousedown on a handle mask.
  44230. * Will store necessary information for drag&drop.
  44231. *
  44232. * @private
  44233. * @function Highcharts.Navigator#handlesMousedown
  44234. * @param {Highcharts.PointerEventObject} e
  44235. * Mouse event
  44236. * @param {number} index
  44237. * Index of a handle in Navigator.handles array
  44238. * @return {void}
  44239. */
  44240. Navigator.prototype.handlesMousedown = function (e, index) {
  44241. e = this.chart.pointer.normalize(e);
  44242. var navigator = this, chart = navigator.chart, baseXAxis = chart.xAxis[0],
  44243. // For reversed axes, min and max are changed,
  44244. // so the other extreme should be stored
  44245. reverse = navigator.reversedExtremes;
  44246. if (index === 0) {
  44247. // Grab the left handle
  44248. navigator.grabbedLeft = true;
  44249. navigator.otherHandlePos = navigator.zoomedMax;
  44250. navigator.fixedExtreme = reverse ? baseXAxis.min : baseXAxis.max;
  44251. }
  44252. else {
  44253. // Grab the right handle
  44254. navigator.grabbedRight = true;
  44255. navigator.otherHandlePos = navigator.zoomedMin;
  44256. navigator.fixedExtreme = reverse ? baseXAxis.max : baseXAxis.min;
  44257. }
  44258. chart.fixedRange = null;
  44259. };
  44260. /**
  44261. * Mouse move event based on x/y mouse position.
  44262. *
  44263. * @private
  44264. * @function Highcharts.Navigator#onMouseMove
  44265. *
  44266. * @param {Highcharts.PointerEventObject} e
  44267. * Mouse event
  44268. */
  44269. Navigator.prototype.onMouseMove = function (e) {
  44270. var navigator = this, chart = navigator.chart, left = navigator.left, navigatorSize = navigator.navigatorSize, range = navigator.range, dragOffset = navigator.dragOffset, inverted = chart.inverted, chartX;
  44271. // In iOS, a mousemove event with e.pageX === 0 is fired when holding
  44272. // the finger down in the center of the scrollbar. This should be
  44273. // ignored.
  44274. if (!e.touches || e.touches[0].pageX !== 0) { // #4696
  44275. e = chart.pointer.normalize(e);
  44276. chartX = e.chartX;
  44277. // Swap some options for inverted chart
  44278. if (inverted) {
  44279. left = navigator.top;
  44280. chartX = e.chartY;
  44281. }
  44282. // Drag left handle or top handle
  44283. if (navigator.grabbedLeft) {
  44284. navigator.hasDragged = true;
  44285. navigator.render(0, 0, chartX - left, navigator.otherHandlePos);
  44286. // Drag right handle or bottom handle
  44287. }
  44288. else if (navigator.grabbedRight) {
  44289. navigator.hasDragged = true;
  44290. navigator.render(0, 0, navigator.otherHandlePos, chartX - left);
  44291. // Drag scrollbar or open area in navigator
  44292. }
  44293. else if (navigator.grabbedCenter) {
  44294. navigator.hasDragged = true;
  44295. if (chartX < dragOffset) { // outside left
  44296. chartX = dragOffset;
  44297. // outside right
  44298. }
  44299. else if (chartX >
  44300. navigatorSize + dragOffset - range) {
  44301. chartX = navigatorSize + dragOffset - range;
  44302. }
  44303. navigator.render(0, 0, chartX - dragOffset, chartX - dragOffset + range);
  44304. }
  44305. if (navigator.hasDragged &&
  44306. navigator.scrollbar &&
  44307. pick(navigator.scrollbar.options.liveRedraw,
  44308. // By default, don't run live redraw on VML, on touch
  44309. // devices or if the chart is in boost.
  44310. H.svg && !isTouchDevice && !this.chart.isBoosting)) {
  44311. e.DOMType = e.type; // DOMType is for IE8
  44312. setTimeout(function () {
  44313. navigator.onMouseUp(e);
  44314. }, 0);
  44315. }
  44316. }
  44317. };
  44318. /**
  44319. * Mouse up event based on x/y mouse position.
  44320. *
  44321. * @private
  44322. * @function Highcharts.Navigator#onMouseUp
  44323. * @param {Highcharts.PointerEventObject} e
  44324. * Mouse event
  44325. * @return {void}
  44326. */
  44327. Navigator.prototype.onMouseUp = function (e) {
  44328. var navigator = this, chart = navigator.chart, xAxis = navigator.xAxis, scrollbar = navigator.scrollbar, DOMEvent = e.DOMEvent || e, inverted = chart.inverted, verb = navigator.rendered && !navigator.hasDragged ?
  44329. 'animate' : 'attr', zoomedMax = Math.round(navigator.zoomedMax), zoomedMin = Math.round(navigator.zoomedMin), unionExtremes, fixedMin, fixedMax, ext;
  44330. if (
  44331. // MouseUp is called for both, navigator and scrollbar (that order),
  44332. // which causes calling afterSetExtremes twice. Prevent first call
  44333. // by checking if scrollbar is going to set new extremes (#6334)
  44334. (navigator.hasDragged && (!scrollbar || !scrollbar.hasDragged)) ||
  44335. e.trigger === 'scrollbar') {
  44336. unionExtremes = navigator.getUnionExtremes();
  44337. // When dragging one handle, make sure the other one doesn't change
  44338. if (navigator.zoomedMin === navigator.otherHandlePos) {
  44339. fixedMin = navigator.fixedExtreme;
  44340. }
  44341. else if (navigator.zoomedMax === navigator.otherHandlePos) {
  44342. fixedMax = navigator.fixedExtreme;
  44343. }
  44344. // Snap to right edge (#4076)
  44345. if (navigator.zoomedMax === navigator.size) {
  44346. fixedMax = navigator.reversedExtremes ?
  44347. unionExtremes.dataMin :
  44348. unionExtremes.dataMax;
  44349. }
  44350. // Snap to left edge (#7576)
  44351. if (navigator.zoomedMin === 0) {
  44352. fixedMin = navigator.reversedExtremes ?
  44353. unionExtremes.dataMax :
  44354. unionExtremes.dataMin;
  44355. }
  44356. ext = xAxis.navigatorAxis.toFixedRange(navigator.zoomedMin, navigator.zoomedMax, fixedMin, fixedMax);
  44357. if (defined(ext.min)) {
  44358. chart.xAxis[0].setExtremes(Math.min(ext.min, ext.max), Math.max(ext.min, ext.max), true,
  44359. // Run animation when clicking buttons, scrollbar track etc,
  44360. // but not when dragging handles or scrollbar
  44361. navigator.hasDragged ? false : null, {
  44362. trigger: 'navigator',
  44363. triggerOp: 'navigator-drag',
  44364. DOMEvent: DOMEvent // #1838
  44365. });
  44366. }
  44367. }
  44368. if (e.DOMType !== 'mousemove' &&
  44369. e.DOMType !== 'touchmove') {
  44370. navigator.grabbedLeft = navigator.grabbedRight =
  44371. navigator.grabbedCenter = navigator.fixedWidth =
  44372. navigator.fixedExtreme = navigator.otherHandlePos =
  44373. navigator.hasDragged = navigator.dragOffset = null;
  44374. }
  44375. // Update position of navigator shades, outline and handles (#12573)
  44376. if (navigator.navigatorEnabled) {
  44377. if (navigator.shades) {
  44378. navigator.drawMasks(zoomedMin, zoomedMax, inverted, verb);
  44379. }
  44380. if (navigator.outline) {
  44381. navigator.drawOutline(zoomedMin, zoomedMax, inverted, verb);
  44382. }
  44383. if (navigator.navigatorOptions.handles.enabled &&
  44384. Object.keys(navigator.handles).length ===
  44385. navigator.handles.length) {
  44386. navigator.drawHandle(zoomedMin, 0, inverted, verb);
  44387. navigator.drawHandle(zoomedMax, 1, inverted, verb);
  44388. }
  44389. }
  44390. };
  44391. /**
  44392. * Removes the event handlers attached previously with addEvents.
  44393. *
  44394. * @private
  44395. * @function Highcharts.Navigator#removeEvents
  44396. * @return {void}
  44397. */
  44398. Navigator.prototype.removeEvents = function () {
  44399. if (this.eventsToUnbind) {
  44400. this.eventsToUnbind.forEach(function (unbind) {
  44401. unbind();
  44402. });
  44403. this.eventsToUnbind = void 0;
  44404. }
  44405. this.removeBaseSeriesEvents();
  44406. };
  44407. /**
  44408. * Remove data events.
  44409. *
  44410. * @private
  44411. * @function Highcharts.Navigator#removeBaseSeriesEvents
  44412. * @return {void}
  44413. */
  44414. Navigator.prototype.removeBaseSeriesEvents = function () {
  44415. var baseSeries = this.baseSeries || [];
  44416. if (this.navigatorEnabled && baseSeries[0]) {
  44417. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  44418. baseSeries.forEach(function (series) {
  44419. removeEvent(series, 'updatedData', this.updatedDataHandler);
  44420. }, this);
  44421. }
  44422. // We only listen for extremes-events on the first baseSeries
  44423. if (baseSeries[0].xAxis) {
  44424. removeEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  44425. }
  44426. }
  44427. };
  44428. /**
  44429. * Initialize the Navigator object
  44430. *
  44431. * @private
  44432. * @function Highcharts.Navigator#init
  44433. *
  44434. * @param {Highcharts.Chart} chart
  44435. */
  44436. Navigator.prototype.init = function (chart) {
  44437. var chartOptions = chart.options, navigatorOptions = chartOptions.navigator, navigatorEnabled = navigatorOptions.enabled, scrollbarOptions = chartOptions.scrollbar, scrollbarEnabled = scrollbarOptions.enabled, height = navigatorEnabled ? navigatorOptions.height : 0, scrollbarHeight = scrollbarEnabled ?
  44438. scrollbarOptions.height :
  44439. 0;
  44440. this.handles = [];
  44441. this.shades = [];
  44442. this.chart = chart;
  44443. this.setBaseSeries();
  44444. this.height = height;
  44445. this.scrollbarHeight = scrollbarHeight;
  44446. this.scrollbarEnabled = scrollbarEnabled;
  44447. this.navigatorEnabled = navigatorEnabled;
  44448. this.navigatorOptions = navigatorOptions;
  44449. this.scrollbarOptions = scrollbarOptions;
  44450. this.outlineHeight = height + scrollbarHeight;
  44451. this.opposite = pick(navigatorOptions.opposite, Boolean(!navigatorEnabled && chart.inverted)); // #6262
  44452. var navigator = this, baseSeries = navigator.baseSeries, xAxisIndex = chart.xAxis.length, yAxisIndex = chart.yAxis.length, baseXaxis = baseSeries && baseSeries[0] && baseSeries[0].xAxis ||
  44453. chart.xAxis[0] || { options: {} };
  44454. chart.isDirtyBox = true;
  44455. if (navigator.navigatorEnabled) {
  44456. // an x axis is required for scrollbar also
  44457. navigator.xAxis = new Axis(chart, merge({
  44458. // inherit base xAxis' break and ordinal options
  44459. breaks: baseXaxis.options.breaks,
  44460. ordinal: baseXaxis.options.ordinal
  44461. }, navigatorOptions.xAxis, {
  44462. id: 'navigator-x-axis',
  44463. yAxis: 'navigator-y-axis',
  44464. isX: true,
  44465. type: 'datetime',
  44466. index: xAxisIndex,
  44467. isInternal: true,
  44468. offset: 0,
  44469. keepOrdinalPadding: true,
  44470. startOnTick: false,
  44471. endOnTick: false,
  44472. minPadding: 0,
  44473. maxPadding: 0,
  44474. zoomEnabled: false
  44475. }, chart.inverted ? {
  44476. offsets: [scrollbarHeight, 0, -scrollbarHeight, 0],
  44477. width: height
  44478. } : {
  44479. offsets: [0, -scrollbarHeight, 0, scrollbarHeight],
  44480. height: height
  44481. }));
  44482. navigator.yAxis = new Axis(chart, merge(navigatorOptions.yAxis, {
  44483. id: 'navigator-y-axis',
  44484. alignTicks: false,
  44485. offset: 0,
  44486. index: yAxisIndex,
  44487. isInternal: true,
  44488. zoomEnabled: false
  44489. }, chart.inverted ? {
  44490. width: height
  44491. } : {
  44492. height: height
  44493. }));
  44494. // If we have a base series, initialize the navigator series
  44495. if (baseSeries || navigatorOptions.series.data) {
  44496. navigator.updateNavigatorSeries(false);
  44497. // If not, set up an event to listen for added series
  44498. }
  44499. else if (chart.series.length === 0) {
  44500. navigator.unbindRedraw = addEvent(chart, 'beforeRedraw', function () {
  44501. // We've got one, now add it as base
  44502. if (chart.series.length > 0 && !navigator.series) {
  44503. navigator.setBaseSeries();
  44504. navigator.unbindRedraw(); // reset
  44505. }
  44506. });
  44507. }
  44508. navigator.reversedExtremes = (chart.inverted && !navigator.xAxis.reversed) || (!chart.inverted && navigator.xAxis.reversed);
  44509. // Render items, so we can bind events to them:
  44510. navigator.renderElements();
  44511. // Add mouse events
  44512. navigator.addMouseEvents();
  44513. // in case of scrollbar only, fake an x axis to get translation
  44514. }
  44515. else {
  44516. navigator.xAxis = {
  44517. chart: chart,
  44518. navigatorAxis: {
  44519. fake: true
  44520. },
  44521. translate: function (value, reverse) {
  44522. var axis = chart.xAxis[0], ext = axis.getExtremes(), scrollTrackWidth = axis.len - 2 * scrollbarHeight, min = numExt('min', axis.options.min, ext.dataMin), valueRange = numExt('max', axis.options.max, ext.dataMax) - min;
  44523. return reverse ?
  44524. // from pixel to value
  44525. (value * valueRange / scrollTrackWidth) + min :
  44526. // from value to pixel
  44527. scrollTrackWidth * (value - min) / valueRange;
  44528. },
  44529. toPixels: function (value) {
  44530. return this.translate(value);
  44531. },
  44532. toValue: function (value) {
  44533. return this.translate(value, true);
  44534. }
  44535. };
  44536. navigator.xAxis.navigatorAxis.axis = navigator.xAxis;
  44537. navigator.xAxis.navigatorAxis.toFixedRange = (NavigatorAxis.AdditionsClass.prototype.toFixedRange.bind(navigator.xAxis.navigatorAxis));
  44538. }
  44539. // Initialize the scrollbar
  44540. if (chart.options.scrollbar.enabled) {
  44541. chart.scrollbar = navigator.scrollbar = new Scrollbar(chart.renderer, merge(chart.options.scrollbar, {
  44542. margin: navigator.navigatorEnabled ? 0 : 10,
  44543. vertical: chart.inverted
  44544. }), chart);
  44545. addEvent(navigator.scrollbar, 'changed', function (e) {
  44546. var range = navigator.size, to = range * this.to, from = range * this.from;
  44547. navigator.hasDragged = navigator.scrollbar.hasDragged;
  44548. navigator.render(0, 0, from, to);
  44549. if (chart.options.scrollbar.liveRedraw ||
  44550. (e.DOMType !== 'mousemove' &&
  44551. e.DOMType !== 'touchmove')) {
  44552. setTimeout(function () {
  44553. navigator.onMouseUp(e);
  44554. });
  44555. }
  44556. });
  44557. }
  44558. // Add data events
  44559. navigator.addBaseSeriesEvents();
  44560. // Add redraw events
  44561. navigator.addChartEvents();
  44562. };
  44563. /**
  44564. * Get the union data extremes of the chart - the outer data extremes of the
  44565. * base X axis and the navigator axis.
  44566. *
  44567. * @private
  44568. * @function Highcharts.Navigator#getUnionExtremes
  44569. * @param {boolean} [returnFalseOnNoBaseSeries]
  44570. * as the param says.
  44571. * @return {Highcharts.Dictionary<(number|undefined)>|undefined}
  44572. */
  44573. Navigator.prototype.getUnionExtremes = function (returnFalseOnNoBaseSeries) {
  44574. var baseAxis = this.chart.xAxis[0], navAxis = this.xAxis, navAxisOptions = navAxis.options, baseAxisOptions = baseAxis.options, ret;
  44575. if (!returnFalseOnNoBaseSeries || baseAxis.dataMin !== null) {
  44576. ret = {
  44577. dataMin: pick(// #4053
  44578. navAxisOptions && navAxisOptions.min, numExt('min', baseAxisOptions.min, baseAxis.dataMin, navAxis.dataMin, navAxis.min)),
  44579. dataMax: pick(navAxisOptions && navAxisOptions.max, numExt('max', baseAxisOptions.max, baseAxis.dataMax, navAxis.dataMax, navAxis.max))
  44580. };
  44581. }
  44582. return ret;
  44583. };
  44584. /**
  44585. * Set the base series and update the navigator series from this. With a bit
  44586. * of modification we should be able to make this an API method to be called
  44587. * from the outside
  44588. *
  44589. * @private
  44590. * @function Highcharts.Navigator#setBaseSeries
  44591. * @param {Highcharts.SeriesOptionsType} [baseSeriesOptions]
  44592. * Additional series options for a navigator
  44593. * @param {boolean} [redraw]
  44594. * Whether to redraw after update.
  44595. * @return {void}
  44596. */
  44597. Navigator.prototype.setBaseSeries = function (baseSeriesOptions, redraw) {
  44598. var chart = this.chart, baseSeries = this.baseSeries = [];
  44599. baseSeriesOptions = (baseSeriesOptions ||
  44600. chart.options && chart.options.navigator.baseSeries ||
  44601. (chart.series.length ?
  44602. // Find the first non-navigator series (#8430)
  44603. find(chart.series, function (s) {
  44604. return !s.options.isInternal;
  44605. }).index :
  44606. 0));
  44607. // Iterate through series and add the ones that should be shown in
  44608. // navigator.
  44609. (chart.series || []).forEach(function (series, i) {
  44610. if (
  44611. // Don't include existing nav series
  44612. !series.options.isInternal &&
  44613. (series.options.showInNavigator ||
  44614. (i === baseSeriesOptions ||
  44615. series.options.id === baseSeriesOptions) &&
  44616. series.options.showInNavigator !== false)) {
  44617. baseSeries.push(series);
  44618. }
  44619. });
  44620. // When run after render, this.xAxis already exists
  44621. if (this.xAxis && !this.xAxis.navigatorAxis.fake) {
  44622. this.updateNavigatorSeries(true, redraw);
  44623. }
  44624. };
  44625. /**
  44626. * Update series in the navigator from baseSeries, adding new if does not
  44627. * exist.
  44628. *
  44629. * @private
  44630. * @function Highcharts.Navigator.updateNavigatorSeries
  44631. * @param {boolean} addEvents
  44632. * @param {boolean} [redraw]
  44633. * @return {void}
  44634. */
  44635. Navigator.prototype.updateNavigatorSeries = function (addEvents, redraw) {
  44636. var navigator = this, chart = navigator.chart, baseSeries = navigator.baseSeries, baseOptions, mergedNavSeriesOptions, chartNavigatorSeriesOptions = navigator.navigatorOptions.series, baseNavigatorOptions, navSeriesMixin = {
  44637. enableMouseTracking: false,
  44638. index: null,
  44639. linkedTo: null,
  44640. group: 'nav',
  44641. padXAxis: false,
  44642. xAxis: 'navigator-x-axis',
  44643. yAxis: 'navigator-y-axis',
  44644. showInLegend: false,
  44645. stacking: void 0,
  44646. isInternal: true,
  44647. states: {
  44648. inactive: {
  44649. opacity: 1
  44650. }
  44651. }
  44652. },
  44653. // Remove navigator series that are no longer in the baseSeries
  44654. navigatorSeries = navigator.series =
  44655. (navigator.series || []).filter(function (navSeries) {
  44656. var base = navSeries.baseSeries;
  44657. if (baseSeries.indexOf(base) < 0) { // Not in array
  44658. // If there is still a base series connected to this
  44659. // series, remove event handler and reference.
  44660. if (base) {
  44661. removeEvent(base, 'updatedData', navigator.updatedDataHandler);
  44662. delete base.navigatorSeries;
  44663. }
  44664. // Kill the nav series. It may already have been
  44665. // destroyed (#8715).
  44666. if (navSeries.chart) {
  44667. navSeries.destroy();
  44668. }
  44669. return false;
  44670. }
  44671. return true;
  44672. });
  44673. // Go through each base series and merge the options to create new
  44674. // series
  44675. if (baseSeries && baseSeries.length) {
  44676. baseSeries.forEach(function eachBaseSeries(base) {
  44677. var linkedNavSeries = base.navigatorSeries, userNavOptions = extend(
  44678. // Grab color and visibility from base as default
  44679. {
  44680. color: base.color,
  44681. visible: base.visible
  44682. }, !isArray(chartNavigatorSeriesOptions) ?
  44683. chartNavigatorSeriesOptions :
  44684. defaultOptions.navigator.series);
  44685. // Don't update if the series exists in nav and we have disabled
  44686. // adaptToUpdatedData.
  44687. if (linkedNavSeries &&
  44688. navigator.navigatorOptions.adaptToUpdatedData === false) {
  44689. return;
  44690. }
  44691. navSeriesMixin.name = 'Navigator ' + baseSeries.length;
  44692. baseOptions = base.options || {};
  44693. baseNavigatorOptions = baseOptions.navigatorOptions || {};
  44694. mergedNavSeriesOptions = merge(baseOptions, navSeriesMixin, userNavOptions, baseNavigatorOptions);
  44695. // Once nav series type is resolved, pick correct pointRange
  44696. mergedNavSeriesOptions.pointRange = pick(
  44697. // Stricte set pointRange in options
  44698. userNavOptions.pointRange, baseNavigatorOptions.pointRange,
  44699. // Fallback to default values, e.g. `null` for column
  44700. defaultOptions.plotOptions[mergedNavSeriesOptions.type || 'line'].pointRange);
  44701. // Merge data separately. Do a slice to avoid mutating the
  44702. // navigator options from base series (#4923).
  44703. var navigatorSeriesData = baseNavigatorOptions.data || userNavOptions.data;
  44704. navigator.hasNavigatorData =
  44705. navigator.hasNavigatorData || !!navigatorSeriesData;
  44706. mergedNavSeriesOptions.data =
  44707. navigatorSeriesData ||
  44708. baseOptions.data && baseOptions.data.slice(0);
  44709. // Update or add the series
  44710. if (linkedNavSeries && linkedNavSeries.options) {
  44711. linkedNavSeries.update(mergedNavSeriesOptions, redraw);
  44712. }
  44713. else {
  44714. base.navigatorSeries = chart.initSeries(mergedNavSeriesOptions);
  44715. base.navigatorSeries.baseSeries = base; // Store ref
  44716. navigatorSeries.push(base.navigatorSeries);
  44717. }
  44718. });
  44719. }
  44720. // If user has defined data (and no base series) or explicitly defined
  44721. // navigator.series as an array, we create these series on top of any
  44722. // base series.
  44723. if (chartNavigatorSeriesOptions.data &&
  44724. !(baseSeries && baseSeries.length) ||
  44725. isArray(chartNavigatorSeriesOptions)) {
  44726. navigator.hasNavigatorData = false;
  44727. // Allow navigator.series to be an array
  44728. chartNavigatorSeriesOptions =
  44729. splat(chartNavigatorSeriesOptions);
  44730. chartNavigatorSeriesOptions.forEach(function (userSeriesOptions, i) {
  44731. navSeriesMixin.name =
  44732. 'Navigator ' + (navigatorSeries.length + 1);
  44733. mergedNavSeriesOptions = merge(defaultOptions.navigator.series, {
  44734. // Since we don't have a base series to pull color from,
  44735. // try to fake it by using color from series with same
  44736. // index. Otherwise pull from the colors array. We need
  44737. // an explicit color as otherwise updates will increment
  44738. // color counter and we'll get a new color for each
  44739. // update of the nav series.
  44740. color: chart.series[i] &&
  44741. !chart.series[i].options.isInternal &&
  44742. chart.series[i].color ||
  44743. chart.options.colors[i] ||
  44744. chart.options.colors[0]
  44745. }, navSeriesMixin, userSeriesOptions);
  44746. mergedNavSeriesOptions.data = userSeriesOptions.data;
  44747. if (mergedNavSeriesOptions.data) {
  44748. navigator.hasNavigatorData = true;
  44749. navigatorSeries.push(chart.initSeries(mergedNavSeriesOptions));
  44750. }
  44751. });
  44752. }
  44753. if (addEvents) {
  44754. this.addBaseSeriesEvents();
  44755. }
  44756. };
  44757. /**
  44758. * Add data events.
  44759. * For example when main series is updated we need to recalculate extremes
  44760. *
  44761. * @private
  44762. * @function Highcharts.Navigator#addBaseSeriesEvent
  44763. * @return {void}
  44764. */
  44765. Navigator.prototype.addBaseSeriesEvents = function () {
  44766. var navigator = this, baseSeries = navigator.baseSeries || [];
  44767. // Bind modified extremes event to first base's xAxis only.
  44768. // In event of > 1 base-xAxes, the navigator will ignore those.
  44769. // Adding this multiple times to the same axis is no problem, as
  44770. // duplicates should be discarded by the browser.
  44771. if (baseSeries[0] && baseSeries[0].xAxis) {
  44772. addEvent(baseSeries[0].xAxis, 'foundExtremes', this.modifyBaseAxisExtremes);
  44773. }
  44774. baseSeries.forEach(function (base) {
  44775. // Link base series show/hide to navigator series visibility
  44776. addEvent(base, 'show', function () {
  44777. if (this.navigatorSeries) {
  44778. this.navigatorSeries.setVisible(true, false);
  44779. }
  44780. });
  44781. addEvent(base, 'hide', function () {
  44782. if (this.navigatorSeries) {
  44783. this.navigatorSeries.setVisible(false, false);
  44784. }
  44785. });
  44786. // Respond to updated data in the base series, unless explicitily
  44787. // not adapting to data changes.
  44788. if (this.navigatorOptions.adaptToUpdatedData !== false) {
  44789. if (base.xAxis) {
  44790. addEvent(base, 'updatedData', this.updatedDataHandler);
  44791. }
  44792. }
  44793. // Handle series removal
  44794. addEvent(base, 'remove', function () {
  44795. if (this.navigatorSeries) {
  44796. erase(navigator.series, this.navigatorSeries);
  44797. if (defined(this.navigatorSeries.options)) {
  44798. this.navigatorSeries.remove(false);
  44799. }
  44800. delete this.navigatorSeries;
  44801. }
  44802. });
  44803. }, this);
  44804. };
  44805. /**
  44806. * Get minimum from all base series connected to the navigator
  44807. * @private
  44808. * @param {number} currentSeriesMin
  44809. * Minium from the current series
  44810. * @return {number} Minimum from all series
  44811. */
  44812. Navigator.prototype.getBaseSeriesMin = function (currentSeriesMin) {
  44813. return this.baseSeries.reduce(function (min, series) {
  44814. // (#10193)
  44815. return Math.min(min, series.xData ? series.xData[0] : min);
  44816. }, currentSeriesMin);
  44817. };
  44818. /**
  44819. * Set the navigator x axis extremes to reflect the total. The navigator
  44820. * extremes should always be the extremes of the union of all series in the
  44821. * chart as well as the navigator series.
  44822. *
  44823. * @private
  44824. * @function Highcharts.Navigator#modifyNavigatorAxisExtremes
  44825. */
  44826. Navigator.prototype.modifyNavigatorAxisExtremes = function () {
  44827. var xAxis = this.xAxis, unionExtremes;
  44828. if (typeof xAxis.getExtremes !== 'undefined') {
  44829. unionExtremes = this.getUnionExtremes(true);
  44830. if (unionExtremes &&
  44831. (unionExtremes.dataMin !== xAxis.min ||
  44832. unionExtremes.dataMax !== xAxis.max)) {
  44833. xAxis.min = unionExtremes.dataMin;
  44834. xAxis.max = unionExtremes.dataMax;
  44835. }
  44836. }
  44837. };
  44838. /**
  44839. * Hook to modify the base axis extremes with information from the Navigator
  44840. *
  44841. * @private
  44842. * @function Highcharts.Navigator#modifyBaseAxisExtremes
  44843. */
  44844. Navigator.prototype.modifyBaseAxisExtremes = function () {
  44845. var 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), newMax, newMin, navigatorSeries = navigator.series && navigator.series[0], hasSetExtremes = !!baseXAxis.setExtremes,
  44846. // When the extremes have been set by range selector button, don't
  44847. // stick to min or max. The range selector buttons will handle the
  44848. // extremes. (#5489)
  44849. unmutable = baseXAxis.eventArgs &&
  44850. baseXAxis.eventArgs.trigger === 'rangeSelectorButton';
  44851. if (!unmutable) {
  44852. // If the zoomed range is already at the min, move it to the right
  44853. // as new data comes in
  44854. if (stickToMin) {
  44855. newMin = baseDataMin;
  44856. newMax = newMin + range;
  44857. }
  44858. // If the zoomed range is already at the max, move it to the right
  44859. // as new data comes in
  44860. if (stickToMax) {
  44861. newMax = baseDataMax + overscroll;
  44862. // If stickToMin is true, the new min value is set above
  44863. if (!stickToMin) {
  44864. newMin = Math.max(baseDataMin, // don't go below data extremes (#13184)
  44865. newMax - range, navigator.getBaseSeriesMin(navigatorSeries && navigatorSeries.xData ?
  44866. navigatorSeries.xData[0] :
  44867. -Number.MAX_VALUE));
  44868. }
  44869. }
  44870. // Update the extremes
  44871. if (hasSetExtremes && (stickToMin || stickToMax)) {
  44872. if (isNumber(newMin)) {
  44873. baseXAxis.min = baseXAxis.userMin = newMin;
  44874. baseXAxis.max = baseXAxis.userMax = newMax;
  44875. }
  44876. }
  44877. }
  44878. // Reset
  44879. navigator.stickToMin =
  44880. navigator.stickToMax = null;
  44881. };
  44882. /**
  44883. * Handler for updated data on the base series. When data is modified, the
  44884. * navigator series must reflect it. This is called from the Chart.redraw
  44885. * function before axis and series extremes are computed.
  44886. *
  44887. * @private
  44888. * @function Highcharts.Navigator#updateDataHandler
  44889. */
  44890. Navigator.prototype.updatedDataHandler = function () {
  44891. var navigator = this.chart.navigator, baseSeries = this, navigatorSeries = this.navigatorSeries, xDataMin = navigator.getBaseSeriesMin(baseSeries.xData[0]);
  44892. // If the scrollbar is scrolled all the way to the right, keep right as
  44893. // new data comes in.
  44894. navigator.stickToMax = navigator.reversedExtremes ?
  44895. Math.round(navigator.zoomedMin) === 0 :
  44896. Math.round(navigator.zoomedMax) >= Math.round(navigator.size);
  44897. // Detect whether the zoomed area should stick to the minimum or
  44898. // maximum. If the current axis minimum falls outside the new updated
  44899. // dataset, we must adjust.
  44900. navigator.stickToMin = isNumber(baseSeries.xAxis.min) &&
  44901. (baseSeries.xAxis.min <= xDataMin) &&
  44902. (!this.chart.fixedRange || !navigator.stickToMax);
  44903. // Set the navigator series data to the new data of the base series
  44904. if (navigatorSeries && !navigator.hasNavigatorData) {
  44905. navigatorSeries.options.pointStart = baseSeries.xData[0];
  44906. navigatorSeries.setData(baseSeries.options.data, false, null, false); // #5414
  44907. }
  44908. };
  44909. /**
  44910. * Add chart events, like redrawing navigator, when chart requires that.
  44911. *
  44912. * @private
  44913. * @function Highcharts.Navigator#addChartEvents
  44914. * @return {void}
  44915. */
  44916. Navigator.prototype.addChartEvents = function () {
  44917. if (!this.eventsToUnbind) {
  44918. this.eventsToUnbind = [];
  44919. }
  44920. this.eventsToUnbind.push(
  44921. // Move the scrollbar after redraw, like after data updata even if
  44922. // axes don't redraw
  44923. addEvent(this.chart, 'redraw', function () {
  44924. var navigator = this.navigator, xAxis = navigator && (navigator.baseSeries &&
  44925. navigator.baseSeries[0] &&
  44926. navigator.baseSeries[0].xAxis ||
  44927. this.xAxis[0]); // #5709, #13114
  44928. if (xAxis) {
  44929. navigator.render(xAxis.min, xAxis.max);
  44930. }
  44931. }),
  44932. // Make room for the navigator, can be placed around the chart:
  44933. addEvent(this.chart, 'getMargins', function () {
  44934. var chart = this, navigator = chart.navigator, marginName = navigator.opposite ?
  44935. 'plotTop' : 'marginBottom';
  44936. if (chart.inverted) {
  44937. marginName = navigator.opposite ?
  44938. 'marginRight' : 'plotLeft';
  44939. }
  44940. chart[marginName] =
  44941. (chart[marginName] || 0) + (navigator.navigatorEnabled || !chart.inverted ?
  44942. navigator.outlineHeight :
  44943. 0) + navigator.navigatorOptions.margin;
  44944. }));
  44945. };
  44946. /**
  44947. * Destroys allocated elements.
  44948. *
  44949. * @private
  44950. * @function Highcharts.Navigator#destroy
  44951. */
  44952. Navigator.prototype.destroy = function () {
  44953. // Disconnect events added in addEvents
  44954. this.removeEvents();
  44955. if (this.xAxis) {
  44956. erase(this.chart.xAxis, this.xAxis);
  44957. erase(this.chart.axes, this.xAxis);
  44958. }
  44959. if (this.yAxis) {
  44960. erase(this.chart.yAxis, this.yAxis);
  44961. erase(this.chart.axes, this.yAxis);
  44962. }
  44963. // Destroy series
  44964. (this.series || []).forEach(function (s) {
  44965. if (s.destroy) {
  44966. s.destroy();
  44967. }
  44968. });
  44969. // Destroy properties
  44970. [
  44971. 'series', 'xAxis', 'yAxis', 'shades', 'outline', 'scrollbarTrack',
  44972. 'scrollbarRifles', 'scrollbarGroup', 'scrollbar', 'navigatorGroup',
  44973. 'rendered'
  44974. ].forEach(function (prop) {
  44975. if (this[prop] && this[prop].destroy) {
  44976. this[prop].destroy();
  44977. }
  44978. this[prop] = null;
  44979. }, this);
  44980. // Destroy elements in collection
  44981. [this.handles].forEach(function (coll) {
  44982. destroyObjectProperties(coll);
  44983. }, this);
  44984. };
  44985. return Navigator;
  44986. }());
  44987. // End of prototype
  44988. if (!H.Navigator) {
  44989. H.Navigator = Navigator;
  44990. NavigatorAxis.compose(Axis);
  44991. // For Stock charts. For x only zooming, do not to create the zoom button
  44992. // because X axis zooming is already allowed by the Navigator and Range
  44993. // selector. (#9285)
  44994. addEvent(Chart, 'beforeShowResetZoom', function () {
  44995. var chartOptions = this.options, navigator = chartOptions.navigator, rangeSelector = chartOptions.rangeSelector;
  44996. if (((navigator && navigator.enabled) ||
  44997. (rangeSelector && rangeSelector.enabled)) &&
  44998. ((!isTouchDevice && chartOptions.chart.zoomType === 'x') ||
  44999. (isTouchDevice && chartOptions.chart.pinchType === 'x'))) {
  45000. return false;
  45001. }
  45002. });
  45003. // Initialize navigator for stock charts
  45004. addEvent(Chart, 'beforeRender', function () {
  45005. var options = this.options;
  45006. if (options.navigator.enabled ||
  45007. options.scrollbar.enabled) {
  45008. this.scroller = this.navigator = new Navigator(this);
  45009. }
  45010. });
  45011. // For stock charts, extend the Chart.setChartSize method so that we can set
  45012. // the final top position of the navigator once the height of the chart,
  45013. // including the legend, is determined. #367. We can't use Chart.getMargins,
  45014. // because labels offsets are not calculated yet.
  45015. addEvent(Chart, 'afterSetChartSize', function () {
  45016. var legend = this.legend, navigator = this.navigator, scrollbarHeight, legendOptions, xAxis, yAxis;
  45017. if (navigator) {
  45018. legendOptions = legend && legend.options;
  45019. xAxis = navigator.xAxis;
  45020. yAxis = navigator.yAxis;
  45021. scrollbarHeight = navigator.scrollbarHeight;
  45022. // Compute the top position
  45023. if (this.inverted) {
  45024. navigator.left = navigator.opposite ?
  45025. this.chartWidth - scrollbarHeight -
  45026. navigator.height :
  45027. this.spacing[3] + scrollbarHeight;
  45028. navigator.top = this.plotTop + scrollbarHeight;
  45029. }
  45030. else {
  45031. navigator.left = this.plotLeft + scrollbarHeight;
  45032. navigator.top = navigator.navigatorOptions.top ||
  45033. this.chartHeight -
  45034. navigator.height -
  45035. scrollbarHeight -
  45036. this.spacing[2] -
  45037. (this.rangeSelector && this.extraBottomMargin ?
  45038. this.rangeSelector.getHeight() :
  45039. 0) -
  45040. ((legendOptions &&
  45041. legendOptions.verticalAlign === 'bottom' &&
  45042. legendOptions.layout !== 'proximate' && // #13392
  45043. legendOptions.enabled &&
  45044. !legendOptions.floating) ?
  45045. legend.legendHeight +
  45046. pick(legendOptions.margin, 10) :
  45047. 0) -
  45048. (this.titleOffset ? this.titleOffset[2] : 0);
  45049. }
  45050. if (xAxis && yAxis) { // false if navigator is disabled (#904)
  45051. if (this.inverted) {
  45052. xAxis.options.left = yAxis.options.left = navigator.left;
  45053. }
  45054. else {
  45055. xAxis.options.top = yAxis.options.top = navigator.top;
  45056. }
  45057. xAxis.setAxisSize();
  45058. yAxis.setAxisSize();
  45059. }
  45060. }
  45061. });
  45062. // Merge options, if no scrolling exists yet
  45063. addEvent(Chart, 'update', function (e) {
  45064. var navigatorOptions = (e.options.navigator || {}), scrollbarOptions = (e.options.scrollbar || {});
  45065. if (!this.navigator && !this.scroller &&
  45066. (navigatorOptions.enabled || scrollbarOptions.enabled)) {
  45067. merge(true, this.options.navigator, navigatorOptions);
  45068. merge(true, this.options.scrollbar, scrollbarOptions);
  45069. delete e.options.navigator;
  45070. delete e.options.scrollbar;
  45071. }
  45072. });
  45073. // Initialize navigator, if no scrolling exists yet
  45074. addEvent(Chart, 'afterUpdate', function (event) {
  45075. if (!this.navigator && !this.scroller &&
  45076. (this.options.navigator.enabled ||
  45077. this.options.scrollbar.enabled)) {
  45078. this.scroller = this.navigator = new Navigator(this);
  45079. if (pick(event.redraw, true)) {
  45080. this.redraw(event.animation); // #7067
  45081. }
  45082. }
  45083. });
  45084. // Handle adding new series
  45085. addEvent(Chart, 'afterAddSeries', function () {
  45086. if (this.navigator) {
  45087. // Recompute which series should be shown in navigator, and add them
  45088. this.navigator.setBaseSeries(null, false);
  45089. }
  45090. });
  45091. // Handle updating series
  45092. addEvent(Series, 'afterUpdate', function () {
  45093. if (this.chart.navigator && !this.options.isInternal) {
  45094. this.chart.navigator.setBaseSeries(null, false);
  45095. }
  45096. });
  45097. Chart.prototype.callbacks.push(function (chart) {
  45098. var extremes, navigator = chart.navigator;
  45099. // Initialize the navigator
  45100. if (navigator && chart.xAxis[0]) {
  45101. extremes = chart.xAxis[0].getExtremes();
  45102. navigator.render(extremes.min, extremes.max);
  45103. }
  45104. });
  45105. }
  45106. H.Navigator = Navigator;
  45107. return H.Navigator;
  45108. });
  45109. _registerModule(_modules, 'parts/OrdinalAxis.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Axis, H, U) {
  45110. /* *
  45111. *
  45112. * (c) 2010-2020 Torstein Honsi
  45113. *
  45114. * License: www.highcharts.com/license
  45115. *
  45116. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45117. *
  45118. * */
  45119. var addEvent = U.addEvent, css = U.css, defined = U.defined, pick = U.pick, timeUnits = U.timeUnits;
  45120. // Has a dependency on Navigator due to the use of Axis.toFixedRange
  45121. var Chart = H.Chart, Series = H.Series;
  45122. /**
  45123. * Extends the axis with ordinal support.
  45124. * @private
  45125. */
  45126. var OrdinalAxis;
  45127. (function (OrdinalAxis) {
  45128. /* *
  45129. *
  45130. * Classes
  45131. *
  45132. * */
  45133. /**
  45134. * @private
  45135. */
  45136. var Composition = /** @class */ (function () {
  45137. /* *
  45138. *
  45139. * Constructors
  45140. *
  45141. * */
  45142. /**
  45143. * @private
  45144. */
  45145. function Composition(axis) {
  45146. this.index = {};
  45147. this.axis = axis;
  45148. }
  45149. /* *
  45150. *
  45151. * Functions
  45152. *
  45153. * */
  45154. /**
  45155. * Calculate the ordinal positions before tick positions are calculated.
  45156. *
  45157. * @private
  45158. */
  45159. Composition.prototype.beforeSetTickPositions = function () {
  45160. var axis = this.axis, ordinal = axis.ordinal, len, ordinalPositions = [], uniqueOrdinalPositions, useOrdinal = false, dist, extremes = axis.getExtremes(), min = extremes.min, max = extremes.max, minIndex, maxIndex, slope, hasBreaks = axis.isXAxis && !!axis.options.breaks, isOrdinal = axis.options.ordinal, overscrollPointsRange = Number.MAX_VALUE, ignoreHiddenSeries = axis.chart.options.chart.ignoreHiddenSeries, i, hasBoostedSeries;
  45161. // Apply the ordinal logic
  45162. if (isOrdinal || hasBreaks) { // #4167 YAxis is never ordinal ?
  45163. axis.series.forEach(function (series, i) {
  45164. uniqueOrdinalPositions = [];
  45165. if ((!ignoreHiddenSeries || series.visible !== false) &&
  45166. (series.takeOrdinalPosition !== false || hasBreaks)) {
  45167. // concatenate the processed X data into the existing
  45168. // positions, or the empty array
  45169. ordinalPositions = ordinalPositions.concat(series.processedXData);
  45170. len = ordinalPositions.length;
  45171. // remove duplicates (#1588)
  45172. ordinalPositions.sort(function (a, b) {
  45173. // without a custom function it is sorted as strings
  45174. return a - b;
  45175. });
  45176. overscrollPointsRange = Math.min(overscrollPointsRange, pick(
  45177. // Check for a single-point series:
  45178. series.closestPointRange, overscrollPointsRange));
  45179. if (len) {
  45180. i = 0;
  45181. while (i < len - 1) {
  45182. if (ordinalPositions[i] !== ordinalPositions[i + 1]) {
  45183. uniqueOrdinalPositions.push(ordinalPositions[i + 1]);
  45184. }
  45185. i++;
  45186. }
  45187. // Check first item:
  45188. if (uniqueOrdinalPositions[0] !== ordinalPositions[0]) {
  45189. uniqueOrdinalPositions.unshift(ordinalPositions[0]);
  45190. }
  45191. ordinalPositions = uniqueOrdinalPositions;
  45192. }
  45193. }
  45194. if (series.isSeriesBoosting) {
  45195. hasBoostedSeries = true;
  45196. }
  45197. });
  45198. if (hasBoostedSeries) {
  45199. ordinalPositions.length = 0;
  45200. }
  45201. // cache the length
  45202. len = ordinalPositions.length;
  45203. // Check if we really need the overhead of mapping axis data
  45204. // against the ordinal positions. If the series consist of
  45205. // evenly spaced data any way, we don't need any ordinal logic.
  45206. if (len > 2) { // two points have equal distance by default
  45207. dist = ordinalPositions[1] - ordinalPositions[0];
  45208. i = len - 1;
  45209. while (i-- && !useOrdinal) {
  45210. if (ordinalPositions[i + 1] - ordinalPositions[i] !== dist) {
  45211. useOrdinal = true;
  45212. }
  45213. }
  45214. // When zooming in on a week, prevent axis padding for
  45215. // weekends even though the data within the week is evenly
  45216. // spaced.
  45217. if (!axis.options.keepOrdinalPadding &&
  45218. (ordinalPositions[0] - min > dist ||
  45219. max - ordinalPositions[ordinalPositions.length - 1] >
  45220. dist)) {
  45221. useOrdinal = true;
  45222. }
  45223. }
  45224. else if (axis.options.overscroll) {
  45225. if (len === 2) {
  45226. // Exactly two points, distance for overscroll is fixed:
  45227. overscrollPointsRange =
  45228. ordinalPositions[1] - ordinalPositions[0];
  45229. }
  45230. else if (len === 1) {
  45231. // We have just one point, closest distance is unknown.
  45232. // Assume then it is last point and overscrolled range:
  45233. overscrollPointsRange = axis.options.overscroll;
  45234. ordinalPositions = [
  45235. ordinalPositions[0],
  45236. ordinalPositions[0] + overscrollPointsRange
  45237. ];
  45238. }
  45239. else {
  45240. // In case of zooming in on overscrolled range, stick to
  45241. // the old range:
  45242. overscrollPointsRange = ordinal.overscrollPointsRange;
  45243. }
  45244. }
  45245. // Record the slope and offset to compute the linear values from
  45246. // the array index. Since the ordinal positions may exceed the
  45247. // current range, get the start and end positions within it
  45248. // (#719, #665b)
  45249. if (useOrdinal) {
  45250. if (axis.options.overscroll) {
  45251. ordinal.overscrollPointsRange = overscrollPointsRange;
  45252. ordinalPositions = ordinalPositions.concat(ordinal.getOverscrollPositions());
  45253. }
  45254. // Register
  45255. ordinal.positions = ordinalPositions;
  45256. // This relies on the ordinalPositions being set. Use
  45257. // Math.max and Math.min to prevent padding on either sides
  45258. // of the data.
  45259. minIndex = axis.ordinal2lin(// #5979
  45260. Math.max(min, ordinalPositions[0]), true);
  45261. maxIndex = Math.max(axis.ordinal2lin(Math.min(max, ordinalPositions[ordinalPositions.length - 1]), true), 1); // #3339
  45262. // Set the slope and offset of the values compared to the
  45263. // indices in the ordinal positions
  45264. ordinal.slope = slope = (max - min) / (maxIndex - minIndex);
  45265. ordinal.offset = min - (minIndex * slope);
  45266. }
  45267. else {
  45268. ordinal.overscrollPointsRange = pick(axis.closestPointRange, ordinal.overscrollPointsRange);
  45269. ordinal.positions = axis.ordinal.slope = ordinal.offset =
  45270. void 0;
  45271. }
  45272. }
  45273. axis.isOrdinal = isOrdinal && useOrdinal; // #3818, #4196, #4926
  45274. ordinal.groupIntervalFactor = null; // reset for next run
  45275. };
  45276. /**
  45277. * Get the ordinal positions for the entire data set. This is necessary
  45278. * in chart panning because we need to find out what points or data
  45279. * groups are available outside the visible range. When a panning
  45280. * operation starts, if an index for the given grouping does not exists,
  45281. * it is created and cached. This index is deleted on updated data, so
  45282. * it will be regenerated the next time a panning operation starts.
  45283. *
  45284. * @private
  45285. */
  45286. Composition.prototype.getExtendedPositions = function () {
  45287. var ordinal = this, axis = ordinal.axis, axisProto = axis.constructor.prototype, chart = axis.chart, grouping = axis.series[0].currentDataGrouping, ordinalIndex = ordinal.index, key = grouping ?
  45288. grouping.count + grouping.unitName :
  45289. 'raw', overscroll = axis.options.overscroll, extremes = axis.getExtremes(), fakeAxis, fakeSeries;
  45290. // If this is the first time, or the ordinal index is deleted by
  45291. // updatedData,
  45292. // create it.
  45293. if (!ordinalIndex) {
  45294. ordinalIndex = ordinal.index = {};
  45295. }
  45296. if (!ordinalIndex[key]) {
  45297. // Create a fake axis object where the extended ordinal
  45298. // positions are emulated
  45299. fakeAxis = {
  45300. series: [],
  45301. chart: chart,
  45302. getExtremes: function () {
  45303. return {
  45304. min: extremes.dataMin,
  45305. max: extremes.dataMax + overscroll
  45306. };
  45307. },
  45308. options: {
  45309. ordinal: true
  45310. },
  45311. ordinal: {},
  45312. ordinal2lin: axisProto.ordinal2lin,
  45313. val2lin: axisProto.val2lin // #2590
  45314. };
  45315. fakeAxis.ordinal.axis = fakeAxis;
  45316. // Add the fake series to hold the full data, then apply
  45317. // processData to it
  45318. axis.series.forEach(function (series) {
  45319. fakeSeries = {
  45320. xAxis: fakeAxis,
  45321. xData: series.xData.slice(),
  45322. chart: chart,
  45323. destroyGroupedData: H.noop,
  45324. getProcessedData: H.Series.prototype.getProcessedData
  45325. };
  45326. fakeSeries.xData = fakeSeries.xData.concat(ordinal.getOverscrollPositions());
  45327. fakeSeries.options = {
  45328. dataGrouping: grouping ? {
  45329. enabled: true,
  45330. forced: true,
  45331. // doesn't matter which, use the fastest
  45332. approximation: 'open',
  45333. units: [[
  45334. grouping.unitName,
  45335. [grouping.count]
  45336. ]]
  45337. } : {
  45338. enabled: false
  45339. }
  45340. };
  45341. series.processData.apply(fakeSeries);
  45342. fakeAxis.series.push(fakeSeries);
  45343. });
  45344. // Run beforeSetTickPositions to compute the ordinalPositions
  45345. axis.ordinal.beforeSetTickPositions.apply({ axis: fakeAxis });
  45346. // Cache it
  45347. ordinalIndex[key] = fakeAxis.ordinal.positions;
  45348. }
  45349. return ordinalIndex[key];
  45350. };
  45351. /**
  45352. * Find the factor to estimate how wide the plot area would have been if
  45353. * ordinal gaps were included. This value is used to compute an imagined
  45354. * plot width in order to establish the data grouping interval.
  45355. *
  45356. * A real world case is the intraday-candlestick example. Without this
  45357. * logic, it would show the correct data grouping when viewing a range
  45358. * within each day, but once moving the range to include the gap between
  45359. * two days, the interval would include the cut-away night hours and the
  45360. * data grouping would be wrong. So the below method tries to compensate
  45361. * by identifying the most common point interval, in this case days.
  45362. *
  45363. * An opposite case is presented in issue #718. We have a long array of
  45364. * daily data, then one point is appended one hour after the last point.
  45365. * We expect the data grouping not to change.
  45366. *
  45367. * In the future, if we find cases where this estimation doesn't work
  45368. * optimally, we might need to add a second pass to the data grouping
  45369. * logic, where we do another run with a greater interval if the number
  45370. * of data groups is more than a certain fraction of the desired group
  45371. * count.
  45372. *
  45373. * @private
  45374. */
  45375. Composition.prototype.getGroupIntervalFactor = function (xMin, xMax, series) {
  45376. var ordinal = this, axis = ordinal.axis, i, processedXData = series.processedXData, len = processedXData.length, distances = [], median, groupIntervalFactor = ordinal.groupIntervalFactor;
  45377. // Only do this computation for the first series, let the other
  45378. // inherit it (#2416)
  45379. if (!groupIntervalFactor) {
  45380. // Register all the distances in an array
  45381. for (i = 0; i < len - 1; i++) {
  45382. distances[i] =
  45383. processedXData[i + 1] - processedXData[i];
  45384. }
  45385. // Sort them and find the median
  45386. distances.sort(function (a, b) {
  45387. return a - b;
  45388. });
  45389. median = distances[Math.floor(len / 2)];
  45390. // Compensate for series that don't extend through the entire
  45391. // axis extent. #1675.
  45392. xMin = Math.max(xMin, processedXData[0]);
  45393. xMax = Math.min(xMax, processedXData[len - 1]);
  45394. ordinal.groupIntervalFactor = groupIntervalFactor =
  45395. (len * median) / (xMax - xMin);
  45396. }
  45397. // Return the factor needed for data grouping
  45398. return groupIntervalFactor;
  45399. };
  45400. /**
  45401. * Get ticks for an ordinal axis within a range where points don't
  45402. * exist. It is required when overscroll is enabled. We can't base on
  45403. * points, because we may not have any, so we use approximated
  45404. * pointRange and generate these ticks between Axis.dataMax,
  45405. * Axis.dataMax + Axis.overscroll evenly spaced. Used in panning and
  45406. * navigator scrolling.
  45407. *
  45408. * @private
  45409. */
  45410. Composition.prototype.getOverscrollPositions = function () {
  45411. var ordinal = this, axis = ordinal.axis, extraRange = axis.options.overscroll, distance = ordinal.overscrollPointsRange, positions = [], max = axis.dataMax;
  45412. if (defined(distance)) {
  45413. // Max + pointRange because we need to scroll to the last
  45414. positions.push(max);
  45415. while (max <= axis.dataMax + extraRange) {
  45416. max += distance;
  45417. positions.push(max);
  45418. }
  45419. }
  45420. return positions;
  45421. };
  45422. /**
  45423. * Make the tick intervals closer because the ordinal gaps make the
  45424. * ticks spread out or cluster.
  45425. *
  45426. * @private
  45427. */
  45428. Composition.prototype.postProcessTickInterval = function (tickInterval) {
  45429. // Problem: https://jsfiddle.net/highcharts/FQm4E/1/
  45430. // This is a case where this algorithm doesn't work optimally. In
  45431. // this case, the tick labels are spread out per week, but all the
  45432. // gaps reside within weeks. So we have a situation where the labels
  45433. // are courser than the ordinal gaps, and thus the tick interval
  45434. // should not be altered.
  45435. var ordinal = this, axis = ordinal.axis, ordinalSlope = ordinal.slope, ret;
  45436. if (ordinalSlope) {
  45437. if (!axis.options.breaks) {
  45438. ret = tickInterval / (ordinalSlope / axis.closestPointRange);
  45439. }
  45440. else {
  45441. ret = axis.closestPointRange || tickInterval; // #7275
  45442. }
  45443. }
  45444. else {
  45445. ret = tickInterval;
  45446. }
  45447. return ret;
  45448. };
  45449. return Composition;
  45450. }());
  45451. OrdinalAxis.Composition = Composition;
  45452. /* *
  45453. *
  45454. * Functions
  45455. *
  45456. * */
  45457. /**
  45458. * Extends the axis with ordinal support.
  45459. *
  45460. * @private
  45461. *
  45462. * @param AxisClass
  45463. * Axis class to extend.
  45464. *
  45465. * @param ChartClass
  45466. * Chart class to use.
  45467. *
  45468. * @param SeriesClass
  45469. * Series class to use.
  45470. */
  45471. function compose(AxisClass, ChartClass, SeriesClass) {
  45472. AxisClass.keepProps.push('ordinal');
  45473. var axisProto = AxisClass.prototype;
  45474. /**
  45475. * In an ordinal axis, there might be areas with dense consentrations of
  45476. * points, then large gaps between some. Creating equally distributed
  45477. * ticks over this entire range may lead to a huge number of ticks that
  45478. * will later be removed. So instead, break the positions up in
  45479. * segments, find the tick positions for each segment then concatenize
  45480. * them. This method is used from both data grouping logic and X axis
  45481. * tick position logic.
  45482. *
  45483. * @private
  45484. */
  45485. AxisClass.prototype.getTimeTicks = function (normalizedInterval, min, max, startOfWeek, positions, closestDistance, findHigherRanks) {
  45486. if (positions === void 0) { positions = []; }
  45487. if (closestDistance === void 0) { closestDistance = 0; }
  45488. var start = 0, end, segmentPositions, higherRanks = {}, hasCrossedHigherRank, info, posLength, outsideMax, groupPositions = [], lastGroupPosition = -Number.MAX_VALUE, tickPixelIntervalOption = this.options.tickPixelInterval, time = this.chart.time,
  45489. // Record all the start positions of a segment, to use when
  45490. // deciding what's a gap in the data.
  45491. segmentStarts = [];
  45492. // The positions are not always defined, for example for ordinal
  45493. // positions when data has regular interval (#1557, #2090)
  45494. if ((!this.options.ordinal && !this.options.breaks) ||
  45495. !positions ||
  45496. positions.length < 3 ||
  45497. typeof min === 'undefined') {
  45498. return time.getTimeTicks.apply(time, arguments);
  45499. }
  45500. // Analyze the positions array to split it into segments on gaps
  45501. // larger than 5 times the closest distance. The closest distance is
  45502. // already found at this point, so we reuse that instead of
  45503. // computing it again.
  45504. posLength = positions.length;
  45505. for (end = 0; end < posLength; end++) {
  45506. outsideMax = end && positions[end - 1] > max;
  45507. if (positions[end] < min) { // Set the last position before min
  45508. start = end;
  45509. }
  45510. if (end === posLength - 1 ||
  45511. positions[end + 1] - positions[end] > closestDistance * 5 ||
  45512. outsideMax) {
  45513. // For each segment, calculate the tick positions from the
  45514. // getTimeTicks utility function. The interval will be the
  45515. // same regardless of how long the segment is.
  45516. if (positions[end] > lastGroupPosition) { // #1475
  45517. segmentPositions = time.getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek);
  45518. // Prevent duplicate groups, for example for multiple
  45519. // segments within one larger time frame (#1475)
  45520. while (segmentPositions.length &&
  45521. segmentPositions[0] <= lastGroupPosition) {
  45522. segmentPositions.shift();
  45523. }
  45524. if (segmentPositions.length) {
  45525. lastGroupPosition =
  45526. segmentPositions[segmentPositions.length - 1];
  45527. }
  45528. segmentStarts.push(groupPositions.length);
  45529. groupPositions = groupPositions.concat(segmentPositions);
  45530. }
  45531. // Set start of next segment
  45532. start = end + 1;
  45533. }
  45534. if (outsideMax) {
  45535. break;
  45536. }
  45537. }
  45538. // Get the grouping info from the last of the segments. The info is
  45539. // the same for all segments.
  45540. info = segmentPositions.info;
  45541. // Optionally identify ticks with higher rank, for example when the
  45542. // ticks have crossed midnight.
  45543. if (findHigherRanks && info.unitRange <= timeUnits.hour) {
  45544. end = groupPositions.length - 1;
  45545. // Compare points two by two
  45546. for (start = 1; start < end; start++) {
  45547. if (time.dateFormat('%d', groupPositions[start]) !==
  45548. time.dateFormat('%d', groupPositions[start - 1])) {
  45549. higherRanks[groupPositions[start]] = 'day';
  45550. hasCrossedHigherRank = true;
  45551. }
  45552. }
  45553. // If the complete array has crossed midnight, we want to mark
  45554. // the first positions also as higher rank
  45555. if (hasCrossedHigherRank) {
  45556. higherRanks[groupPositions[0]] = 'day';
  45557. }
  45558. info.higherRanks = higherRanks;
  45559. }
  45560. // Save the info
  45561. info.segmentStarts = segmentStarts;
  45562. groupPositions.info = info;
  45563. // Don't show ticks within a gap in the ordinal axis, where the
  45564. // space between two points is greater than a portion of the tick
  45565. // pixel interval
  45566. if (findHigherRanks && defined(tickPixelIntervalOption)) {
  45567. var length = groupPositions.length, i = length, itemToRemove, translated, translatedArr = [], lastTranslated, medianDistance, distance, distances = [];
  45568. // Find median pixel distance in order to keep a reasonably even
  45569. // distance between ticks (#748)
  45570. while (i--) {
  45571. translated = this.translate(groupPositions[i]);
  45572. if (lastTranslated) {
  45573. distances[i] = lastTranslated - translated;
  45574. }
  45575. translatedArr[i] = lastTranslated = translated;
  45576. }
  45577. distances.sort();
  45578. medianDistance = distances[Math.floor(distances.length / 2)];
  45579. if (medianDistance < tickPixelIntervalOption * 0.6) {
  45580. medianDistance = null;
  45581. }
  45582. // Now loop over again and remove ticks where needed
  45583. i = groupPositions[length - 1] > max ? length - 1 : length; // #817
  45584. lastTranslated = void 0;
  45585. while (i--) {
  45586. translated = translatedArr[i];
  45587. distance = Math.abs(lastTranslated - translated);
  45588. // #4175 - when axis is reversed, the distance, is negative
  45589. // but tickPixelIntervalOption positive, so we need to
  45590. // compare the same values
  45591. // Remove ticks that are closer than 0.6 times the pixel
  45592. // interval from the one to the right, but not if it is
  45593. // close to the median distance (#748).
  45594. if (lastTranslated &&
  45595. distance < tickPixelIntervalOption * 0.8 &&
  45596. (medianDistance === null || distance < medianDistance * 0.8)) {
  45597. // Is this a higher ranked position with a normal
  45598. // position to the right?
  45599. if (higherRanks[groupPositions[i]] &&
  45600. !higherRanks[groupPositions[i + 1]]) {
  45601. // Yes: remove the lower ranked neighbour to the
  45602. // right
  45603. itemToRemove = i + 1;
  45604. lastTranslated = translated; // #709
  45605. }
  45606. else {
  45607. // No: remove this one
  45608. itemToRemove = i;
  45609. }
  45610. groupPositions.splice(itemToRemove, 1);
  45611. }
  45612. else {
  45613. lastTranslated = translated;
  45614. }
  45615. }
  45616. }
  45617. return groupPositions;
  45618. };
  45619. /**
  45620. * Translate from linear (internal) to axis value.
  45621. *
  45622. * @private
  45623. * @function Highcharts.Axis#lin2val
  45624. *
  45625. * @param {number} val
  45626. * The linear abstracted value.
  45627. *
  45628. * @param {boolean} [fromIndex]
  45629. * Translate from an index in the ordinal positions rather than a
  45630. * value.
  45631. *
  45632. * @return {number}
  45633. */
  45634. axisProto.lin2val = function (val, fromIndex) {
  45635. var axis = this, ordinal = axis.ordinal, ordinalPositions = ordinal.positions, ret;
  45636. // the visible range contains only equally spaced values
  45637. if (!ordinalPositions) {
  45638. ret = val;
  45639. }
  45640. else {
  45641. var ordinalSlope = ordinal.slope, ordinalOffset = ordinal.offset, i = ordinalPositions.length - 1, linearEquivalentLeft, linearEquivalentRight, distance;
  45642. // Handle the case where we translate from the index directly,
  45643. // used only when panning an ordinal axis
  45644. if (fromIndex) {
  45645. if (val < 0) { // out of range, in effect panning to the left
  45646. val = ordinalPositions[0];
  45647. }
  45648. else if (val > i) { // out of range, panning to the right
  45649. val = ordinalPositions[i];
  45650. }
  45651. else { // split it up
  45652. i = Math.floor(val);
  45653. distance = val - i; // the decimal
  45654. }
  45655. // Loop down along the ordinal positions. When the linear
  45656. // equivalent of i matches an ordinal position, interpolate
  45657. // between the left and right values.
  45658. }
  45659. else {
  45660. while (i--) {
  45661. linearEquivalentLeft =
  45662. (ordinalSlope * i) + ordinalOffset;
  45663. if (val >= linearEquivalentLeft) {
  45664. linearEquivalentRight =
  45665. (ordinalSlope *
  45666. (i + 1)) +
  45667. ordinalOffset;
  45668. // something between 0 and 1
  45669. distance = (val - linearEquivalentLeft) /
  45670. (linearEquivalentRight - linearEquivalentLeft);
  45671. break;
  45672. }
  45673. }
  45674. }
  45675. // If the index is within the range of the ordinal positions,
  45676. // return the associated or interpolated value. If not, just
  45677. // return the value.
  45678. return (typeof distance !== 'undefined' &&
  45679. typeof ordinalPositions[i] !== 'undefined' ?
  45680. ordinalPositions[i] + (distance ?
  45681. distance *
  45682. (ordinalPositions[i + 1] - ordinalPositions[i]) :
  45683. 0) :
  45684. val);
  45685. }
  45686. return ret;
  45687. };
  45688. /**
  45689. * Translate from a linear axis value to the corresponding ordinal axis
  45690. * position. If there are no gaps in the ordinal axis this will be the
  45691. * same. The translated value is the value that the point would have if
  45692. * the axis were linear, using the same min and max.
  45693. *
  45694. * @private
  45695. * @function Highcharts.Axis#val2lin
  45696. *
  45697. * @param {number} val
  45698. * The axis value.
  45699. *
  45700. * @param {boolean} [toIndex]
  45701. * Whether to return the index in the ordinalPositions or the new value.
  45702. *
  45703. * @return {number}
  45704. */
  45705. axisProto.val2lin = function (val, toIndex) {
  45706. var axis = this, ordinal = axis.ordinal, ordinalPositions = ordinal.positions, ret;
  45707. if (!ordinalPositions) {
  45708. ret = val;
  45709. }
  45710. else {
  45711. var ordinalLength = ordinalPositions.length, i, distance, ordinalIndex;
  45712. // first look for an exact match in the ordinalpositions array
  45713. i = ordinalLength;
  45714. while (i--) {
  45715. if (ordinalPositions[i] === val) {
  45716. ordinalIndex = i;
  45717. break;
  45718. }
  45719. }
  45720. // if that failed, find the intermediate position between the
  45721. // two nearest values
  45722. i = ordinalLength - 1;
  45723. while (i--) {
  45724. if (val > ordinalPositions[i] || i === 0) { // interpolate
  45725. // something between 0 and 1
  45726. distance = (val - ordinalPositions[i]) /
  45727. (ordinalPositions[i + 1] - ordinalPositions[i]);
  45728. ordinalIndex = i + distance;
  45729. break;
  45730. }
  45731. }
  45732. ret = toIndex ?
  45733. ordinalIndex :
  45734. ordinal.slope *
  45735. (ordinalIndex || 0) +
  45736. ordinal.offset;
  45737. }
  45738. return ret;
  45739. };
  45740. // Record this to prevent overwriting by broken-axis module (#5979)
  45741. axisProto.ordinal2lin = axisProto.val2lin;
  45742. /* eslint-disable no-invalid-this */
  45743. addEvent(AxisClass, 'afterInit', function () {
  45744. var axis = this;
  45745. if (!axis.ordinal) {
  45746. axis.ordinal = new OrdinalAxis.Composition(axis);
  45747. }
  45748. });
  45749. addEvent(AxisClass, 'foundExtremes', function () {
  45750. var axis = this;
  45751. if (axis.isXAxis &&
  45752. defined(axis.options.overscroll) &&
  45753. axis.max === axis.dataMax &&
  45754. (
  45755. // Panning is an execption. We don't want to apply
  45756. // overscroll when panning over the dataMax
  45757. !axis.chart.mouseIsDown ||
  45758. axis.isInternal) && (
  45759. // Scrollbar buttons are the other execption:
  45760. !axis.eventArgs ||
  45761. axis.eventArgs && axis.eventArgs.trigger !== 'navigator')) {
  45762. axis.max += axis.options.overscroll;
  45763. // Live data and buttons require translation for the min:
  45764. if (!axis.isInternal && defined(axis.userMin)) {
  45765. axis.min += axis.options.overscroll;
  45766. }
  45767. }
  45768. });
  45769. // For ordinal axis, that loads data async, redraw axis after data is
  45770. // loaded. If we don't do that, axis will have the same extremes as
  45771. // previously, but ordinal positions won't be calculated. See #10290
  45772. addEvent(AxisClass, 'afterSetScale', function () {
  45773. var axis = this;
  45774. if (axis.horiz && !axis.isDirty) {
  45775. axis.isDirty = axis.isOrdinal &&
  45776. axis.chart.navigator &&
  45777. !axis.chart.navigator.adaptToUpdatedData;
  45778. }
  45779. });
  45780. addEvent(AxisClass, 'initialAxisTranslation', function () {
  45781. var axis = this;
  45782. if (axis.ordinal) {
  45783. axis.ordinal.beforeSetTickPositions();
  45784. axis.tickInterval = axis.ordinal.postProcessTickInterval(axis.tickInterval);
  45785. }
  45786. });
  45787. // Extending the Chart.pan method for ordinal axes
  45788. addEvent(ChartClass, 'pan', function (e) {
  45789. var chart = this, xAxis = chart.xAxis[0], overscroll = xAxis.options.overscroll, chartX = e.originalEvent.chartX, panning = chart.options.chart &&
  45790. chart.options.chart.panning, runBase = false;
  45791. if (panning &&
  45792. panning.type !== 'y' &&
  45793. xAxis.options.ordinal &&
  45794. xAxis.series.length) {
  45795. var mouseDownX = chart.mouseDownX, extremes = xAxis.getExtremes(), dataMax = extremes.dataMax, min = extremes.min, max = extremes.max, trimmedRange, hoverPoints = chart.hoverPoints, closestPointRange = (xAxis.closestPointRange ||
  45796. (xAxis.ordinal && xAxis.ordinal.overscrollPointsRange)), pointPixelWidth = (xAxis.translationSlope *
  45797. (xAxis.ordinal.slope || closestPointRange)),
  45798. // how many ordinal units did we move?
  45799. movedUnits = (mouseDownX - chartX) / pointPixelWidth,
  45800. // get index of all the chart's points
  45801. extendedAxis = { ordinal: { positions: xAxis.ordinal.getExtendedPositions() } }, ordinalPositions, searchAxisLeft, lin2val = xAxis.lin2val, val2lin = xAxis.val2lin, searchAxisRight;
  45802. // we have an ordinal axis, but the data is equally spaced
  45803. if (!extendedAxis.ordinal.positions) {
  45804. runBase = true;
  45805. }
  45806. else if (Math.abs(movedUnits) > 1) {
  45807. // Remove active points for shared tooltip
  45808. if (hoverPoints) {
  45809. hoverPoints.forEach(function (point) {
  45810. point.setState();
  45811. });
  45812. }
  45813. if (movedUnits < 0) {
  45814. searchAxisLeft = extendedAxis;
  45815. searchAxisRight = xAxis.ordinal.positions ? xAxis : extendedAxis;
  45816. }
  45817. else {
  45818. searchAxisLeft = xAxis.ordinal.positions ? xAxis : extendedAxis;
  45819. searchAxisRight = extendedAxis;
  45820. }
  45821. // In grouped data series, the last ordinal position
  45822. // represents the grouped data, which is to the left of the
  45823. // real data max. If we don't compensate for this, we will
  45824. // be allowed to pan grouped data series passed the right of
  45825. // the plot area.
  45826. ordinalPositions = searchAxisRight.ordinal.positions;
  45827. if (dataMax >
  45828. ordinalPositions[ordinalPositions.length - 1]) {
  45829. ordinalPositions.push(dataMax);
  45830. }
  45831. // Get the new min and max values by getting the ordinal
  45832. // index for the current extreme, then add the moved units
  45833. // and translate back to values. This happens on the
  45834. // extended ordinal positions if the new position is out of
  45835. // range, else it happens on the current x axis which is
  45836. // smaller and faster.
  45837. chart.fixedRange = max - min;
  45838. trimmedRange = xAxis.navigatorAxis.toFixedRange(null, null, lin2val.apply(searchAxisLeft, [
  45839. val2lin.apply(searchAxisLeft, [min, true]) + movedUnits,
  45840. true // translate from index
  45841. ]), lin2val.apply(searchAxisRight, [
  45842. val2lin.apply(searchAxisRight, [max, true]) + movedUnits,
  45843. true // translate from index
  45844. ]));
  45845. // Apply it if it is within the available data range
  45846. if (trimmedRange.min >= Math.min(extremes.dataMin, min) &&
  45847. trimmedRange.max <= Math.max(dataMax, max) + overscroll) {
  45848. xAxis.setExtremes(trimmedRange.min, trimmedRange.max, true, false, { trigger: 'pan' });
  45849. }
  45850. chart.mouseDownX = chartX; // set new reference for next run
  45851. css(chart.container, { cursor: 'move' });
  45852. }
  45853. }
  45854. else {
  45855. runBase = true;
  45856. }
  45857. // revert to the linear chart.pan version
  45858. if (runBase || (panning && /y/.test(panning.type))) {
  45859. if (overscroll) {
  45860. xAxis.max = xAxis.dataMax + overscroll;
  45861. }
  45862. }
  45863. else {
  45864. e.preventDefault();
  45865. }
  45866. });
  45867. addEvent(SeriesClass, 'updatedData', function () {
  45868. var xAxis = this.xAxis;
  45869. // Destroy the extended ordinal index on updated data
  45870. if (xAxis && xAxis.options.ordinal) {
  45871. delete xAxis.ordinal.index;
  45872. }
  45873. });
  45874. /* eslint-enable no-invalid-this */
  45875. }
  45876. OrdinalAxis.compose = compose;
  45877. })(OrdinalAxis || (OrdinalAxis = {}));
  45878. OrdinalAxis.compose(Axis, Chart, Series); // @todo move to StockChart, remove from master
  45879. return OrdinalAxis;
  45880. });
  45881. _registerModule(_modules, 'modules/broken-axis.src.js', [_modules['parts/Axis.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['parts/Stacking.js']], function (Axis, H, U, StackItem) {
  45882. /* *
  45883. *
  45884. * (c) 2009-2020 Torstein Honsi
  45885. *
  45886. * License: www.highcharts.com/license
  45887. *
  45888. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  45889. *
  45890. * */
  45891. var addEvent = U.addEvent, find = U.find, fireEvent = U.fireEvent, isArray = U.isArray, isNumber = U.isNumber, pick = U.pick;
  45892. var Series = H.Series;
  45893. /* eslint-disable valid-jsdoc */
  45894. /**
  45895. * Provides support for broken axes.
  45896. * @private
  45897. * @class
  45898. */
  45899. var BrokenAxisAdditions = /** @class */ (function () {
  45900. /* *
  45901. *
  45902. * Constructors
  45903. *
  45904. * */
  45905. function BrokenAxisAdditions(axis) {
  45906. this.hasBreaks = false;
  45907. this.axis = axis;
  45908. }
  45909. /* *
  45910. *
  45911. * Static Functions
  45912. *
  45913. * */
  45914. /**
  45915. * @private
  45916. */
  45917. BrokenAxisAdditions.isInBreak = function (brk, val) {
  45918. var ret, repeat = brk.repeat || Infinity, from = brk.from, length = brk.to - brk.from, test = (val >= from ?
  45919. (val - from) % repeat :
  45920. repeat - ((from - val) % repeat));
  45921. if (!brk.inclusive) {
  45922. ret = test < length && test !== 0;
  45923. }
  45924. else {
  45925. ret = test <= length;
  45926. }
  45927. return ret;
  45928. };
  45929. /**
  45930. * @private
  45931. */
  45932. BrokenAxisAdditions.lin2Val = function (val) {
  45933. var axis = this;
  45934. var brokenAxis = axis.brokenAxis;
  45935. var breakArray = brokenAxis && brokenAxis.breakArray;
  45936. if (!breakArray) {
  45937. return val;
  45938. }
  45939. var nval = val, brk, i;
  45940. for (i = 0; i < breakArray.length; i++) {
  45941. brk = breakArray[i];
  45942. if (brk.from >= nval) {
  45943. break;
  45944. }
  45945. else if (brk.to < nval) {
  45946. nval += brk.len;
  45947. }
  45948. else if (BrokenAxisAdditions.isInBreak(brk, nval)) {
  45949. nval += brk.len;
  45950. }
  45951. }
  45952. return nval;
  45953. };
  45954. /**
  45955. * @private
  45956. */
  45957. BrokenAxisAdditions.val2Lin = function (val) {
  45958. var axis = this;
  45959. var brokenAxis = axis.brokenAxis;
  45960. var breakArray = brokenAxis && brokenAxis.breakArray;
  45961. if (!breakArray) {
  45962. return val;
  45963. }
  45964. var nval = val, brk, i;
  45965. for (i = 0; i < breakArray.length; i++) {
  45966. brk = breakArray[i];
  45967. if (brk.to <= val) {
  45968. nval -= brk.len;
  45969. }
  45970. else if (brk.from >= val) {
  45971. break;
  45972. }
  45973. else if (BrokenAxisAdditions.isInBreak(brk, val)) {
  45974. nval -= (val - brk.from);
  45975. break;
  45976. }
  45977. }
  45978. return nval;
  45979. };
  45980. /* *
  45981. *
  45982. * Functions
  45983. *
  45984. * */
  45985. /**
  45986. * Returns the first break found where the x is larger then break.from and
  45987. * smaller then break.to.
  45988. *
  45989. * @param {number} x
  45990. * The number which should be within a break.
  45991. *
  45992. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  45993. * The array of breaks to search within.
  45994. *
  45995. * @return {Highcharts.XAxisBreaksOptions|undefined}
  45996. * Returns the first break found that matches, returns false if no break is
  45997. * found.
  45998. */
  45999. BrokenAxisAdditions.prototype.findBreakAt = function (x, breaks) {
  46000. return find(breaks, function (b) {
  46001. return b.from < x && x < b.to;
  46002. });
  46003. };
  46004. /**
  46005. * @private
  46006. */
  46007. BrokenAxisAdditions.prototype.isInAnyBreak = function (val, testKeep) {
  46008. var brokenAxis = this;
  46009. var axis = brokenAxis.axis;
  46010. var breaks = axis.options.breaks, i = breaks && breaks.length, inbrk, keep, ret;
  46011. if (i) {
  46012. while (i--) {
  46013. if (BrokenAxisAdditions.isInBreak(breaks[i], val)) {
  46014. inbrk = true;
  46015. if (!keep) {
  46016. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  46017. }
  46018. }
  46019. }
  46020. if (inbrk && testKeep) {
  46021. ret = inbrk && !keep;
  46022. }
  46023. else {
  46024. ret = inbrk;
  46025. }
  46026. }
  46027. return ret;
  46028. };
  46029. /**
  46030. * Dynamically set or unset breaks in an axis. This function in lighter than
  46031. * usin Axis.update, and it also preserves animation.
  46032. *
  46033. * @private
  46034. * @function Highcharts.Axis#setBreaks
  46035. *
  46036. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  46037. * The breaks to add. When `undefined` it removes existing breaks.
  46038. *
  46039. * @param {boolean} [redraw=true]
  46040. * Whether to redraw the chart immediately.
  46041. *
  46042. * @return {void}
  46043. */
  46044. BrokenAxisAdditions.prototype.setBreaks = function (breaks, redraw) {
  46045. var brokenAxis = this;
  46046. var axis = brokenAxis.axis;
  46047. var hasBreaks = (isArray(breaks) && !!breaks.length);
  46048. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  46049. brokenAxis.hasBreaks = hasBreaks;
  46050. axis.options.breaks = axis.userOptions.breaks = breaks;
  46051. axis.forceRedraw = true; // Force recalculation in setScale
  46052. // Recalculate series related to the axis.
  46053. axis.series.forEach(function (series) {
  46054. series.isDirty = true;
  46055. });
  46056. if (!hasBreaks && axis.val2lin === BrokenAxisAdditions.val2Lin) {
  46057. // Revert to prototype functions
  46058. delete axis.val2lin;
  46059. delete axis.lin2val;
  46060. }
  46061. if (hasBreaks) {
  46062. axis.userOptions.ordinal = false;
  46063. axis.lin2val = BrokenAxisAdditions.lin2Val;
  46064. axis.val2lin = BrokenAxisAdditions.val2Lin;
  46065. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  46066. // If trying to set extremes inside a break, extend min to
  46067. // after, and max to before the break ( #3857 )
  46068. if (brokenAxis.hasBreaks) {
  46069. var axisBreak, breaks = this.options.breaks;
  46070. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks))) {
  46071. newMin = axisBreak.to;
  46072. }
  46073. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks))) {
  46074. newMax = axisBreak.from;
  46075. }
  46076. // If both min and max is within the same break.
  46077. if (newMax < newMin) {
  46078. newMax = newMin;
  46079. }
  46080. }
  46081. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  46082. };
  46083. axis.setAxisTranslation = function (saveOld) {
  46084. Axis.prototype.setAxisTranslation.call(this, saveOld);
  46085. brokenAxis.unitLength = null;
  46086. if (brokenAxis.hasBreaks) {
  46087. var breaks = axis.options.breaks || [],
  46088. // Temporary one:
  46089. breakArrayT = [], breakArray = [], length = 0, inBrk, repeat, min = axis.userMin || axis.min, max = axis.userMax || axis.max, pointRangePadding = pick(axis.pointRangePadding, 0), start, i;
  46090. // Min & max check (#4247)
  46091. breaks.forEach(function (brk) {
  46092. repeat = brk.repeat || Infinity;
  46093. if (BrokenAxisAdditions.isInBreak(brk, min)) {
  46094. min +=
  46095. (brk.to % repeat) -
  46096. (min % repeat);
  46097. }
  46098. if (BrokenAxisAdditions.isInBreak(brk, max)) {
  46099. max -=
  46100. (max % repeat) -
  46101. (brk.from % repeat);
  46102. }
  46103. });
  46104. // Construct an array holding all breaks in the axis
  46105. breaks.forEach(function (brk) {
  46106. start = brk.from;
  46107. repeat = brk.repeat || Infinity;
  46108. while (start - repeat > min) {
  46109. start -= repeat;
  46110. }
  46111. while (start < min) {
  46112. start += repeat;
  46113. }
  46114. for (i = start; i < max; i += repeat) {
  46115. breakArrayT.push({
  46116. value: i,
  46117. move: 'in'
  46118. });
  46119. breakArrayT.push({
  46120. value: i + (brk.to - brk.from),
  46121. move: 'out',
  46122. size: brk.breakSize
  46123. });
  46124. }
  46125. });
  46126. breakArrayT.sort(function (a, b) {
  46127. return ((a.value === b.value) ?
  46128. ((a.move === 'in' ? 0 : 1) -
  46129. (b.move === 'in' ? 0 : 1)) :
  46130. a.value - b.value);
  46131. });
  46132. // Simplify the breaks
  46133. inBrk = 0;
  46134. start = min;
  46135. breakArrayT.forEach(function (brk) {
  46136. inBrk += (brk.move === 'in' ? 1 : -1);
  46137. if (inBrk === 1 && brk.move === 'in') {
  46138. start = brk.value;
  46139. }
  46140. if (inBrk === 0) {
  46141. breakArray.push({
  46142. from: start,
  46143. to: brk.value,
  46144. len: brk.value - start - (brk.size || 0)
  46145. });
  46146. length += brk.value - start - (brk.size || 0);
  46147. }
  46148. });
  46149. /**
  46150. * HC <= 8 backwards compatibility, used by demo samples.
  46151. * @deprecated
  46152. * @private
  46153. * @requires modules/broken-axis
  46154. */
  46155. axis.breakArray = brokenAxis.breakArray = breakArray;
  46156. // Used with staticScale, and below the actual axis length,
  46157. // when breaks are substracted.
  46158. brokenAxis.unitLength = max - min - length + pointRangePadding;
  46159. fireEvent(axis, 'afterBreaks');
  46160. if (axis.staticScale) {
  46161. axis.transA = axis.staticScale;
  46162. }
  46163. else if (brokenAxis.unitLength) {
  46164. axis.transA *=
  46165. (max - axis.min + pointRangePadding) /
  46166. brokenAxis.unitLength;
  46167. }
  46168. if (pointRangePadding) {
  46169. axis.minPixelPadding =
  46170. axis.transA * axis.minPointOffset;
  46171. }
  46172. axis.min = min;
  46173. axis.max = max;
  46174. }
  46175. };
  46176. }
  46177. if (pick(redraw, true)) {
  46178. axis.chart.redraw();
  46179. }
  46180. };
  46181. return BrokenAxisAdditions;
  46182. }());
  46183. /**
  46184. * Axis with support of broken data rows.
  46185. * @private
  46186. * @class
  46187. */
  46188. var BrokenAxis = /** @class */ (function () {
  46189. function BrokenAxis() {
  46190. }
  46191. /**
  46192. * Adds support for broken axes.
  46193. * @private
  46194. */
  46195. BrokenAxis.compose = function (AxisClass, SeriesClass) {
  46196. AxisClass.keepProps.push('brokenAxis');
  46197. var seriesProto = Series.prototype;
  46198. /**
  46199. * @private
  46200. */
  46201. seriesProto.drawBreaks = function (axis, keys) {
  46202. var series = this, points = series.points, breaks, threshold, eventName, y;
  46203. if (axis && // #5950
  46204. axis.brokenAxis &&
  46205. axis.brokenAxis.hasBreaks) {
  46206. var brokenAxis_1 = axis.brokenAxis;
  46207. keys.forEach(function (key) {
  46208. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  46209. threshold = axis.isXAxis ?
  46210. axis.min :
  46211. pick(series.options.threshold, axis.min);
  46212. points.forEach(function (point) {
  46213. y = pick(point['stack' + key.toUpperCase()], point[key]);
  46214. breaks.forEach(function (brk) {
  46215. if (isNumber(threshold) && isNumber(y)) {
  46216. eventName = false;
  46217. if ((threshold < brk.from && y > brk.to) ||
  46218. (threshold > brk.from && y < brk.from)) {
  46219. eventName = 'pointBreak';
  46220. }
  46221. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  46222. (threshold > brk.from && y > brk.to && y < brk.from)) {
  46223. eventName = 'pointInBreak';
  46224. }
  46225. if (eventName) {
  46226. fireEvent(axis, eventName, { point: point, brk: brk });
  46227. }
  46228. }
  46229. });
  46230. });
  46231. });
  46232. }
  46233. };
  46234. /**
  46235. * Extend getGraphPath by identifying gaps in the data so that we can
  46236. * draw a gap in the line or area. This was moved from ordinal axis
  46237. * module to broken axis module as of #5045.
  46238. *
  46239. * @private
  46240. * @function Highcharts.Series#gappedPath
  46241. *
  46242. * @return {Highcharts.SVGPathArray}
  46243. * Gapped path
  46244. */
  46245. seriesProto.gappedPath = function () {
  46246. var currentDataGrouping = this.currentDataGrouping, groupingSize = currentDataGrouping && currentDataGrouping.gapSize, gapSize = this.options.gapSize, points = this.points.slice(), i = points.length - 1, yAxis = this.yAxis, stack;
  46247. /**
  46248. * Defines when to display a gap in the graph, together with the
  46249. * [gapUnit](plotOptions.series.gapUnit) option.
  46250. *
  46251. * In case when `dataGrouping` is enabled, points can be grouped
  46252. * into a larger time span. This can make the grouped points to have
  46253. * a greater distance than the absolute value of `gapSize` property,
  46254. * which will result in disappearing graph completely. To prevent
  46255. * this situation the mentioned distance between grouped points is
  46256. * used instead of previously defined `gapSize`.
  46257. *
  46258. * In practice, this option is most often used to visualize gaps in
  46259. * time series. In a stock chart, intraday data is available for
  46260. * daytime hours, while gaps will appear in nights and weekends.
  46261. *
  46262. * @see [gapUnit](plotOptions.series.gapUnit)
  46263. * @see [xAxis.breaks](#xAxis.breaks)
  46264. *
  46265. * @sample {highstock} stock/plotoptions/series-gapsize/
  46266. * Setting the gap size to 2 introduces gaps for weekends
  46267. * in daily datasets.
  46268. *
  46269. * @type {number}
  46270. * @default 0
  46271. * @product highstock
  46272. * @requires modules/broken-axis
  46273. * @apioption plotOptions.series.gapSize
  46274. */
  46275. /**
  46276. * Together with [gapSize](plotOptions.series.gapSize), this option
  46277. * defines where to draw gaps in the graph.
  46278. *
  46279. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  46280. * means that if the distance between two points is greater than
  46281. * 5 times that of the two closest points, the graph will be broken.
  46282. *
  46283. * When the `gapUnit` is `"value"`, the gap is based on absolute
  46284. * axis values, which on a datetime axis is milliseconds. This also
  46285. * applies to the navigator series that inherits gap options from
  46286. * the base series.
  46287. *
  46288. * @see [gapSize](plotOptions.series.gapSize)
  46289. *
  46290. * @type {string}
  46291. * @default relative
  46292. * @since 5.0.13
  46293. * @product highstock
  46294. * @validvalue ["relative", "value"]
  46295. * @requires modules/broken-axis
  46296. * @apioption plotOptions.series.gapUnit
  46297. */
  46298. if (gapSize && i > 0) { // #5008
  46299. // Gap unit is relative
  46300. if (this.options.gapUnit !== 'value') {
  46301. gapSize *= this.basePointRange;
  46302. }
  46303. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  46304. if (groupingSize &&
  46305. groupingSize > gapSize &&
  46306. // Except when DG is forced (e.g. from other series)
  46307. // and has lower granularity than actual points (#11351)
  46308. groupingSize >= this.basePointRange) {
  46309. gapSize = groupingSize;
  46310. }
  46311. // extension for ordinal breaks
  46312. var current = void 0, next = void 0;
  46313. while (i--) {
  46314. // Reassign next if it is not visible
  46315. if (!(next && next.visible !== false)) {
  46316. next = points[i + 1];
  46317. }
  46318. current = points[i];
  46319. // Skip iteration if one of the points is not visible
  46320. if (next.visible === false || current.visible === false) {
  46321. continue;
  46322. }
  46323. if (next.x - current.x > gapSize) {
  46324. var xRange = (current.x + next.x) / 2;
  46325. points.splice(// insert after this one
  46326. i + 1, 0, {
  46327. isNull: true,
  46328. x: xRange
  46329. });
  46330. // For stacked chart generate empty stack items, #6546
  46331. if (yAxis.stacking && this.options.stacking) {
  46332. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  46333. new StackItem(yAxis, yAxis.options
  46334. .stackLabels, false, xRange, this.stack);
  46335. stack.total = 0;
  46336. }
  46337. }
  46338. // Assign current to next for the upcoming iteration
  46339. next = current;
  46340. }
  46341. }
  46342. // Call base method
  46343. return this.getGraphPath(points);
  46344. };
  46345. /* eslint-disable no-invalid-this */
  46346. addEvent(AxisClass, 'init', function () {
  46347. var axis = this;
  46348. if (!axis.brokenAxis) {
  46349. axis.brokenAxis = new BrokenAxisAdditions(axis);
  46350. }
  46351. });
  46352. addEvent(AxisClass, 'afterInit', function () {
  46353. if (typeof this.brokenAxis !== 'undefined') {
  46354. this.brokenAxis.setBreaks(this.options.breaks, false);
  46355. }
  46356. });
  46357. addEvent(AxisClass, 'afterSetTickPositions', function () {
  46358. var axis = this;
  46359. var brokenAxis = axis.brokenAxis;
  46360. if (brokenAxis &&
  46361. brokenAxis.hasBreaks) {
  46362. var tickPositions = this.tickPositions, info = this.tickPositions.info, newPositions = [], i;
  46363. for (i = 0; i < tickPositions.length; i++) {
  46364. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  46365. newPositions.push(tickPositions[i]);
  46366. }
  46367. }
  46368. this.tickPositions = newPositions;
  46369. this.tickPositions.info = info;
  46370. }
  46371. });
  46372. // Force Axis to be not-ordinal when breaks are defined
  46373. addEvent(AxisClass, 'afterSetOptions', function () {
  46374. if (this.brokenAxis && this.brokenAxis.hasBreaks) {
  46375. this.options.ordinal = false;
  46376. }
  46377. });
  46378. addEvent(SeriesClass, 'afterGeneratePoints', function () {
  46379. var _a = this, isDirty = _a.isDirty, connectNulls = _a.options.connectNulls, points = _a.points, xAxis = _a.xAxis, yAxis = _a.yAxis;
  46380. // Set, or reset visibility of the points. Axis.setBreaks marks the
  46381. // series as isDirty
  46382. if (isDirty) {
  46383. var i = points.length;
  46384. while (i--) {
  46385. var point = points[i];
  46386. // Respect nulls inside the break (#4275)
  46387. var nullGap = point.y === null && connectNulls === false;
  46388. var isPointInBreak = (!nullGap && ((xAxis &&
  46389. xAxis.brokenAxis &&
  46390. xAxis.brokenAxis.isInAnyBreak(point.x, true)) || (yAxis &&
  46391. yAxis.brokenAxis &&
  46392. yAxis.brokenAxis.isInAnyBreak(point.y, true))));
  46393. // Set point.visible if in any break.
  46394. // If not in break, reset visible to original value.
  46395. point.visible = isPointInBreak ?
  46396. false :
  46397. point.options.visible !== false;
  46398. }
  46399. }
  46400. });
  46401. addEvent(SeriesClass, 'afterRender', function drawPointsWrapped() {
  46402. this.drawBreaks(this.xAxis, ['x']);
  46403. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  46404. });
  46405. };
  46406. return BrokenAxis;
  46407. }());
  46408. BrokenAxis.compose(Axis, Series); // @todo remove automatism
  46409. return BrokenAxis;
  46410. });
  46411. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [], function () {
  46412. });
  46413. _registerModule(_modules, 'parts/DataGrouping.js', [_modules['parts/DateTimeAxis.js'], _modules['parts/Globals.js'], _modules['parts/Options.js'], _modules['parts/Point.js'], _modules['parts/Tooltip.js'], _modules['parts/Utilities.js']], function (DateTimeAxis, H, O, Point, Tooltip, U) {
  46414. /* *
  46415. *
  46416. * (c) 2010-2020 Torstein Honsi
  46417. *
  46418. * License: www.highcharts.com/license
  46419. *
  46420. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  46421. *
  46422. * */
  46423. /**
  46424. * @typedef {"average"|"averages"|"open"|"high"|"low"|"close"|"sum"} Highcharts.DataGroupingApproximationValue
  46425. */
  46426. /**
  46427. * @interface Highcharts.DataGroupingInfoObject
  46428. */ /**
  46429. * @name Highcharts.DataGroupingInfoObject#length
  46430. * @type {number}
  46431. */ /**
  46432. * @name Highcharts.DataGroupingInfoObject#options
  46433. * @type {Highcharts.SeriesOptionsType|undefined}
  46434. */ /**
  46435. * @name Highcharts.DataGroupingInfoObject#start
  46436. * @type {number}
  46437. */
  46438. ''; // detach doclets above
  46439. var defaultOptions = O.defaultOptions;
  46440. var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, correctFloat = U.correctFloat, defined = U.defined, error = U.error, extend = U.extend, format = U.format, isNumber = U.isNumber, merge = U.merge, pick = U.pick;
  46441. var Axis = H.Axis, Series = H.Series;
  46442. /* ************************************************************************** *
  46443. * Start data grouping module *
  46444. * ************************************************************************** */
  46445. /* eslint-disable no-invalid-this, valid-jsdoc */
  46446. /**
  46447. * Define the available approximation types. The data grouping
  46448. * approximations takes an array or numbers as the first parameter. In case
  46449. * of ohlc, four arrays are sent in as four parameters. Each array consists
  46450. * only of numbers. In case null values belong to the group, the property
  46451. * .hasNulls will be set to true on the array.
  46452. *
  46453. * @product highstock
  46454. *
  46455. * @private
  46456. * @name Highcharts.approximations
  46457. * @type {Highcharts.Dictionary<Function>}
  46458. */
  46459. var approximations = H.approximations = {
  46460. sum: function (arr) {
  46461. var len = arr.length, ret;
  46462. // 1. it consists of nulls exclusive
  46463. if (!len && arr.hasNulls) {
  46464. ret = null;
  46465. // 2. it has a length and real values
  46466. }
  46467. else if (len) {
  46468. ret = 0;
  46469. while (len--) {
  46470. ret += arr[len];
  46471. }
  46472. }
  46473. // 3. it has zero length, so just return undefined
  46474. // => doNothing()
  46475. return ret;
  46476. },
  46477. average: function (arr) {
  46478. var len = arr.length, ret = approximations.sum(arr);
  46479. // If we have a number, return it divided by the length. If not,
  46480. // return null or undefined based on what the sum method finds.
  46481. if (isNumber(ret) && len) {
  46482. ret = correctFloat(ret / len);
  46483. }
  46484. return ret;
  46485. },
  46486. // The same as average, but for series with multiple values, like area
  46487. // ranges.
  46488. averages: function () {
  46489. var ret = [];
  46490. [].forEach.call(arguments, function (arr) {
  46491. ret.push(approximations.average(arr));
  46492. });
  46493. // Return undefined when first elem. is undefined and let
  46494. // sum method handle null (#7377)
  46495. return typeof ret[0] === 'undefined' ? void 0 : ret;
  46496. },
  46497. open: function (arr) {
  46498. return arr.length ? arr[0] : (arr.hasNulls ? null : void 0);
  46499. },
  46500. high: function (arr) {
  46501. return arr.length ?
  46502. arrayMax(arr) :
  46503. (arr.hasNulls ? null : void 0);
  46504. },
  46505. low: function (arr) {
  46506. return arr.length ?
  46507. arrayMin(arr) :
  46508. (arr.hasNulls ? null : void 0);
  46509. },
  46510. close: function (arr) {
  46511. return arr.length ?
  46512. arr[arr.length - 1] :
  46513. (arr.hasNulls ? null : void 0);
  46514. },
  46515. // ohlc and range are special cases where a multidimensional array is
  46516. // input and an array is output
  46517. ohlc: function (open, high, low, close) {
  46518. open = approximations.open(open);
  46519. high = approximations.high(high);
  46520. low = approximations.low(low);
  46521. close = approximations.close(close);
  46522. if (isNumber(open) ||
  46523. isNumber(high) ||
  46524. isNumber(low) ||
  46525. isNumber(close)) {
  46526. return [open, high, low, close];
  46527. }
  46528. // else, return is undefined
  46529. },
  46530. range: function (low, high) {
  46531. low = approximations.low(low);
  46532. high = approximations.high(high);
  46533. if (isNumber(low) || isNumber(high)) {
  46534. return [low, high];
  46535. }
  46536. if (low === null && high === null) {
  46537. return null;
  46538. }
  46539. // else, return is undefined
  46540. }
  46541. };
  46542. var groupData = function (xData, yData, groupPositions, approximation) {
  46543. var series = this, data = series.data, dataOptions = series.options && series.options.data, groupedXData = [], groupedYData = [], groupMap = [], dataLength = xData.length, pointX, pointY, groupedY,
  46544. // when grouping the fake extended axis for panning,
  46545. // we don't need to consider y
  46546. handleYData = !!yData, values = [], approximationFn, pointArrayMap = series.pointArrayMap, pointArrayMapLength = pointArrayMap && pointArrayMap.length, extendedPointArrayMap = ['x'].concat(pointArrayMap || ['y']), pos = 0, start = 0, valuesLen, i, j;
  46547. /**
  46548. * @private
  46549. */
  46550. function getApproximation(approx) {
  46551. if (typeof approx === 'function') {
  46552. return approx;
  46553. }
  46554. if (approximations[approx]) {
  46555. return approximations[approx];
  46556. }
  46557. return approximations[(series.getDGApproximation && series.getDGApproximation()) ||
  46558. 'average'];
  46559. }
  46560. approximationFn = getApproximation(approximation);
  46561. // Calculate values array size from pointArrayMap length
  46562. if (pointArrayMapLength) {
  46563. pointArrayMap.forEach(function () {
  46564. values.push([]);
  46565. });
  46566. }
  46567. else {
  46568. values.push([]);
  46569. }
  46570. valuesLen = pointArrayMapLength || 1;
  46571. // Start with the first point within the X axis range (#2696)
  46572. for (i = 0; i <= dataLength; i++) {
  46573. if (xData[i] >= groupPositions[0]) {
  46574. break;
  46575. }
  46576. }
  46577. for (i; i <= dataLength; i++) {
  46578. // when a new group is entered, summarize and initialize
  46579. // the previous group
  46580. while ((typeof groupPositions[pos + 1] !== 'undefined' &&
  46581. xData[i] >= groupPositions[pos + 1]) ||
  46582. i === dataLength) { // get the last group
  46583. // get group x and y
  46584. pointX = groupPositions[pos];
  46585. series.dataGroupInfo = {
  46586. start: series.cropStart + start,
  46587. length: values[0].length
  46588. };
  46589. groupedY = approximationFn.apply(series, values);
  46590. // By default, let options of the first grouped point be passed over
  46591. // to the grouped point. This allows preserving properties like
  46592. // `name` and `color` or custom properties. Implementers can
  46593. // override this from the approximation function, where they can
  46594. // write custom options to `this.dataGroupInfo.options`.
  46595. if (series.pointClass && !defined(series.dataGroupInfo.options)) {
  46596. // Convert numbers and arrays into objects
  46597. series.dataGroupInfo.options = merge(series.pointClass.prototype
  46598. .optionsToObject.call({ series: series }, series.options.data[series.cropStart + start]));
  46599. // Make sure the raw data (x, y, open, high etc) is not copied
  46600. // over and overwriting approximated data.
  46601. extendedPointArrayMap.forEach(function (key) {
  46602. delete series.dataGroupInfo.options[key];
  46603. });
  46604. }
  46605. // push the grouped data
  46606. if (typeof groupedY !== 'undefined') {
  46607. groupedXData.push(pointX);
  46608. groupedYData.push(groupedY);
  46609. groupMap.push(series.dataGroupInfo);
  46610. }
  46611. // reset the aggregate arrays
  46612. start = i;
  46613. for (j = 0; j < valuesLen; j++) {
  46614. values[j].length = 0; // faster than values[j] = []
  46615. values[j].hasNulls = false;
  46616. }
  46617. // Advance on the group positions
  46618. pos += 1;
  46619. // don't loop beyond the last group
  46620. if (i === dataLength) {
  46621. break;
  46622. }
  46623. }
  46624. // break out
  46625. if (i === dataLength) {
  46626. break;
  46627. }
  46628. // for each raw data point, push it to an array that contains all values
  46629. // for this specific group
  46630. if (pointArrayMap) {
  46631. var index = series.cropStart + i, point = (data && data[index]) ||
  46632. series.pointClass.prototype.applyOptions.apply({
  46633. series: series
  46634. }, [dataOptions[index]]), val;
  46635. for (j = 0; j < pointArrayMapLength; j++) {
  46636. val = point[pointArrayMap[j]];
  46637. if (isNumber(val)) {
  46638. values[j].push(val);
  46639. }
  46640. else if (val === null) {
  46641. values[j].hasNulls = true;
  46642. }
  46643. }
  46644. }
  46645. else {
  46646. pointY = handleYData ? yData[i] : null;
  46647. if (isNumber(pointY)) {
  46648. values[0].push(pointY);
  46649. }
  46650. else if (pointY === null) {
  46651. values[0].hasNulls = true;
  46652. }
  46653. }
  46654. }
  46655. return {
  46656. groupedXData: groupedXData,
  46657. groupedYData: groupedYData,
  46658. groupMap: groupMap
  46659. };
  46660. };
  46661. var dataGrouping = {
  46662. approximations: approximations,
  46663. groupData: groupData
  46664. };
  46665. // -----------------------------------------------------------------------------
  46666. // The following code applies to implementation of data grouping on a Series
  46667. var seriesProto = Series.prototype, baseProcessData = seriesProto.processData, baseGeneratePoints = seriesProto.generatePoints,
  46668. /** @ignore */
  46669. commonOptions = {
  46670. // enabled: null, // (true for stock charts, false for basic),
  46671. // forced: undefined,
  46672. groupPixelWidth: 2,
  46673. // the first one is the point or start value, the second is the start
  46674. // value if we're dealing with range, the third one is the end value if
  46675. // dealing with a range
  46676. dateTimeLabelFormats: {
  46677. millisecond: [
  46678. '%A, %b %e, %H:%M:%S.%L',
  46679. '%A, %b %e, %H:%M:%S.%L',
  46680. '-%H:%M:%S.%L'
  46681. ],
  46682. second: [
  46683. '%A, %b %e, %H:%M:%S',
  46684. '%A, %b %e, %H:%M:%S',
  46685. '-%H:%M:%S'
  46686. ],
  46687. minute: [
  46688. '%A, %b %e, %H:%M',
  46689. '%A, %b %e, %H:%M',
  46690. '-%H:%M'
  46691. ],
  46692. hour: [
  46693. '%A, %b %e, %H:%M',
  46694. '%A, %b %e, %H:%M',
  46695. '-%H:%M'
  46696. ],
  46697. day: [
  46698. '%A, %b %e, %Y',
  46699. '%A, %b %e',
  46700. '-%A, %b %e, %Y'
  46701. ],
  46702. week: [
  46703. 'Week from %A, %b %e, %Y',
  46704. '%A, %b %e',
  46705. '-%A, %b %e, %Y'
  46706. ],
  46707. month: [
  46708. '%B %Y',
  46709. '%B',
  46710. '-%B %Y'
  46711. ],
  46712. year: [
  46713. '%Y',
  46714. '%Y',
  46715. '-%Y'
  46716. ]
  46717. }
  46718. // smoothed = false, // enable this for navigator series only
  46719. }, specificOptions = {
  46720. line: {},
  46721. spline: {},
  46722. area: {},
  46723. areaspline: {},
  46724. arearange: {},
  46725. column: {
  46726. groupPixelWidth: 10
  46727. },
  46728. columnrange: {
  46729. groupPixelWidth: 10
  46730. },
  46731. candlestick: {
  46732. groupPixelWidth: 10
  46733. },
  46734. ohlc: {
  46735. groupPixelWidth: 5
  46736. }
  46737. },
  46738. // units are defined in a separate array to allow complete overriding in
  46739. // case of a user option
  46740. defaultDataGroupingUnits = H.defaultDataGroupingUnits = [
  46741. [
  46742. 'millisecond',
  46743. [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  46744. ], [
  46745. 'second',
  46746. [1, 2, 5, 10, 15, 30]
  46747. ], [
  46748. 'minute',
  46749. [1, 2, 5, 10, 15, 30]
  46750. ], [
  46751. 'hour',
  46752. [1, 2, 3, 4, 6, 8, 12]
  46753. ], [
  46754. 'day',
  46755. [1]
  46756. ], [
  46757. 'week',
  46758. [1]
  46759. ], [
  46760. 'month',
  46761. [1, 3, 6]
  46762. ], [
  46763. 'year',
  46764. null
  46765. ]
  46766. ];
  46767. // Set default approximations to the prototypes if present. Properties are
  46768. // inherited down. Can be overridden for individual series types.
  46769. seriesProto.getDGApproximation = function () {
  46770. if (this.is('arearange')) {
  46771. return 'range';
  46772. }
  46773. if (this.is('ohlc')) {
  46774. return 'ohlc';
  46775. }
  46776. if (this.is('column')) {
  46777. return 'sum';
  46778. }
  46779. return 'average';
  46780. };
  46781. /**
  46782. * Takes parallel arrays of x and y data and groups the data into intervals
  46783. * defined by groupPositions, a collection of starting x values for each group.
  46784. *
  46785. * @private
  46786. * @function Highcharts.Series#groupData
  46787. *
  46788. * @param {Array<number>} xData
  46789. *
  46790. * @param {Array<number>|Array<Array<number>>} yData
  46791. *
  46792. * @param {boolean} groupPositions
  46793. *
  46794. * @param {string|Function} approximation
  46795. *
  46796. * @return {void}
  46797. */
  46798. seriesProto.groupData = groupData;
  46799. // Extend the basic processData method, that crops the data to the current zoom
  46800. // range, with data grouping logic.
  46801. seriesProto.processData = function () {
  46802. var series = this, chart = series.chart, options = series.options, dataGroupingOptions = options.dataGrouping, groupingEnabled = series.allowDG !== false && dataGroupingOptions &&
  46803. pick(dataGroupingOptions.enabled, chart.options.isStock), visible = (series.visible || !chart.options.chart.ignoreHiddenSeries), hasGroupedData, skip, lastDataGrouping = this.currentDataGrouping, currentDataGrouping, croppedData, revertRequireSorting = false;
  46804. // Run base method
  46805. series.forceCrop = groupingEnabled; // #334
  46806. series.groupPixelWidth = null; // #2110
  46807. series.hasProcessed = true; // #2692
  46808. // Data needs to be sorted for dataGrouping
  46809. if (groupingEnabled && !series.requireSorting) {
  46810. series.requireSorting = revertRequireSorting = true;
  46811. }
  46812. // Skip if processData returns false or if grouping is disabled (in that
  46813. // order)
  46814. skip = (baseProcessData.apply(series, arguments) === false ||
  46815. !groupingEnabled);
  46816. // Revert original requireSorting value if changed
  46817. if (revertRequireSorting) {
  46818. series.requireSorting = false;
  46819. }
  46820. if (!skip) {
  46821. series.destroyGroupedData();
  46822. var i, processedXData = dataGroupingOptions.groupAll ?
  46823. series.xData :
  46824. series.processedXData, processedYData = dataGroupingOptions.groupAll ?
  46825. series.yData :
  46826. series.processedYData, plotSizeX = chart.plotSizeX, xAxis = series.xAxis, ordinal = xAxis.options.ordinal, groupPixelWidth = series.groupPixelWidth =
  46827. xAxis.getGroupPixelWidth && xAxis.getGroupPixelWidth();
  46828. // Execute grouping if the amount of points is greater than the limit
  46829. // defined in groupPixelWidth
  46830. if (groupPixelWidth) {
  46831. hasGroupedData = true;
  46832. // Force recreation of point instances in series.translate, #5699
  46833. series.isDirty = true;
  46834. series.points = null; // #6709
  46835. var extremes = xAxis.getExtremes(), xMin = extremes.min, xMax = extremes.max, groupIntervalFactor = (ordinal &&
  46836. xAxis.ordinal &&
  46837. xAxis.ordinal.getGroupIntervalFactor(xMin, xMax, series)) || 1, interval = (groupPixelWidth * (xMax - xMin) / plotSizeX) *
  46838. groupIntervalFactor, groupPositions = xAxis.getTimeTicks(DateTimeAxis.AdditionsClass.prototype.normalizeTimeTickInterval(interval, dataGroupingOptions.units ||
  46839. defaultDataGroupingUnits),
  46840. // Processed data may extend beyond axis (#4907)
  46841. Math.min(xMin, processedXData[0]), Math.max(xMax, processedXData[processedXData.length - 1]), xAxis.options.startOfWeek, processedXData, series.closestPointRange), groupedData = seriesProto.groupData.apply(series, [
  46842. processedXData,
  46843. processedYData,
  46844. groupPositions,
  46845. dataGroupingOptions.approximation
  46846. ]), groupedXData = groupedData.groupedXData, groupedYData = groupedData.groupedYData, gapSize = 0;
  46847. // Prevent the smoothed data to spill out left and right, and make
  46848. // sure data is not shifted to the left
  46849. if (dataGroupingOptions.smoothed && groupedXData.length) {
  46850. i = groupedXData.length - 1;
  46851. groupedXData[i] = Math.min(groupedXData[i], xMax);
  46852. while (i-- && i > 0) {
  46853. groupedXData[i] += interval / 2;
  46854. }
  46855. groupedXData[0] = Math.max(groupedXData[0], xMin);
  46856. }
  46857. // Record what data grouping values were used
  46858. for (i = 1; i < groupPositions.length; i++) {
  46859. // The grouped gapSize needs to be the largest distance between
  46860. // the group to capture varying group sizes like months or DST
  46861. // crossing (#10000). Also check that the gap is not at the
  46862. // start of a segment.
  46863. if (!groupPositions.info.segmentStarts ||
  46864. groupPositions.info.segmentStarts.indexOf(i) === -1) {
  46865. gapSize = Math.max(groupPositions[i] - groupPositions[i - 1], gapSize);
  46866. }
  46867. }
  46868. currentDataGrouping = groupPositions.info;
  46869. currentDataGrouping.gapSize = gapSize;
  46870. series.closestPointRange = groupPositions.info.totalRange;
  46871. series.groupMap = groupedData.groupMap;
  46872. // Make sure the X axis extends to show the first group (#2533)
  46873. // But only for visible series (#5493, #6393)
  46874. if (defined(groupedXData[0]) &&
  46875. groupedXData[0] < xAxis.min &&
  46876. visible) {
  46877. if ((!defined(xAxis.options.min) &&
  46878. xAxis.min <= xAxis.dataMin) ||
  46879. xAxis.min === xAxis.dataMin) {
  46880. xAxis.min = Math.min(groupedXData[0], xAxis.min);
  46881. }
  46882. xAxis.dataMin = Math.min(groupedXData[0], xAxis.dataMin);
  46883. }
  46884. // We calculated all group positions but we should render
  46885. // only the ones within the visible range
  46886. if (dataGroupingOptions.groupAll) {
  46887. croppedData = series.cropData(groupedXData, groupedYData, xAxis.min, xAxis.max, 1 // Ordinal xAxis will remove left-most points otherwise
  46888. );
  46889. groupedXData = croppedData.xData;
  46890. groupedYData = croppedData.yData;
  46891. }
  46892. // Set series props
  46893. series.processedXData = groupedXData;
  46894. series.processedYData = groupedYData;
  46895. }
  46896. else {
  46897. series.groupMap = null;
  46898. }
  46899. series.hasGroupedData = hasGroupedData;
  46900. series.currentDataGrouping = currentDataGrouping;
  46901. series.preventGraphAnimation =
  46902. (lastDataGrouping && lastDataGrouping.totalRange) !==
  46903. (currentDataGrouping && currentDataGrouping.totalRange);
  46904. }
  46905. };
  46906. // Destroy the grouped data points. #622, #740
  46907. seriesProto.destroyGroupedData = function () {
  46908. // Clear previous groups
  46909. if (this.groupedData) {
  46910. this.groupedData.forEach(function (point, i) {
  46911. if (point) {
  46912. this.groupedData[i] = point.destroy ?
  46913. point.destroy() : null;
  46914. }
  46915. }, this);
  46916. // Clears all:
  46917. // - `this.groupedData`
  46918. // - `this.points`
  46919. // - `preserve` object in series.update()
  46920. this.groupedData.length = 0;
  46921. }
  46922. };
  46923. // Override the generatePoints method by adding a reference to grouped data
  46924. seriesProto.generatePoints = function () {
  46925. baseGeneratePoints.apply(this);
  46926. // Record grouped data in order to let it be destroyed the next time
  46927. // processData runs
  46928. this.destroyGroupedData(); // #622
  46929. this.groupedData = this.hasGroupedData ? this.points : null;
  46930. };
  46931. // Override point prototype to throw a warning when trying to update grouped
  46932. // points.
  46933. addEvent(Point, 'update', function () {
  46934. if (this.dataGroup) {
  46935. error(24, false, this.series.chart);
  46936. return false;
  46937. }
  46938. });
  46939. // Extend the original method, make the tooltip's header reflect the grouped
  46940. // range.
  46941. addEvent(Tooltip, 'headerFormatter', function (e) {
  46942. var tooltip = this, chart = this.chart, time = chart.time, labelConfig = e.labelConfig, series = labelConfig.series, options = series.options, tooltipOptions = series.tooltipOptions, dataGroupingOptions = options.dataGrouping, xDateFormat = tooltipOptions.xDateFormat, xDateFormatEnd, xAxis = series.xAxis, currentDataGrouping, dateTimeLabelFormats, labelFormats, formattedKey, formatString = tooltipOptions[(e.isFooter ? 'footer' : 'header') + 'Format'];
  46943. // apply only to grouped series
  46944. if (xAxis &&
  46945. xAxis.options.type === 'datetime' &&
  46946. dataGroupingOptions &&
  46947. isNumber(labelConfig.key)) {
  46948. // set variables
  46949. currentDataGrouping = series.currentDataGrouping;
  46950. dateTimeLabelFormats = dataGroupingOptions.dateTimeLabelFormats ||
  46951. // Fallback to commonOptions (#9693)
  46952. commonOptions.dateTimeLabelFormats;
  46953. // if we have grouped data, use the grouping information to get the
  46954. // right format
  46955. if (currentDataGrouping) {
  46956. labelFormats =
  46957. dateTimeLabelFormats[currentDataGrouping.unitName];
  46958. if (currentDataGrouping.count === 1) {
  46959. xDateFormat = labelFormats[0];
  46960. }
  46961. else {
  46962. xDateFormat = labelFormats[1];
  46963. xDateFormatEnd = labelFormats[2];
  46964. }
  46965. // if not grouped, and we don't have set the xDateFormat option, get the
  46966. // best fit, so if the least distance between points is one minute, show
  46967. // it, but if the least distance is one day, skip hours and minutes etc.
  46968. }
  46969. else if (!xDateFormat && dateTimeLabelFormats) {
  46970. xDateFormat = tooltip.getXDateFormat(labelConfig, tooltipOptions, xAxis);
  46971. }
  46972. // now format the key
  46973. formattedKey = time.dateFormat(xDateFormat, labelConfig.key);
  46974. if (xDateFormatEnd) {
  46975. formattedKey += time.dateFormat(xDateFormatEnd, labelConfig.key + currentDataGrouping.totalRange - 1);
  46976. }
  46977. // Replace default header style with class name
  46978. if (series.chart.styledMode) {
  46979. formatString = this.styledModeFormat(formatString);
  46980. }
  46981. // return the replaced format
  46982. e.text = format(formatString, {
  46983. point: extend(labelConfig.point, { key: formattedKey }),
  46984. series: series
  46985. }, chart);
  46986. e.preventDefault();
  46987. }
  46988. });
  46989. // Destroy grouped data on series destroy
  46990. addEvent(Series, 'destroy', seriesProto.destroyGroupedData);
  46991. // Handle default options for data grouping. This must be set at runtime because
  46992. // some series types are defined after this.
  46993. addEvent(Series, 'afterSetOptions', function (e) {
  46994. var options = e.options, type = this.type, plotOptions = this.chart.options.plotOptions, defaultOptions = O.defaultOptions.plotOptions[type].dataGrouping,
  46995. // External series, for example technical indicators should also
  46996. // inherit commonOptions which are not available outside this module
  46997. baseOptions = this.useCommonDataGrouping && commonOptions;
  46998. if (specificOptions[type] || baseOptions) { // #1284
  46999. if (!defaultOptions) {
  47000. defaultOptions = merge(commonOptions, specificOptions[type]);
  47001. }
  47002. options.dataGrouping = merge(baseOptions, defaultOptions, plotOptions.series && plotOptions.series.dataGrouping, // #1228
  47003. // Set by the StockChart constructor:
  47004. plotOptions[type].dataGrouping, this.userOptions.dataGrouping);
  47005. }
  47006. });
  47007. // When resetting the scale reset the hasProccessed flag to avoid taking
  47008. // previous data grouping of neighbour series into accound when determining
  47009. // group pixel width (#2692).
  47010. addEvent(Axis, 'afterSetScale', function () {
  47011. this.series.forEach(function (series) {
  47012. series.hasProcessed = false;
  47013. });
  47014. });
  47015. // Get the data grouping pixel width based on the greatest defined individual
  47016. // width of the axis' series, and if whether one of the axes need grouping.
  47017. Axis.prototype.getGroupPixelWidth = function () {
  47018. var series = this.series, len = series.length, i, groupPixelWidth = 0, doGrouping = false, dataLength, dgOptions;
  47019. // If multiple series are compared on the same x axis, give them the same
  47020. // group pixel width (#334)
  47021. i = len;
  47022. while (i--) {
  47023. dgOptions = series[i].options.dataGrouping;
  47024. if (dgOptions) {
  47025. groupPixelWidth = Math.max(groupPixelWidth,
  47026. // Fallback to commonOptions (#9693)
  47027. pick(dgOptions.groupPixelWidth, commonOptions.groupPixelWidth));
  47028. }
  47029. }
  47030. // If one of the series needs grouping, apply it to all (#1634)
  47031. i = len;
  47032. while (i--) {
  47033. dgOptions = series[i].options.dataGrouping;
  47034. if (dgOptions && series[i].hasProcessed) { // #2692
  47035. dataLength = (series[i].processedXData || series[i].data).length;
  47036. // Execute grouping if the amount of points is greater than the
  47037. // limit defined in groupPixelWidth
  47038. if (series[i].groupPixelWidth ||
  47039. dataLength >
  47040. (this.chart.plotSizeX / groupPixelWidth) ||
  47041. (dataLength && dgOptions.forced)) {
  47042. doGrouping = true;
  47043. }
  47044. }
  47045. }
  47046. return doGrouping ? groupPixelWidth : 0;
  47047. };
  47048. /**
  47049. * Highstock only. Force data grouping on all the axis' series.
  47050. *
  47051. * @product highstock
  47052. *
  47053. * @function Highcharts.Axis#setDataGrouping
  47054. *
  47055. * @param {boolean|Highcharts.DataGroupingOptionsObject} [dataGrouping]
  47056. * A `dataGrouping` configuration. Use `false` to disable data grouping
  47057. * dynamically.
  47058. *
  47059. * @param {boolean} [redraw=true]
  47060. * Whether to redraw the chart or wait for a later call to
  47061. * {@link Chart#redraw}.
  47062. *
  47063. * @return {void}
  47064. */
  47065. Axis.prototype.setDataGrouping = function (dataGrouping, redraw) {
  47066. var axis = this;
  47067. var i;
  47068. redraw = pick(redraw, true);
  47069. if (!dataGrouping) {
  47070. dataGrouping = {
  47071. forced: false,
  47072. units: null
  47073. };
  47074. }
  47075. // Axis is instantiated, update all series
  47076. if (this instanceof Axis) {
  47077. i = this.series.length;
  47078. while (i--) {
  47079. this.series[i].update({
  47080. dataGrouping: dataGrouping
  47081. }, false);
  47082. }
  47083. // Axis not yet instanciated, alter series options
  47084. }
  47085. else {
  47086. this.chart.options.series.forEach(function (seriesOptions) {
  47087. seriesOptions.dataGrouping = dataGrouping;
  47088. }, false);
  47089. }
  47090. // Clear ordinal slope, so we won't accidentaly use the old one (#7827)
  47091. if (axis.ordinal) {
  47092. axis.ordinal.slope = void 0;
  47093. }
  47094. if (redraw) {
  47095. this.chart.redraw();
  47096. }
  47097. };
  47098. H.dataGrouping = dataGrouping;
  47099. /* eslint-enable no-invalid-this, valid-jsdoc */
  47100. /**
  47101. * Data grouping is the concept of sampling the data values into larger
  47102. * blocks in order to ease readability and increase performance of the
  47103. * JavaScript charts. Highstock by default applies data grouping when
  47104. * the points become closer than a certain pixel value, determined by
  47105. * the `groupPixelWidth` option.
  47106. *
  47107. * If data grouping is applied, the grouping information of grouped
  47108. * points can be read from the [Point.dataGroup](
  47109. * /class-reference/Highcharts.Point#dataGroup). If point options other than
  47110. * the data itself are set, for example `name` or `color` or custom properties,
  47111. * the grouping logic doesn't know how to group it. In this case the options of
  47112. * the first point instance are copied over to the group point. This can be
  47113. * altered through a custom `approximation` callback function.
  47114. *
  47115. * @declare Highcharts.DataGroupingOptionsObject
  47116. * @product highstock
  47117. * @requires product:highstock
  47118. * @requires module:modules/datagrouping
  47119. * @apioption plotOptions.series.dataGrouping
  47120. */
  47121. /**
  47122. * The method of approximation inside a group. When for example 30 days
  47123. * are grouped into one month, this determines what value should represent
  47124. * the group. Possible values are "average", "averages", "open", "high",
  47125. * "low", "close" and "sum". For OHLC and candlestick series the approximation
  47126. * is "ohlc" by default, which finds the open, high, low and close values
  47127. * within all the grouped data. For ranges, the approximation is "range",
  47128. * which finds the low and high values. For multi-dimensional data,
  47129. * like ranges and OHLC, "averages" will compute the average for each
  47130. * dimension.
  47131. *
  47132. * Custom aggregate methods can be added by assigning a callback function
  47133. * as the approximation. This function takes a numeric array as the
  47134. * argument and should return a single numeric value or `null`. Note
  47135. * that the numeric array will never contain null values, only true
  47136. * numbers. Instead, if null values are present in the raw data, the
  47137. * numeric array will have an `.hasNulls` property set to `true`. For
  47138. * single-value data sets the data is available in the first argument
  47139. * of the callback function. For OHLC data sets, all the open values
  47140. * are in the first argument, all high values in the second etc.
  47141. *
  47142. * Since v4.2.7, grouping meta data is available in the approximation
  47143. * callback from `this.dataGroupInfo`. It can be used to extract information
  47144. * from the raw data.
  47145. *
  47146. * Defaults to `average` for line-type series, `sum` for columns, `range`
  47147. * for range series and `ohlc` for OHLC and candlestick.
  47148. *
  47149. * @sample {highstock} stock/plotoptions/series-datagrouping-approximation
  47150. * Approximation callback with custom data
  47151. *
  47152. * @type {Highcharts.DataGroupingApproximationValue|Function}
  47153. * @apioption plotOptions.series.dataGrouping.approximation
  47154. */
  47155. /**
  47156. * Datetime formats for the header of the tooltip in a stock chart.
  47157. * The format can vary within a chart depending on the currently selected
  47158. * time range and the current data grouping.
  47159. *
  47160. * The default formats are:
  47161. * ```js
  47162. * {
  47163. * millisecond: [
  47164. * '%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'
  47165. * ],
  47166. * second: ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'],
  47167. * minute: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  47168. * hour: ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'],
  47169. * day: ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  47170. * week: ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'],
  47171. * month: ['%B %Y', '%B', '-%B %Y'],
  47172. * year: ['%Y', '%Y', '-%Y']
  47173. * }
  47174. * ```
  47175. *
  47176. * For each of these array definitions, the first item is the format
  47177. * used when the active time span is one unit. For instance, if the
  47178. * current data applies to one week, the first item of the week array
  47179. * is used. The second and third items are used when the active time
  47180. * span is more than two units. For instance, if the current data applies
  47181. * to two weeks, the second and third item of the week array are used,
  47182. * and applied to the start and end date of the time span.
  47183. *
  47184. * @type {object}
  47185. * @apioption plotOptions.series.dataGrouping.dateTimeLabelFormats
  47186. */
  47187. /**
  47188. * Enable or disable data grouping.
  47189. *
  47190. * @type {boolean}
  47191. * @default true
  47192. * @apioption plotOptions.series.dataGrouping.enabled
  47193. */
  47194. /**
  47195. * When data grouping is forced, it runs no matter how small the intervals
  47196. * are. This can be handy for example when the sum should be calculated
  47197. * for values appearing at random times within each hour.
  47198. *
  47199. * @type {boolean}
  47200. * @default false
  47201. * @apioption plotOptions.series.dataGrouping.forced
  47202. */
  47203. /**
  47204. * The approximate pixel width of each group. If for example a series
  47205. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  47206. * is performed. If however the series contains so many points that
  47207. * the spacing is less than the groupPixelWidth, Highcharts will try
  47208. * to group it into appropriate groups so that each is more or less
  47209. * two pixels wide. If multiple series with different group pixel widths
  47210. * are drawn on the same x axis, all series will take the greatest width.
  47211. * For example, line series have 2px default group width, while column
  47212. * series have 10px. If combined, both the line and the column will
  47213. * have 10px by default.
  47214. *
  47215. * @type {number}
  47216. * @default 2
  47217. * @apioption plotOptions.series.dataGrouping.groupPixelWidth
  47218. */
  47219. /**
  47220. * By default only points within the visible range are grouped. Enabling this
  47221. * option will force data grouping to calculate all grouped points for a given
  47222. * dataset. That option prevents for example a column series from calculating
  47223. * a grouped point partially. The effect is similar to
  47224. * [Series.getExtremesFromAll](#plotOptions.series.getExtremesFromAll) but does
  47225. * not affect yAxis extremes.
  47226. *
  47227. * @sample {highstock} stock/plotoptions/series-datagrouping-groupall/
  47228. * Two series with the same data but different groupAll setting
  47229. *
  47230. * @type {boolean}
  47231. * @default false
  47232. * @since 6.1.0
  47233. * @apioption plotOptions.series.dataGrouping.groupAll
  47234. */
  47235. /**
  47236. * Normally, a group is indexed by the start of that group, so for example
  47237. * when 30 daily values are grouped into one month, that month's x value
  47238. * will be the 1st of the month. This apparently shifts the data to
  47239. * the left. When the smoothed option is true, this is compensated for.
  47240. * The data is shifted to the middle of the group, and min and max
  47241. * values are preserved. Internally, this is used in the Navigator series.
  47242. *
  47243. * @type {boolean}
  47244. * @default false
  47245. * @apioption plotOptions.series.dataGrouping.smoothed
  47246. */
  47247. /**
  47248. * An array determining what time intervals the data is allowed to be
  47249. * grouped to. Each array item is an array where the first value is
  47250. * the time unit and the second value another array of allowed multiples.
  47251. *
  47252. * Defaults to:
  47253. * ```js
  47254. * units: [[
  47255. * 'millisecond', // unit name
  47256. * [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
  47257. * ], [
  47258. * 'second',
  47259. * [1, 2, 5, 10, 15, 30]
  47260. * ], [
  47261. * 'minute',
  47262. * [1, 2, 5, 10, 15, 30]
  47263. * ], [
  47264. * 'hour',
  47265. * [1, 2, 3, 4, 6, 8, 12]
  47266. * ], [
  47267. * 'day',
  47268. * [1]
  47269. * ], [
  47270. * 'week',
  47271. * [1]
  47272. * ], [
  47273. * 'month',
  47274. * [1, 3, 6]
  47275. * ], [
  47276. * 'year',
  47277. * null
  47278. * ]]
  47279. * ```
  47280. *
  47281. * @type {Array<Array<string,(Array<number>|null)>>}
  47282. * @apioption plotOptions.series.dataGrouping.units
  47283. */
  47284. /**
  47285. * The approximate pixel width of each group. If for example a series
  47286. * with 30 points is displayed over a 600 pixel wide plot area, no grouping
  47287. * is performed. If however the series contains so many points that
  47288. * the spacing is less than the groupPixelWidth, Highcharts will try
  47289. * to group it into appropriate groups so that each is more or less
  47290. * two pixels wide. Defaults to `10`.
  47291. *
  47292. * @sample {highstock} stock/plotoptions/series-datagrouping-grouppixelwidth/
  47293. * Two series with the same data density but different groupPixelWidth
  47294. *
  47295. * @type {number}
  47296. * @default 10
  47297. * @apioption plotOptions.column.dataGrouping.groupPixelWidth
  47298. */
  47299. ''; // required by JSDoc parsing
  47300. return dataGrouping;
  47301. });
  47302. _registerModule(_modules, 'parts/OHLCSeries.js', [_modules['parts/Globals.js'], _modules['parts/Point.js'], _modules['parts/Utilities.js']], function (H, Point, U) {
  47303. /* *
  47304. *
  47305. * (c) 2010-2020 Torstein Honsi
  47306. *
  47307. * License: www.highcharts.com/license
  47308. *
  47309. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47310. *
  47311. * */
  47312. var seriesType = U.seriesType;
  47313. var seriesTypes = H.seriesTypes;
  47314. /**
  47315. * The ohlc series type.
  47316. *
  47317. * @private
  47318. * @class
  47319. * @name Highcharts.seriesTypes.ohlc
  47320. *
  47321. * @augments Highcharts.Series
  47322. */
  47323. seriesType('ohlc', 'column'
  47324. /**
  47325. * An OHLC chart is a style of financial chart used to describe price
  47326. * movements over time. It displays open, high, low and close values per
  47327. * data point.
  47328. *
  47329. * @sample stock/demo/ohlc/
  47330. * OHLC chart
  47331. *
  47332. * @extends plotOptions.column
  47333. * @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
  47334. * stack
  47335. * @product highstock
  47336. * @optionparent plotOptions.ohlc
  47337. */
  47338. , {
  47339. /**
  47340. * The approximate pixel width of each group. If for example a series
  47341. * with 30 points is displayed over a 600 pixel wide plot area, no
  47342. * grouping is performed. If however the series contains so many points
  47343. * that the spacing is less than the groupPixelWidth, Highcharts will
  47344. * try to group it into appropriate groups so that each is more or less
  47345. * two pixels wide. Defaults to `5`.
  47346. *
  47347. * @type {number}
  47348. * @default 5
  47349. * @product highstock
  47350. * @apioption plotOptions.ohlc.dataGrouping.groupPixelWidth
  47351. */
  47352. /**
  47353. * The pixel width of the line/border. Defaults to `1`.
  47354. *
  47355. * @sample {highstock} stock/plotoptions/ohlc-linewidth/
  47356. * A greater line width
  47357. *
  47358. * @type {number}
  47359. * @default 1
  47360. * @product highstock
  47361. *
  47362. * @private
  47363. */
  47364. lineWidth: 1,
  47365. tooltip: {
  47366. pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
  47367. '<b> {series.name}</b><br/>' +
  47368. 'Open: {point.open}<br/>' +
  47369. 'High: {point.high}<br/>' +
  47370. 'Low: {point.low}<br/>' +
  47371. 'Close: {point.close}<br/>'
  47372. },
  47373. threshold: null,
  47374. states: {
  47375. /**
  47376. * @extends plotOptions.column.states.hover
  47377. * @product highstock
  47378. */
  47379. hover: {
  47380. /**
  47381. * The pixel width of the line representing the OHLC point.
  47382. *
  47383. * @type {number}
  47384. * @default 3
  47385. * @product highstock
  47386. */
  47387. lineWidth: 3
  47388. }
  47389. },
  47390. /**
  47391. * Determines which one of `open`, `high`, `low`, `close` values should
  47392. * be represented as `point.y`, which is later used to set dataLabel
  47393. * position and [compare](#plotOptions.series.compare).
  47394. *
  47395. * @sample {highstock} stock/plotoptions/ohlc-pointvalkey/
  47396. * Possible values
  47397. *
  47398. * @type {string}
  47399. * @default close
  47400. * @validvalue ["open", "high", "low", "close"]
  47401. * @product highstock
  47402. * @apioption plotOptions.ohlc.pointValKey
  47403. */
  47404. /**
  47405. * @default close
  47406. * @apioption plotOptions.ohlc.colorKey
  47407. */
  47408. /**
  47409. * Line color for up points.
  47410. *
  47411. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  47412. * @product highstock
  47413. * @apioption plotOptions.ohlc.upColor
  47414. */
  47415. stickyTracking: true
  47416. },
  47417. /**
  47418. * @lends Highcharts.seriesTypes.ohlc
  47419. */
  47420. {
  47421. /* eslint-disable valid-jsdoc */
  47422. directTouch: false,
  47423. pointArrayMap: ['open', 'high', 'low', 'close'],
  47424. toYData: function (point) {
  47425. // return a plain array for speedy calculation
  47426. return [point.open, point.high, point.low, point.close];
  47427. },
  47428. pointValKey: 'close',
  47429. pointAttrToOptions: {
  47430. stroke: 'color',
  47431. 'stroke-width': 'lineWidth'
  47432. },
  47433. /**
  47434. * @private
  47435. * @function Highcarts.seriesTypes.ohlc#init
  47436. * @return {void}
  47437. */
  47438. init: function () {
  47439. seriesTypes.column.prototype.init.apply(this, arguments);
  47440. this.options.stacking = void 0; // #8817
  47441. },
  47442. /**
  47443. * Postprocess mapping between options and SVG attributes
  47444. *
  47445. * @private
  47446. * @function Highcharts.seriesTypes.ohlc#pointAttribs
  47447. * @param {Highcharts.OHLCPoint} point
  47448. * @param {string} state
  47449. * @return {Highcharts.SVGAttributes}
  47450. */
  47451. pointAttribs: function (point, state) {
  47452. var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options;
  47453. delete attribs.fill;
  47454. if (!point.options.color &&
  47455. options.upColor &&
  47456. point.open < point.close) {
  47457. attribs.stroke = options.upColor;
  47458. }
  47459. return attribs;
  47460. },
  47461. /**
  47462. * Translate data points from raw values x and y to plotX and plotY
  47463. *
  47464. * @private
  47465. * @function Highcharts.seriesTypes.ohlc#translate
  47466. * @return {void}
  47467. */
  47468. translate: function () {
  47469. var series = this, yAxis = series.yAxis, hasModifyValue = !!series.modifyValue, translated = [
  47470. 'plotOpen',
  47471. 'plotHigh',
  47472. 'plotLow',
  47473. 'plotClose',
  47474. 'yBottom'
  47475. ]; // translate OHLC for
  47476. seriesTypes.column.prototype.translate.apply(series);
  47477. // Do the translation
  47478. series.points.forEach(function (point) {
  47479. [point.open, point.high, point.low, point.close, point.low]
  47480. .forEach(function (value, i) {
  47481. if (value !== null) {
  47482. if (hasModifyValue) {
  47483. value = series.modifyValue(value);
  47484. }
  47485. point[translated[i]] =
  47486. yAxis.toPixels(value, true);
  47487. }
  47488. });
  47489. // Align the tooltip to the high value to avoid covering the
  47490. // point
  47491. point.tooltipPos[1] =
  47492. point.plotHigh + yAxis.pos - series.chart.plotTop;
  47493. });
  47494. },
  47495. /**
  47496. * Draw the data points
  47497. *
  47498. * @private
  47499. * @function Highcharts.seriesTypes.ohlc#drawPoints
  47500. * @return {void}
  47501. */
  47502. drawPoints: function () {
  47503. var series = this, points = series.points, chart = series.chart,
  47504. /**
  47505. * Extend vertical stem to open and close values.
  47506. */
  47507. extendStem = function (path, halfStrokeWidth, openOrClose) {
  47508. var start = path[0];
  47509. var end = path[1];
  47510. // We don't need to worry about crisp - openOrClose value
  47511. // is already crisped and halfStrokeWidth should remove it.
  47512. if (typeof start[2] === 'number') {
  47513. start[2] = Math.max(openOrClose + halfStrokeWidth, start[2]);
  47514. }
  47515. if (typeof end[2] === 'number') {
  47516. end[2] = Math.min(openOrClose - halfStrokeWidth, end[2]);
  47517. }
  47518. };
  47519. points.forEach(function (point) {
  47520. var plotOpen, plotClose, crispCorr, halfWidth, path, graphic = point.graphic, crispX, isNew = !graphic, strokeWidth;
  47521. if (typeof point.plotY !== 'undefined') {
  47522. // Create and/or update the graphic
  47523. if (!graphic) {
  47524. point.graphic = graphic = chart.renderer.path()
  47525. .add(series.group);
  47526. }
  47527. if (!chart.styledMode) {
  47528. graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
  47529. }
  47530. // crisp vector coordinates
  47531. strokeWidth = graphic.strokeWidth();
  47532. crispCorr = (strokeWidth % 2) / 2;
  47533. // #2596:
  47534. crispX = Math.round(point.plotX) - crispCorr;
  47535. halfWidth = Math.round(point.shapeArgs.width / 2);
  47536. // the vertical stem
  47537. path = [
  47538. ['M', crispX, Math.round(point.yBottom)],
  47539. ['L', crispX, Math.round(point.plotHigh)]
  47540. ];
  47541. // open
  47542. if (point.open !== null) {
  47543. plotOpen = Math.round(point.plotOpen) + crispCorr;
  47544. path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
  47545. extendStem(path, strokeWidth / 2, plotOpen);
  47546. }
  47547. // close
  47548. if (point.close !== null) {
  47549. plotClose = Math.round(point.plotClose) + crispCorr;
  47550. path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
  47551. extendStem(path, strokeWidth / 2, plotClose);
  47552. }
  47553. graphic[isNew ? 'attr' : 'animate']({ d: path })
  47554. .addClass(point.getClassName(), true);
  47555. }
  47556. });
  47557. },
  47558. animate: null // Disable animation
  47559. /* eslint-enable valid-jsdoc */
  47560. },
  47561. /**
  47562. * @lends Highcharts.seriesTypes.ohlc.prototype.pointClass.prototype
  47563. */
  47564. {
  47565. /* eslint-disable valid-jsdoc */
  47566. /**
  47567. * Extend the parent method by adding up or down to the class name.
  47568. * @private
  47569. * @function Highcharts.seriesTypes.ohlc#getClassName
  47570. * @return {string}
  47571. */
  47572. getClassName: function () {
  47573. return Point.prototype.getClassName.call(this) +
  47574. (this.open < this.close ?
  47575. ' highcharts-point-up' :
  47576. ' highcharts-point-down');
  47577. }
  47578. /* eslint-enable valid-jsdoc */
  47579. });
  47580. /**
  47581. * A `ohlc` series. If the [type](#series.ohlc.type) option is not
  47582. * specified, it is inherited from [chart.type](#chart.type).
  47583. *
  47584. * @extends series,plotOptions.ohlc
  47585. * @excluding dataParser, dataURL
  47586. * @product highstock
  47587. * @apioption series.ohlc
  47588. */
  47589. /**
  47590. * An array of data points for the series. For the `ohlc` series type,
  47591. * points can be given in the following ways:
  47592. *
  47593. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  47594. * to `x,open,high,low,close`. If the first value is a string, it is applied
  47595. * as the name of the point, and the `x` value is inferred. The `x` value can
  47596. * also be omitted, in which case the inner arrays should be of length 4\.
  47597. * Then the `x` value is automatically calculated, either starting at 0 and
  47598. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  47599. * series options.
  47600. * ```js
  47601. * data: [
  47602. * [0, 6, 5, 6, 7],
  47603. * [1, 9, 4, 8, 2],
  47604. * [2, 6, 3, 4, 10]
  47605. * ]
  47606. * ```
  47607. *
  47608. * 2. An array of objects with named values. The following snippet shows only a
  47609. * few settings, see the complete options set below. If the total number of
  47610. * data points exceeds the series'
  47611. * [turboThreshold](#series.ohlc.turboThreshold), this option is not
  47612. * available.
  47613. * ```js
  47614. * data: [{
  47615. * x: 1,
  47616. * open: 3,
  47617. * high: 4,
  47618. * low: 5,
  47619. * close: 2,
  47620. * name: "Point2",
  47621. * color: "#00FF00"
  47622. * }, {
  47623. * x: 1,
  47624. * open: 4,
  47625. * high: 3,
  47626. * low: 6,
  47627. * close: 7,
  47628. * name: "Point1",
  47629. * color: "#FF00FF"
  47630. * }]
  47631. * ```
  47632. *
  47633. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  47634. * @extends series.arearange.data
  47635. * @excluding y, marker
  47636. * @product highstock
  47637. * @apioption series.ohlc.data
  47638. */
  47639. /**
  47640. * The closing value of each data point.
  47641. *
  47642. * @type {number}
  47643. * @product highstock
  47644. * @apioption series.ohlc.data.close
  47645. */
  47646. /**
  47647. * The opening value of each data point.
  47648. *
  47649. * @type {number}
  47650. * @product highstock
  47651. * @apioption series.ohlc.data.open
  47652. */
  47653. ''; // adds doclets above to transpilat
  47654. });
  47655. _registerModule(_modules, 'parts/CandlestickSeries.js', [_modules['parts/Globals.js'], _modules['parts/Options.js'], _modules['parts/Utilities.js']], function (H, O, U) {
  47656. /* *
  47657. *
  47658. * (c) 2010-2020 Torstein Honsi
  47659. *
  47660. * License: www.highcharts.com/license
  47661. *
  47662. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47663. *
  47664. * */
  47665. var defaultOptions = O.defaultOptions;
  47666. var merge = U.merge, seriesType = U.seriesType;
  47667. var seriesTypes = H.seriesTypes;
  47668. /**
  47669. * A candlestick chart is a style of financial chart used to describe price
  47670. * movements over time.
  47671. *
  47672. * @sample stock/demo/candlestick/
  47673. * Candlestick chart
  47674. *
  47675. * @extends plotOptions.ohlc
  47676. * @excluding borderColor,borderRadius,borderWidth
  47677. * @product highstock
  47678. * @optionparent plotOptions.candlestick
  47679. */
  47680. var candlestickOptions = {
  47681. /**
  47682. * The specific line color for up candle sticks. The default is to inherit
  47683. * the general `lineColor` setting.
  47684. *
  47685. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  47686. * Candlestick line colors
  47687. *
  47688. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  47689. * @since 1.3.6
  47690. * @product highstock
  47691. * @apioption plotOptions.candlestick.upLineColor
  47692. */
  47693. /**
  47694. * @type {Highcharts.DataGroupingApproximationValue|Function}
  47695. * @default ohlc
  47696. * @product highstock
  47697. * @apioption plotOptions.candlestick.dataGrouping.approximation
  47698. */
  47699. states: {
  47700. /**
  47701. * @extends plotOptions.column.states.hover
  47702. * @product highstock
  47703. */
  47704. hover: {
  47705. /**
  47706. * The pixel width of the line/border around the candlestick.
  47707. *
  47708. * @product highstock
  47709. */
  47710. lineWidth: 2
  47711. }
  47712. },
  47713. /**
  47714. * @extends plotOptions.ohlc.tooltip
  47715. */
  47716. tooltip: defaultOptions.plotOptions.ohlc.tooltip,
  47717. /**
  47718. * @type {number|null}
  47719. * @product highstock
  47720. */
  47721. threshold: null,
  47722. /**
  47723. * The color of the line/border of the candlestick.
  47724. *
  47725. * In styled mode, the line stroke can be set with the
  47726. * `.highcharts-candlestick-series .highcahrts-point` rule.
  47727. *
  47728. * @see [upLineColor](#plotOptions.candlestick.upLineColor)
  47729. *
  47730. * @sample {highstock} stock/plotoptions/candlestick-linecolor/
  47731. * Candlestick line colors
  47732. *
  47733. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  47734. * @default #000000
  47735. * @product highstock
  47736. */
  47737. lineColor: '#000000',
  47738. /**
  47739. * The pixel width of the candlestick line/border. Defaults to `1`.
  47740. *
  47741. *
  47742. * In styled mode, the line stroke width can be set with the
  47743. * `.highcharts-candlestick-series .highcahrts-point` rule.
  47744. *
  47745. * @product highstock
  47746. */
  47747. lineWidth: 1,
  47748. /**
  47749. * The fill color of the candlestick when values are rising.
  47750. *
  47751. * In styled mode, the up color can be set with the
  47752. * `.highcharts-candlestick-series .highcharts-point-up` rule.
  47753. *
  47754. * @sample {highstock} stock/plotoptions/candlestick-color/
  47755. * Custom colors
  47756. * @sample {highstock} highcharts/css/candlestick/
  47757. * Colors in styled mode
  47758. *
  47759. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  47760. * @default #ffffff
  47761. * @product highstock
  47762. */
  47763. upColor: '#ffffff',
  47764. /**
  47765. * @product highstock
  47766. */
  47767. stickyTracking: true
  47768. };
  47769. /**
  47770. * The candlestick series type.
  47771. *
  47772. * @private
  47773. * @class
  47774. * @name Highcharts.seriesTypes.candlestick
  47775. *
  47776. * @augments Highcharts.seriesTypes.ohlc
  47777. */
  47778. seriesType('candlestick', 'ohlc', merge(defaultOptions.plotOptions.column, candlestickOptions),
  47779. /**
  47780. * @lends seriesTypes.candlestick
  47781. */
  47782. {
  47783. /* eslint-disable valid-jsdoc */
  47784. /**
  47785. * Postprocess mapping between options and SVG attributes
  47786. *
  47787. * @private
  47788. * @function Highcharts.seriesTypes.candlestick#pointAttribs
  47789. * @param {Highcharts.Point} point
  47790. * @param {string} [state]
  47791. * @return {Highcharts.SVGAttributes}
  47792. */
  47793. pointAttribs: function (point, state) {
  47794. var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options, isUp = point.open < point.close, stroke = options.lineColor || this.color, stateOptions;
  47795. attribs['stroke-width'] = options.lineWidth;
  47796. attribs.fill = point.options.color ||
  47797. (isUp ? (options.upColor || this.color) : this.color);
  47798. attribs.stroke = point.options.lineColor ||
  47799. (isUp ? (options.upLineColor || stroke) : stroke);
  47800. // Select or hover states
  47801. if (state) {
  47802. stateOptions = options.states[state];
  47803. attribs.fill = stateOptions.color || attribs.fill;
  47804. attribs.stroke = stateOptions.lineColor || attribs.stroke;
  47805. attribs['stroke-width'] =
  47806. stateOptions.lineWidth || attribs['stroke-width'];
  47807. }
  47808. return attribs;
  47809. },
  47810. /**
  47811. * Draw the data points.
  47812. *
  47813. * @private
  47814. * @function Highcharts.seriesTypes.candlestick#drawPoints
  47815. * @return {void}
  47816. */
  47817. drawPoints: function () {
  47818. var series = this, points = series.points, chart = series.chart, reversedYAxis = series.yAxis.reversed;
  47819. points.forEach(function (point) {
  47820. var graphic = point.graphic, plotOpen, plotClose, topBox, bottomBox, hasTopWhisker, hasBottomWhisker, crispCorr, crispX, path, halfWidth, isNew = !graphic;
  47821. if (typeof point.plotY !== 'undefined') {
  47822. if (!graphic) {
  47823. point.graphic = graphic = chart.renderer.path()
  47824. .add(series.group);
  47825. }
  47826. if (!series.chart.styledMode) {
  47827. graphic
  47828. .attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
  47829. .shadow(series.options.shadow);
  47830. }
  47831. // Crisp vector coordinates
  47832. crispCorr = (graphic.strokeWidth() % 2) / 2;
  47833. // #2596:
  47834. crispX = Math.round(point.plotX) - crispCorr;
  47835. plotOpen = point.plotOpen;
  47836. plotClose = point.plotClose;
  47837. topBox = Math.min(plotOpen, plotClose);
  47838. bottomBox = Math.max(plotOpen, plotClose);
  47839. halfWidth = Math.round(point.shapeArgs.width / 2);
  47840. hasTopWhisker = reversedYAxis ?
  47841. bottomBox !== point.yBottom :
  47842. Math.round(topBox) !==
  47843. Math.round(point.plotHigh);
  47844. hasBottomWhisker = reversedYAxis ?
  47845. Math.round(topBox) !==
  47846. Math.round(point.plotHigh) :
  47847. bottomBox !== point.yBottom;
  47848. topBox = Math.round(topBox) + crispCorr;
  47849. bottomBox = Math.round(bottomBox) + crispCorr;
  47850. // Create the path. Due to a bug in Chrome 49, the path is
  47851. // first instanciated with no values, then the values
  47852. // pushed. For unknown reasons, instanciating the path array
  47853. // with all the values would lead to a crash when updating
  47854. // frequently (#5193).
  47855. path = [];
  47856. path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
  47857. ['M', crispX, topBox], [
  47858. 'L',
  47859. // #460, #2094
  47860. crispX,
  47861. hasTopWhisker ?
  47862. Math.round(reversedYAxis ?
  47863. point.yBottom :
  47864. point.plotHigh) :
  47865. topBox
  47866. ], ['M', crispX, bottomBox], [
  47867. 'L',
  47868. // #460, #2094
  47869. crispX,
  47870. hasBottomWhisker ?
  47871. Math.round(reversedYAxis ?
  47872. point.plotHigh :
  47873. point.yBottom) :
  47874. bottomBox
  47875. ]);
  47876. graphic[isNew ? 'attr' : 'animate']({ d: path })
  47877. .addClass(point.getClassName(), true);
  47878. }
  47879. });
  47880. /* eslint-enable valid-jsdoc */
  47881. }
  47882. });
  47883. /**
  47884. * A `candlestick` series. If the [type](#series.candlestick.type)
  47885. * option is not specified, it is inherited from [chart.type](
  47886. * #chart.type).
  47887. *
  47888. * @type {*}
  47889. * @extends series,plotOptions.candlestick
  47890. * @excluding dataParser, dataURL
  47891. * @product highstock
  47892. * @apioption series.candlestick
  47893. */
  47894. /**
  47895. * An array of data points for the series. For the `candlestick` series
  47896. * type, points can be given in the following ways:
  47897. *
  47898. * 1. An array of arrays with 5 or 4 values. In this case, the values correspond
  47899. * to `x,open,high,low,close`. If the first value is a string, it is applied
  47900. * as the name of the point, and the `x` value is inferred. The `x` value can
  47901. * also be omitted, in which case the inner arrays should be of length 4.
  47902. * Then the `x` value is automatically calculated, either starting at 0 and
  47903. * incremented by 1, or from `pointStart` and `pointInterval` given in the
  47904. * series options.
  47905. * ```js
  47906. * data: [
  47907. * [0, 7, 2, 0, 4],
  47908. * [1, 1, 4, 2, 8],
  47909. * [2, 3, 3, 9, 3]
  47910. * ]
  47911. * ```
  47912. *
  47913. * 2. An array of objects with named values. The following snippet shows only a
  47914. * few settings, see the complete options set below. If the total number of
  47915. * data points exceeds the series'
  47916. * [turboThreshold](#series.candlestick.turboThreshold), this option is not
  47917. * available.
  47918. * ```js
  47919. * data: [{
  47920. * x: 1,
  47921. * open: 9,
  47922. * high: 2,
  47923. * low: 4,
  47924. * close: 6,
  47925. * name: "Point2",
  47926. * color: "#00FF00"
  47927. * }, {
  47928. * x: 1,
  47929. * open: 1,
  47930. * high: 4,
  47931. * low: 7,
  47932. * close: 7,
  47933. * name: "Point1",
  47934. * color: "#FF00FF"
  47935. * }]
  47936. * ```
  47937. *
  47938. * @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
  47939. * @extends series.ohlc.data
  47940. * @excluding y
  47941. * @product highstock
  47942. * @apioption series.candlestick.data
  47943. */
  47944. ''; // adds doclets above to transpilat
  47945. });
  47946. _registerModule(_modules, 'mixins/on-series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  47947. /* *
  47948. *
  47949. * (c) 2010-2020 Torstein Honsi
  47950. *
  47951. * License: www.highcharts.com/license
  47952. *
  47953. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  47954. *
  47955. * */
  47956. var defined = U.defined, stableSort = U.stableSort;
  47957. var seriesTypes = H.seriesTypes;
  47958. /**
  47959. * @private
  47960. * @mixin onSeriesMixin
  47961. */
  47962. var onSeriesMixin = {
  47963. /* eslint-disable valid-jsdoc */
  47964. /**
  47965. * Override getPlotBox. If the onSeries option is valid, return the plot box
  47966. * of the onSeries, otherwise proceed as usual.
  47967. *
  47968. * @private
  47969. * @function onSeriesMixin.getPlotBox
  47970. * @return {Highcharts.SeriesPlotBoxObject}
  47971. */
  47972. getPlotBox: function () {
  47973. return H.Series.prototype.getPlotBox.call((this.options.onSeries &&
  47974. this.chart.get(this.options.onSeries)) || this);
  47975. },
  47976. /**
  47977. * Extend the translate method by placing the point on the related series
  47978. *
  47979. * @private
  47980. * @function onSeriesMixin.translate
  47981. * @return {void}
  47982. */
  47983. translate: function () {
  47984. seriesTypes.column.prototype.translate.apply(this);
  47985. var series = this, options = series.options, chart = series.chart, points = series.points, cursor = points.length - 1, point, lastPoint, optionsOnSeries = options.onSeries, onSeries = (optionsOnSeries &&
  47986. chart.get(optionsOnSeries)), onKey = options.onKey || 'y', step = onSeries && onSeries.options.step, onData = (onSeries && onSeries.points), i = onData && onData.length, inverted = chart.inverted, xAxis = series.xAxis, yAxis = series.yAxis, xOffset = 0, leftPoint, lastX, rightPoint, currentDataGrouping, distanceRatio;
  47987. // relate to a master series
  47988. if (onSeries && onSeries.visible && i) {
  47989. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  47990. currentDataGrouping = onSeries.currentDataGrouping;
  47991. lastX = (onData[i - 1].x +
  47992. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  47993. // sort the data points
  47994. stableSort(points, function (a, b) {
  47995. return (a.x - b.x);
  47996. });
  47997. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  47998. while (i-- && points[cursor]) {
  47999. leftPoint = onData[i];
  48000. point = points[cursor];
  48001. point.y = leftPoint.y;
  48002. if (leftPoint.x <= point.x &&
  48003. typeof leftPoint[onKey] !== 'undefined') {
  48004. if (point.x <= lastX) { // #803
  48005. point.plotY = leftPoint[onKey];
  48006. // interpolate between points, #666
  48007. if (leftPoint.x < point.x &&
  48008. !step) {
  48009. rightPoint = onData[i + 1];
  48010. if (rightPoint &&
  48011. typeof rightPoint[onKey] !== 'undefined') {
  48012. // the distance ratio, between 0 and 1
  48013. distanceRatio =
  48014. (point.x - leftPoint.x) /
  48015. (rightPoint.x - leftPoint.x);
  48016. point.plotY +=
  48017. distanceRatio *
  48018. // the plotY distance
  48019. (rightPoint[onKey] - leftPoint[onKey]);
  48020. point.y +=
  48021. distanceRatio *
  48022. (rightPoint.y - leftPoint.y);
  48023. }
  48024. }
  48025. }
  48026. cursor--;
  48027. i++; // check again for points in the same x position
  48028. if (cursor < 0) {
  48029. break;
  48030. }
  48031. }
  48032. }
  48033. }
  48034. // Add plotY position and handle stacking
  48035. points.forEach(function (point, i) {
  48036. var stackIndex;
  48037. point.plotX += xOffset; // #2049
  48038. // Undefined plotY means the point is either on axis, outside series
  48039. // range or hidden series. If the series is outside the range of the
  48040. // x axis it should fall through with an undefined plotY, but then
  48041. // we must remove the shapeArgs (#847). For inverted charts, we need
  48042. // to calculate position anyway, because series.invertGroups is not
  48043. // defined
  48044. if (typeof point.plotY === 'undefined' || inverted) {
  48045. if (point.plotX >= 0 &&
  48046. point.plotX <= xAxis.len) {
  48047. // We're inside xAxis range
  48048. if (inverted) {
  48049. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  48050. point.plotX = defined(point.y) ?
  48051. yAxis.translate(point.y, 0, 0, 0, 1) :
  48052. 0;
  48053. }
  48054. else {
  48055. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  48056. xAxis.offset; // For the windbarb demo
  48057. }
  48058. }
  48059. else {
  48060. point.shapeArgs = {}; // 847
  48061. }
  48062. }
  48063. // if multiple flags appear at the same x, order them into a stack
  48064. lastPoint = points[i - 1];
  48065. if (lastPoint && lastPoint.plotX === point.plotX) {
  48066. if (typeof lastPoint.stackIndex === 'undefined') {
  48067. lastPoint.stackIndex = 0;
  48068. }
  48069. stackIndex = lastPoint.stackIndex + 1;
  48070. }
  48071. point.stackIndex = stackIndex; // #3639
  48072. });
  48073. this.onSeries = onSeries;
  48074. }
  48075. /* eslint-enable valid-jsdoc */
  48076. };
  48077. return onSeriesMixin;
  48078. });
  48079. _registerModule(_modules, 'parts/FlagsSeries.js', [_modules['parts/Globals.js'], _modules['parts/SVGElement.js'], _modules['parts/SVGRenderer.js'], _modules['parts/Utilities.js'], _modules['mixins/on-series.js']], function (H, SVGElement, SVGRenderer, U, onSeriesMixin) {
  48080. /* *
  48081. *
  48082. * (c) 2010-2020 Torstein Honsi
  48083. *
  48084. * License: www.highcharts.com/license
  48085. *
  48086. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48087. *
  48088. * */
  48089. var addEvent = U.addEvent, defined = U.defined, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, seriesType = U.seriesType, wrap = U.wrap;
  48090. /**
  48091. * @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
  48092. */
  48093. var noop = H.noop, Renderer = H.Renderer, Series = H.Series, TrackerMixin = H.TrackerMixin, VMLRenderer = H.VMLRenderer, symbols = SVGRenderer.prototype.symbols;
  48094. /**
  48095. * The Flags series.
  48096. *
  48097. * @private
  48098. * @class
  48099. * @name Highcharts.seriesTypes.flags
  48100. *
  48101. * @augments Highcharts.Series
  48102. */
  48103. seriesType('flags', 'column'
  48104. /**
  48105. * Flags are used to mark events in stock charts. They can be added on the
  48106. * timeline, or attached to a specific series.
  48107. *
  48108. * @sample stock/demo/flags-general/
  48109. * Flags on a line series
  48110. *
  48111. * @extends plotOptions.column
  48112. * @excluding animation, borderColor, borderRadius, borderWidth,
  48113. * colorByPoint, dataGrouping, pointPadding, pointWidth,
  48114. * turboThreshold
  48115. * @product highstock
  48116. * @optionparent plotOptions.flags
  48117. */
  48118. , {
  48119. /**
  48120. * In case the flag is placed on a series, on what point key to place
  48121. * it. Line and columns have one key, `y`. In range or OHLC-type series,
  48122. * however, the flag can optionally be placed on the `open`, `high`,
  48123. * `low` or `close` key.
  48124. *
  48125. * @sample {highstock} stock/plotoptions/flags-onkey/
  48126. * Range series, flag on high
  48127. *
  48128. * @type {string}
  48129. * @default y
  48130. * @since 4.2.2
  48131. * @product highstock
  48132. * @validvalue ["y", "open", "high", "low", "close"]
  48133. * @apioption plotOptions.flags.onKey
  48134. */
  48135. /**
  48136. * The id of the series that the flags should be drawn on. If no id
  48137. * is given, the flags are drawn on the x axis.
  48138. *
  48139. * @sample {highstock} stock/plotoptions/flags/
  48140. * Flags on series and on x axis
  48141. *
  48142. * @type {string}
  48143. * @product highstock
  48144. * @apioption plotOptions.flags.onSeries
  48145. */
  48146. pointRange: 0,
  48147. /**
  48148. * Whether the flags are allowed to overlap sideways. If `false`, the
  48149. * flags are moved sideways using an algorithm that seeks to place every
  48150. * flag as close as possible to its original position.
  48151. *
  48152. * @sample {highstock} stock/plotoptions/flags-allowoverlapx
  48153. * Allow sideways overlap
  48154. *
  48155. * @since 6.0.4
  48156. */
  48157. allowOverlapX: false,
  48158. /**
  48159. * The shape of the marker. Can be one of "flag", "circlepin",
  48160. * "squarepin", or an image of the format `url(/path-to-image.jpg)`.
  48161. * Individual shapes can also be set for each point.
  48162. *
  48163. * @sample {highstock} stock/plotoptions/flags/
  48164. * Different shapes
  48165. *
  48166. * @type {Highcharts.FlagsShapeValue}
  48167. * @product highstock
  48168. */
  48169. shape: 'flag',
  48170. /**
  48171. * When multiple flags in the same series fall on the same value, this
  48172. * number determines the vertical offset between them.
  48173. *
  48174. * @sample {highstock} stock/plotoptions/flags-stackdistance/
  48175. * A greater stack distance
  48176. *
  48177. * @product highstock
  48178. */
  48179. stackDistance: 12,
  48180. /**
  48181. * Text alignment for the text inside the flag.
  48182. *
  48183. * @since 5.0.0
  48184. * @product highstock
  48185. * @validvalue ["left", "center", "right"]
  48186. */
  48187. textAlign: 'center',
  48188. /**
  48189. * Specific tooltip options for flag series. Flag series tooltips are
  48190. * different from most other types in that a flag doesn't have a data
  48191. * value, so the tooltip rather displays the `text` option for each
  48192. * point.
  48193. *
  48194. * @extends plotOptions.series.tooltip
  48195. * @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
  48196. * @product highstock
  48197. */
  48198. tooltip: {
  48199. pointFormat: '{point.text}<br/>'
  48200. },
  48201. threshold: null,
  48202. /**
  48203. * The text to display on each flag. This can be defined on series
  48204. * level, or individually for each point. Defaults to `"A"`.
  48205. *
  48206. * @type {string}
  48207. * @default A
  48208. * @product highstock
  48209. * @apioption plotOptions.flags.title
  48210. */
  48211. /**
  48212. * The y position of the top left corner of the flag relative to either
  48213. * the series (if onSeries is defined), or the x axis. Defaults to
  48214. * `-30`.
  48215. *
  48216. * @product highstock
  48217. */
  48218. y: -30,
  48219. /**
  48220. * Whether to use HTML to render the flag texts. Using HTML allows for
  48221. * advanced formatting, images and reliable bi-directional text
  48222. * rendering. Note that exported images won't respect the HTML, and that
  48223. * HTML won't respect Z-index settings.
  48224. *
  48225. * @type {boolean}
  48226. * @default false
  48227. * @since 1.3
  48228. * @product highstock
  48229. * @apioption plotOptions.flags.useHTML
  48230. */
  48231. /**
  48232. * Fixed width of the flag's shape. By default, width is autocalculated
  48233. * according to the flag's title.
  48234. *
  48235. * @sample {highstock} stock/demo/flags-shapes/
  48236. * Flags with fixed width
  48237. *
  48238. * @type {number}
  48239. * @product highstock
  48240. * @apioption plotOptions.flags.width
  48241. */
  48242. /**
  48243. * Fixed height of the flag's shape. By default, height is
  48244. * autocalculated according to the flag's title.
  48245. *
  48246. * @type {number}
  48247. * @product highstock
  48248. * @apioption plotOptions.flags.height
  48249. */
  48250. /**
  48251. * The fill color for the flags.
  48252. *
  48253. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48254. * @product highstock
  48255. */
  48256. fillColor: '#ffffff',
  48257. /**
  48258. * The color of the line/border of the flag.
  48259. *
  48260. * In styled mode, the stroke is set in the
  48261. * `.highcharts-flag-series.highcharts-point` rule.
  48262. *
  48263. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48264. * @default #000000
  48265. * @product highstock
  48266. * @apioption plotOptions.flags.lineColor
  48267. */
  48268. /**
  48269. * The pixel width of the flag's line/border.
  48270. *
  48271. * @product highstock
  48272. */
  48273. lineWidth: 1,
  48274. states: {
  48275. /**
  48276. * @extends plotOptions.column.states.hover
  48277. * @product highstock
  48278. */
  48279. hover: {
  48280. /**
  48281. * The color of the line/border of the flag.
  48282. *
  48283. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48284. * @product highstock
  48285. */
  48286. lineColor: '#000000',
  48287. /**
  48288. * The fill or background color of the flag.
  48289. *
  48290. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48291. * @product highstock
  48292. */
  48293. fillColor: '#ccd6eb'
  48294. }
  48295. },
  48296. /**
  48297. * The text styles of the flag.
  48298. *
  48299. * In styled mode, the styles are set in the
  48300. * `.highcharts-flag-series .highcharts-point` rule.
  48301. *
  48302. * @type {Highcharts.CSSObject}
  48303. * @default {"fontSize": "11px", "fontWeight": "bold"}
  48304. * @product highstock
  48305. */
  48306. style: {
  48307. /** @ignore-option */
  48308. fontSize: '11px',
  48309. /** @ignore-option */
  48310. fontWeight: 'bold'
  48311. }
  48312. },
  48313. /**
  48314. * @lends seriesTypes.flags.prototype
  48315. */
  48316. {
  48317. sorted: false,
  48318. noSharedTooltip: true,
  48319. allowDG: false,
  48320. takeOrdinalPosition: false,
  48321. trackerGroups: ['markerGroup'],
  48322. forceCrop: true,
  48323. /* eslint-disable no-invalid-this, valid-jsdoc */
  48324. /**
  48325. * Inherit the initialization from base Series.
  48326. *
  48327. * @private
  48328. * @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
  48329. */
  48330. init: Series.prototype.init,
  48331. /**
  48332. * Get presentational attributes
  48333. *
  48334. * @private
  48335. * @function Highcharts.seriesTypes.flags#pointAttribs
  48336. *
  48337. * @param {Highcharts.Point} point
  48338. *
  48339. * @param {string} [state]
  48340. *
  48341. * @return {Highcharts.SVGAttributes}
  48342. */
  48343. pointAttribs: function (point, state) {
  48344. var options = this.options, color = (point && point.color) || this.color, lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
  48345. if (state) {
  48346. fill = options.states[state].fillColor;
  48347. lineColor = options.states[state].lineColor;
  48348. lineWidth = options.states[state].lineWidth;
  48349. }
  48350. return {
  48351. fill: fill || color,
  48352. stroke: lineColor || color,
  48353. 'stroke-width': lineWidth || options.lineWidth || 0
  48354. };
  48355. },
  48356. translate: onSeriesMixin.translate,
  48357. getPlotBox: onSeriesMixin.getPlotBox,
  48358. /**
  48359. * Draw the markers.
  48360. *
  48361. * @private
  48362. * @function Highcharts.seriesTypes.flags#drawPoints
  48363. * @return {void}
  48364. */
  48365. drawPoints: function () {
  48366. var series = this, points = series.points, chart = series.chart, renderer = chart.renderer, plotX, plotY, inverted = chart.inverted, options = series.options, optionsY = options.y, shape, i, point, graphic, stackIndex, anchorY, attribs, outsideRight, yAxis = series.yAxis, boxesMap = {}, boxes = [], centered;
  48367. i = points.length;
  48368. while (i--) {
  48369. point = points[i];
  48370. outsideRight =
  48371. (inverted ? point.plotY : point.plotX) >
  48372. series.xAxis.len;
  48373. plotX = point.plotX;
  48374. stackIndex = point.stackIndex;
  48375. shape = point.options.shape || options.shape;
  48376. plotY = point.plotY;
  48377. if (typeof plotY !== 'undefined') {
  48378. plotY = point.plotY + optionsY -
  48379. (typeof stackIndex !== 'undefined' &&
  48380. (stackIndex * options.stackDistance));
  48381. }
  48382. // skip connectors for higher level stacked points
  48383. point.anchorX = stackIndex ? void 0 : point.plotX;
  48384. anchorY = stackIndex ? void 0 : point.plotY;
  48385. centered = shape !== 'flag';
  48386. graphic = point.graphic;
  48387. // Only draw the point if y is defined and the flag is within
  48388. // the visible area
  48389. if (typeof plotY !== 'undefined' &&
  48390. plotX >= 0 &&
  48391. !outsideRight) {
  48392. // Create the flag
  48393. if (!graphic) {
  48394. graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML);
  48395. if (!chart.styledMode) {
  48396. graphic
  48397. .attr(series.pointAttribs(point))
  48398. .css(merge(options.style, point.style));
  48399. }
  48400. graphic.attr({
  48401. align: centered ? 'center' : 'left',
  48402. width: options.width,
  48403. height: options.height,
  48404. 'text-align': options.textAlign
  48405. })
  48406. .addClass('highcharts-point')
  48407. .add(series.markerGroup);
  48408. // Add reference to the point for tracker (#6303)
  48409. if (point.graphic.div) {
  48410. point.graphic.div.point = point;
  48411. }
  48412. if (!chart.styledMode) {
  48413. graphic.shadow(options.shadow);
  48414. }
  48415. graphic.isNew = true;
  48416. }
  48417. if (plotX > 0) { // #3119
  48418. plotX -= graphic.strokeWidth() % 2; // #4285
  48419. }
  48420. // Plant the flag
  48421. attribs = {
  48422. y: plotY,
  48423. anchorY: anchorY
  48424. };
  48425. if (options.allowOverlapX) {
  48426. attribs.x = plotX;
  48427. attribs.anchorX = point.anchorX;
  48428. }
  48429. graphic.attr({
  48430. text: point.options.title || options.title || 'A'
  48431. })[graphic.isNew ? 'attr' : 'animate'](attribs);
  48432. // Rig for the distribute function
  48433. if (!options.allowOverlapX) {
  48434. if (!boxesMap[point.plotX]) {
  48435. boxesMap[point.plotX] = {
  48436. align: centered ? 0.5 : 0,
  48437. size: graphic.width,
  48438. target: plotX,
  48439. anchorX: plotX
  48440. };
  48441. }
  48442. else {
  48443. boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
  48444. }
  48445. }
  48446. // Set the tooltip anchor position
  48447. point.tooltipPos = [
  48448. plotX,
  48449. plotY + yAxis.pos - chart.plotTop
  48450. ]; // #6327
  48451. }
  48452. else if (graphic) {
  48453. point.graphic = graphic.destroy();
  48454. }
  48455. }
  48456. // Handle X-dimension overlapping
  48457. if (!options.allowOverlapX) {
  48458. objectEach(boxesMap, function (box) {
  48459. box.plotX = box.anchorX;
  48460. boxes.push(box);
  48461. });
  48462. H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
  48463. points.forEach(function (point) {
  48464. var box = point.graphic && boxesMap[point.plotX];
  48465. if (box) {
  48466. point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
  48467. x: box.pos + box.align * box.size,
  48468. anchorX: point.anchorX
  48469. });
  48470. // Hide flag when its box position is not specified
  48471. // (#8573, #9299)
  48472. if (!defined(box.pos)) {
  48473. point.graphic.attr({
  48474. x: -9999,
  48475. anchorX: -9999
  48476. });
  48477. point.graphic.isNew = true;
  48478. }
  48479. else {
  48480. point.graphic.isNew = false;
  48481. }
  48482. }
  48483. });
  48484. }
  48485. // Can be a mix of SVG and HTML and we need events for both (#6303)
  48486. if (options.useHTML) {
  48487. wrap(series.markerGroup, 'on', function (proceed) {
  48488. return SVGElement.prototype.on.apply(
  48489. // for HTML
  48490. proceed.apply(this, [].slice.call(arguments, 1)),
  48491. // and for SVG
  48492. [].slice.call(arguments, 1));
  48493. });
  48494. }
  48495. },
  48496. /**
  48497. * Extend the column trackers with listeners to expand and contract
  48498. * stacks.
  48499. *
  48500. * @private
  48501. * @function Highcharts.seriesTypes.flags#drawTracker
  48502. * @return {void}
  48503. */
  48504. drawTracker: function () {
  48505. var series = this, points = series.points;
  48506. TrackerMixin.drawTrackerPoint.apply(this);
  48507. /* *
  48508. * Bring each stacked flag up on mouse over, this allows readability
  48509. * of vertically stacked elements as well as tight points on the x
  48510. * axis. #1924.
  48511. */
  48512. points.forEach(function (point) {
  48513. var graphic = point.graphic;
  48514. if (graphic) {
  48515. addEvent(graphic.element, 'mouseover', function () {
  48516. // Raise this point
  48517. if (point.stackIndex > 0 &&
  48518. !point.raised) {
  48519. point._y = graphic.y;
  48520. graphic.attr({
  48521. y: point._y - 8
  48522. });
  48523. point.raised = true;
  48524. }
  48525. // Revert other raised points
  48526. points.forEach(function (otherPoint) {
  48527. if (otherPoint !== point &&
  48528. otherPoint.raised &&
  48529. otherPoint.graphic) {
  48530. otherPoint.graphic.attr({
  48531. y: otherPoint._y
  48532. });
  48533. otherPoint.raised = false;
  48534. }
  48535. });
  48536. });
  48537. }
  48538. });
  48539. },
  48540. /**
  48541. * Disable animation, but keep clipping (#8546).
  48542. *
  48543. * @private
  48544. * @function Highcharts.seriesTypes.flags#animate
  48545. * @param {boolean} [init]
  48546. * @return {void}
  48547. */
  48548. animate: function (init) {
  48549. if (init) {
  48550. this.setClip();
  48551. }
  48552. },
  48553. /**
  48554. * @private
  48555. * @function Highcharts.seriesTypes.flags#setClip
  48556. * @return {void}
  48557. */
  48558. setClip: function () {
  48559. Series.prototype.setClip.apply(this, arguments);
  48560. if (this.options.clip !== false && this.sharedClipKey) {
  48561. this.markerGroup
  48562. .clip(this.chart[this.sharedClipKey]);
  48563. }
  48564. },
  48565. /**
  48566. * @private
  48567. * @function Highcharts.seriesTypes.flags#buildKDTree
  48568. */
  48569. buildKDTree: noop,
  48570. /**
  48571. * Don't invert the flag marker group (#4960).
  48572. *
  48573. * @private
  48574. * @function Highcharts.seriesTypes.flags#invertGroups
  48575. */
  48576. invertGroups: noop
  48577. /* eslint-enable no-invalid-this, valid-jsdoc */
  48578. },
  48579. /**
  48580. * @lends Highcharts.seriesTypes.flag.prototype.pointClass.prototype
  48581. */
  48582. {
  48583. isValid: function () {
  48584. // #9233 - Prevent from treating flags as null points (even if
  48585. // they have no y values defined).
  48586. return isNumber(this.y) || typeof this.y === 'undefined';
  48587. }
  48588. });
  48589. // create the flag icon with anchor
  48590. symbols.flag = function (x, y, w, h, options) {
  48591. var anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
  48592. // To do: unwanted any cast because symbols.circle has wrong type, it
  48593. // actually returns an SVGPathArray
  48594. var path = symbols.circle(anchorX - 1, anchorY - 1, 2, 2);
  48595. 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']);
  48596. return path;
  48597. };
  48598. /**
  48599. * Create the circlepin and squarepin icons with anchor.
  48600. * @private
  48601. * @param {string} shape - circle or square
  48602. * @return {void}
  48603. */
  48604. function createPinSymbol(shape) {
  48605. symbols[shape + 'pin'] = function (x, y, w, h, options) {
  48606. var anchorX = options && options.anchorX, anchorY = options && options.anchorY, path;
  48607. // For single-letter flags, make sure circular flags are not taller
  48608. // than their width
  48609. if (shape === 'circle' && h > w) {
  48610. x -= Math.round((h - w) / 2);
  48611. w = h;
  48612. }
  48613. path = (symbols[shape])(x, y, w, h);
  48614. if (anchorX && anchorY) {
  48615. /**
  48616. * If the label is below the anchor, draw the connecting line from
  48617. * the top edge of the label, otherwise start drawing from the
  48618. * bottom edge
  48619. */
  48620. var labelX = anchorX;
  48621. if (shape === 'circle') {
  48622. labelX = x + w / 2;
  48623. }
  48624. else {
  48625. var startSeg = path[0];
  48626. var endSeg = path[1];
  48627. if (startSeg[0] === 'M' && endSeg[0] === 'L') {
  48628. labelX = (startSeg[1] + endSeg[1]) / 2;
  48629. }
  48630. }
  48631. var labelY = (y > anchorY) ? y : y + h;
  48632. path.push([
  48633. 'M',
  48634. labelX,
  48635. labelY
  48636. ], [
  48637. 'L',
  48638. anchorX,
  48639. anchorY
  48640. ]);
  48641. path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
  48642. }
  48643. return path;
  48644. };
  48645. }
  48646. createPinSymbol('circle');
  48647. createPinSymbol('square');
  48648. /**
  48649. * The symbol callbacks are generated on the SVGRenderer object in all browsers.
  48650. * Even VML browsers need this in order to generate shapes in export. Now share
  48651. * them with the VMLRenderer.
  48652. */
  48653. if (Renderer === VMLRenderer) {
  48654. ['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
  48655. VMLRenderer.prototype.symbols[shape] = symbols[shape];
  48656. });
  48657. }
  48658. /**
  48659. * A `flags` series. If the [type](#series.flags.type) option is not
  48660. * specified, it is inherited from [chart.type](#chart.type).
  48661. *
  48662. * @extends series,plotOptions.flags
  48663. * @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
  48664. * connectNulls, dashStyle, dataGrouping, dataParser, dataURL,
  48665. * gapSize, gapUnit, linecap, lineWidth, marker, pointPadding,
  48666. * pointWidth, step, turboThreshold, useOhlcData
  48667. * @product highstock
  48668. * @apioption series.flags
  48669. */
  48670. /**
  48671. * An array of data points for the series. For the `flags` series type,
  48672. * points can be given in the following ways:
  48673. *
  48674. * 1. An array of objects with named values. The following snippet shows only a
  48675. * few settings, see the complete options set below. If the total number of
  48676. * data points exceeds the series'
  48677. * [turboThreshold](#series.flags.turboThreshold), this option is not
  48678. * available.
  48679. * ```js
  48680. * data: [{
  48681. * x: 1,
  48682. * title: "A",
  48683. * text: "First event"
  48684. * }, {
  48685. * x: 1,
  48686. * title: "B",
  48687. * text: "Second event"
  48688. * }]
  48689. * ```
  48690. *
  48691. * @type {Array<*>}
  48692. * @extends series.line.data
  48693. * @excluding dataLabels, marker, name, y
  48694. * @product highstock
  48695. * @apioption series.flags.data
  48696. */
  48697. /**
  48698. * The fill color of an individual flag. By default it inherits from
  48699. * the series color.
  48700. *
  48701. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  48702. * @product highstock
  48703. * @apioption series.flags.data.fillColor
  48704. */
  48705. /**
  48706. * The longer text to be shown in the flag's tooltip.
  48707. *
  48708. * @type {string}
  48709. * @product highstock
  48710. * @apioption series.flags.data.text
  48711. */
  48712. /**
  48713. * The short text to be shown on the flag.
  48714. *
  48715. * @type {string}
  48716. * @product highstock
  48717. * @apioption series.flags.data.title
  48718. */
  48719. ''; // adds doclets above to transpiled file
  48720. });
  48721. _registerModule(_modules, 'parts/RangeSelector.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Options.js'], _modules['parts/SVGElement.js'], _modules['parts/Utilities.js']], function (Axis, Chart, H, O, SVGElement, U) {
  48722. /* *
  48723. *
  48724. * (c) 2010-2020 Torstein Honsi
  48725. *
  48726. * License: www.highcharts.com/license
  48727. *
  48728. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  48729. *
  48730. * */
  48731. var defaultOptions = O.defaultOptions;
  48732. var addEvent = U.addEvent, createElement = U.createElement, css = U.css, defined = U.defined, destroyObjectProperties = U.destroyObjectProperties, discardElement = U.discardElement, extend = U.extend, fireEvent = U.fireEvent, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick, pInt = U.pInt, splat = U.splat;
  48733. /**
  48734. * Define the time span for the button
  48735. *
  48736. * @typedef {"all"|"day"|"hour"|"millisecond"|"minute"|"month"|"second"|"week"|"year"|"ytd"} Highcharts.RangeSelectorButtonTypeValue
  48737. */
  48738. /**
  48739. * Callback function to react on button clicks.
  48740. *
  48741. * @callback Highcharts.RangeSelectorClickCallbackFunction
  48742. *
  48743. * @param {global.Event} e
  48744. * Event arguments.
  48745. *
  48746. * @param {boolean|undefined}
  48747. * Return false to cancel the default button event.
  48748. */
  48749. /**
  48750. * Callback function to parse values entered in the input boxes and return a
  48751. * valid JavaScript time as milliseconds since 1970.
  48752. *
  48753. * @callback Highcharts.RangeSelectorParseCallbackFunction
  48754. *
  48755. * @param {string} value
  48756. * Input value to parse.
  48757. *
  48758. * @return {number}
  48759. * Parsed JavaScript time value.
  48760. */
  48761. /* ************************************************************************** *
  48762. * Start Range Selector code *
  48763. * ************************************************************************** */
  48764. extend(defaultOptions, {
  48765. /**
  48766. * The range selector is a tool for selecting ranges to display within
  48767. * the chart. It provides buttons to select preconfigured ranges in
  48768. * the chart, like 1 day, 1 week, 1 month etc. It also provides input
  48769. * boxes where min and max dates can be manually input.
  48770. *
  48771. * @product highstock gantt
  48772. * @optionparent rangeSelector
  48773. */
  48774. rangeSelector: {
  48775. /**
  48776. * Whether to enable all buttons from the start. By default buttons are
  48777. * only enabled if the corresponding time range exists on the X axis,
  48778. * but enabling all buttons allows for dynamically loading different
  48779. * time ranges.
  48780. *
  48781. * @sample {highstock} stock/rangeselector/allbuttonsenabled-true/
  48782. * All buttons enabled
  48783. *
  48784. * @type {boolean}
  48785. * @default false
  48786. * @since 2.0.3
  48787. * @apioption rangeSelector.allButtonsEnabled
  48788. */
  48789. /**
  48790. * An array of configuration objects for the buttons.
  48791. *
  48792. * Defaults to:
  48793. * ```js
  48794. * buttons: [{
  48795. * type: 'month',
  48796. * count: 1,
  48797. * text: '1m'
  48798. * }, {
  48799. * type: 'month',
  48800. * count: 3,
  48801. * text: '3m'
  48802. * }, {
  48803. * type: 'month',
  48804. * count: 6,
  48805. * text: '6m'
  48806. * }, {
  48807. * type: 'ytd',
  48808. * text: 'YTD'
  48809. * }, {
  48810. * type: 'year',
  48811. * count: 1,
  48812. * text: '1y'
  48813. * }, {
  48814. * type: 'all',
  48815. * text: 'All'
  48816. * }]
  48817. * ```
  48818. *
  48819. * @sample {highstock} stock/rangeselector/datagrouping/
  48820. * Data grouping by buttons
  48821. *
  48822. * @type {Array<*>}
  48823. * @apioption rangeSelector.buttons
  48824. */
  48825. /**
  48826. * How many units of the defined type the button should span. If `type`
  48827. * is "month" and `count` is 3, the button spans three months.
  48828. *
  48829. * @type {number}
  48830. * @default 1
  48831. * @apioption rangeSelector.buttons.count
  48832. */
  48833. /**
  48834. * Fires when clicking on the rangeSelector button. One parameter,
  48835. * event, is passed to the function, containing common event
  48836. * information.
  48837. *
  48838. * ```js
  48839. * click: function(e) {
  48840. * console.log(this);
  48841. * }
  48842. * ```
  48843. *
  48844. * Return false to stop default button's click action.
  48845. *
  48846. * @sample {highstock} stock/rangeselector/button-click/
  48847. * Click event on the button
  48848. *
  48849. * @type {Highcharts.RangeSelectorClickCallbackFunction}
  48850. * @apioption rangeSelector.buttons.events.click
  48851. */
  48852. /**
  48853. * Additional range (in milliseconds) added to the end of the calculated
  48854. * time span.
  48855. *
  48856. * @sample {highstock} stock/rangeselector/min-max-offsets/
  48857. * Button offsets
  48858. *
  48859. * @type {number}
  48860. * @default 0
  48861. * @since 6.0.0
  48862. * @apioption rangeSelector.buttons.offsetMax
  48863. */
  48864. /**
  48865. * Additional range (in milliseconds) added to the start of the
  48866. * calculated time span.
  48867. *
  48868. * @sample {highstock} stock/rangeselector/min-max-offsets/
  48869. * Button offsets
  48870. *
  48871. * @type {number}
  48872. * @default 0
  48873. * @since 6.0.0
  48874. * @apioption rangeSelector.buttons.offsetMin
  48875. */
  48876. /**
  48877. * When buttons apply dataGrouping on a series, by default zooming
  48878. * in/out will deselect buttons and unset dataGrouping. Enable this
  48879. * option to keep buttons selected when extremes change.
  48880. *
  48881. * @sample {highstock} stock/rangeselector/preserve-datagrouping/
  48882. * Different preserveDataGrouping settings
  48883. *
  48884. * @type {boolean}
  48885. * @default false
  48886. * @since 6.1.2
  48887. * @apioption rangeSelector.buttons.preserveDataGrouping
  48888. */
  48889. /**
  48890. * A custom data grouping object for each button.
  48891. *
  48892. * @see [series.dataGrouping](#plotOptions.series.dataGrouping)
  48893. *
  48894. * @sample {highstock} stock/rangeselector/datagrouping/
  48895. * Data grouping by range selector buttons
  48896. *
  48897. * @type {*}
  48898. * @extends plotOptions.series.dataGrouping
  48899. * @apioption rangeSelector.buttons.dataGrouping
  48900. */
  48901. /**
  48902. * The text for the button itself.
  48903. *
  48904. * @type {string}
  48905. * @apioption rangeSelector.buttons.text
  48906. */
  48907. /**
  48908. * Defined the time span for the button. Can be one of `millisecond`,
  48909. * `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `ytd`,
  48910. * and `all`.
  48911. *
  48912. * @type {Highcharts.RangeSelectorButtonTypeValue}
  48913. * @apioption rangeSelector.buttons.type
  48914. */
  48915. /**
  48916. * The space in pixels between the buttons in the range selector.
  48917. *
  48918. * @type {number}
  48919. * @default 0
  48920. * @apioption rangeSelector.buttonSpacing
  48921. */
  48922. /**
  48923. * Enable or disable the range selector.
  48924. *
  48925. * @sample {highstock} stock/rangeselector/enabled/
  48926. * Disable the range selector
  48927. *
  48928. * @type {boolean}
  48929. * @default true
  48930. * @apioption rangeSelector.enabled
  48931. */
  48932. /**
  48933. * The vertical alignment of the rangeselector box. Allowed properties
  48934. * are `top`, `middle`, `bottom`.
  48935. *
  48936. * @sample {highstock} stock/rangeselector/vertical-align-middle/
  48937. * Middle
  48938. * @sample {highstock} stock/rangeselector/vertical-align-bottom/
  48939. * Bottom
  48940. *
  48941. * @type {Highcharts.VerticalAlignValue}
  48942. * @since 6.0.0
  48943. */
  48944. verticalAlign: 'top',
  48945. /**
  48946. * A collection of attributes for the buttons. The object takes SVG
  48947. * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
  48948. * a collection of CSS properties for the text.
  48949. *
  48950. * The object can also be extended with states, so you can set
  48951. * presentational options for `hover`, `select` or `disabled` button
  48952. * states.
  48953. *
  48954. * CSS styles for the text label.
  48955. *
  48956. * In styled mode, the buttons are styled by the
  48957. * `.highcharts-range-selector-buttons .highcharts-button` rule with its
  48958. * different states.
  48959. *
  48960. * @sample {highstock} stock/rangeselector/styling/
  48961. * Styling the buttons and inputs
  48962. *
  48963. * @type {Highcharts.SVGAttributes}
  48964. */
  48965. buttonTheme: {
  48966. /** @ignore */
  48967. width: 28,
  48968. /** @ignore */
  48969. height: 18,
  48970. /** @ignore */
  48971. padding: 2,
  48972. /** @ignore */
  48973. zIndex: 7 // #484, #852
  48974. },
  48975. /**
  48976. * When the rangeselector is floating, the plot area does not reserve
  48977. * space for it. This opens for positioning anywhere on the chart.
  48978. *
  48979. * @sample {highstock} stock/rangeselector/floating/
  48980. * Placing the range selector between the plot area and the
  48981. * navigator
  48982. *
  48983. * @since 6.0.0
  48984. */
  48985. floating: false,
  48986. /**
  48987. * The x offset of the range selector relative to its horizontal
  48988. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  48989. *
  48990. * @since 6.0.0
  48991. */
  48992. x: 0,
  48993. /**
  48994. * The y offset of the range selector relative to its horizontal
  48995. * alignment within `chart.spacingLeft` and `chart.spacingRight`.
  48996. *
  48997. * @since 6.0.0
  48998. */
  48999. y: 0,
  49000. /**
  49001. * Deprecated. The height of the range selector. Currently it is
  49002. * calculated dynamically.
  49003. *
  49004. * @deprecated
  49005. * @type {number|undefined}
  49006. * @since 2.1.9
  49007. */
  49008. height: void 0,
  49009. /**
  49010. * The border color of the date input boxes.
  49011. *
  49012. * @sample {highstock} stock/rangeselector/styling/
  49013. * Styling the buttons and inputs
  49014. *
  49015. * @type {Highcharts.ColorString}
  49016. * @default #cccccc
  49017. * @since 1.3.7
  49018. * @apioption rangeSelector.inputBoxBorderColor
  49019. */
  49020. /**
  49021. * The pixel height of the date input boxes.
  49022. *
  49023. * @sample {highstock} stock/rangeselector/styling/
  49024. * Styling the buttons and inputs
  49025. *
  49026. * @type {number}
  49027. * @default 17
  49028. * @since 1.3.7
  49029. * @apioption rangeSelector.inputBoxHeight
  49030. */
  49031. /**
  49032. * CSS for the container DIV holding the input boxes. Deprecated as
  49033. * of 1.2.5\. Use [inputPosition](#rangeSelector.inputPosition) instead.
  49034. *
  49035. * @sample {highstock} stock/rangeselector/styling/
  49036. * Styling the buttons and inputs
  49037. *
  49038. * @deprecated
  49039. * @type {Highcharts.CSSObject}
  49040. * @apioption rangeSelector.inputBoxStyle
  49041. */
  49042. /**
  49043. * The pixel width of the date input boxes.
  49044. *
  49045. * @sample {highstock} stock/rangeselector/styling/
  49046. * Styling the buttons and inputs
  49047. *
  49048. * @type {number}
  49049. * @default 90
  49050. * @since 1.3.7
  49051. * @apioption rangeSelector.inputBoxWidth
  49052. */
  49053. /**
  49054. * The date format in the input boxes when not selected for editing.
  49055. * Defaults to `%b %e, %Y`.
  49056. *
  49057. * @sample {highstock} stock/rangeselector/input-format/
  49058. * Milliseconds in the range selector
  49059. *
  49060. * @type {string}
  49061. * @default %b %e, %Y
  49062. * @apioption rangeSelector.inputDateFormat
  49063. */
  49064. /**
  49065. * A custom callback function to parse values entered in the input boxes
  49066. * and return a valid JavaScript time as milliseconds since 1970.
  49067. *
  49068. * @sample {highstock} stock/rangeselector/input-format/
  49069. * Milliseconds in the range selector
  49070. *
  49071. * @type {Highcharts.RangeSelectorParseCallbackFunction}
  49072. * @since 1.3.3
  49073. * @apioption rangeSelector.inputDateParser
  49074. */
  49075. /**
  49076. * The date format in the input boxes when they are selected for
  49077. * editing. This must be a format that is recognized by JavaScript
  49078. * Date.parse.
  49079. *
  49080. * @sample {highstock} stock/rangeselector/input-format/
  49081. * Milliseconds in the range selector
  49082. *
  49083. * @type {string}
  49084. * @default %Y-%m-%d
  49085. * @apioption rangeSelector.inputEditDateFormat
  49086. */
  49087. /**
  49088. * Enable or disable the date input boxes. Defaults to enabled when
  49089. * there is enough space, disabled if not (typically mobile).
  49090. *
  49091. * @sample {highstock} stock/rangeselector/input-datepicker/
  49092. * Extending the input with a jQuery UI datepicker
  49093. *
  49094. * @type {boolean}
  49095. * @default true
  49096. * @apioption rangeSelector.inputEnabled
  49097. */
  49098. /**
  49099. * Positioning for the input boxes. Allowed properties are `align`,
  49100. * `x` and `y`.
  49101. *
  49102. * @since 1.2.4
  49103. */
  49104. inputPosition: {
  49105. /**
  49106. * The alignment of the input box. Allowed properties are `left`,
  49107. * `center`, `right`.
  49108. *
  49109. * @sample {highstock} stock/rangeselector/input-button-position/
  49110. * Alignment
  49111. *
  49112. * @type {Highcharts.AlignValue}
  49113. * @since 6.0.0
  49114. */
  49115. align: 'right',
  49116. /**
  49117. * X offset of the input row.
  49118. */
  49119. x: 0,
  49120. /**
  49121. * Y offset of the input row.
  49122. */
  49123. y: 0
  49124. },
  49125. /**
  49126. * The index of the button to appear pre-selected.
  49127. *
  49128. * @type {number}
  49129. * @apioption rangeSelector.selected
  49130. */
  49131. /**
  49132. * Positioning for the button row.
  49133. *
  49134. * @since 1.2.4
  49135. */
  49136. buttonPosition: {
  49137. /**
  49138. * The alignment of the input box. Allowed properties are `left`,
  49139. * `center`, `right`.
  49140. *
  49141. * @sample {highstock} stock/rangeselector/input-button-position/
  49142. * Alignment
  49143. *
  49144. * @type {Highcharts.AlignValue}
  49145. * @since 6.0.0
  49146. */
  49147. align: 'left',
  49148. /**
  49149. * X offset of the button row.
  49150. */
  49151. x: 0,
  49152. /**
  49153. * Y offset of the button row.
  49154. */
  49155. y: 0
  49156. },
  49157. /**
  49158. * CSS for the HTML inputs in the range selector.
  49159. *
  49160. * In styled mode, the inputs are styled by the
  49161. * `.highcharts-range-input text` rule in SVG mode, and
  49162. * `input.highcharts-range-selector` when active.
  49163. *
  49164. * @sample {highstock} stock/rangeselector/styling/
  49165. * Styling the buttons and inputs
  49166. *
  49167. * @type {Highcharts.CSSObject}
  49168. * @apioption rangeSelector.inputStyle
  49169. */
  49170. /**
  49171. * CSS styles for the labels - the Zoom, From and To texts.
  49172. *
  49173. * In styled mode, the labels are styled by the
  49174. * `.highcharts-range-label` class.
  49175. *
  49176. * @sample {highstock} stock/rangeselector/styling/
  49177. * Styling the buttons and inputs
  49178. *
  49179. * @type {Highcharts.CSSObject}
  49180. */
  49181. labelStyle: {
  49182. /** @ignore */
  49183. color: '#666666'
  49184. }
  49185. }
  49186. });
  49187. defaultOptions.lang = merge(defaultOptions.lang,
  49188. /**
  49189. * Language object. The language object is global and it can't be set
  49190. * on each chart initialization. Instead, use `Highcharts.setOptions` to
  49191. * set it before any chart is initialized.
  49192. *
  49193. * ```js
  49194. * Highcharts.setOptions({
  49195. * lang: {
  49196. * months: [
  49197. * 'Janvier', 'Février', 'Mars', 'Avril',
  49198. * 'Mai', 'Juin', 'Juillet', 'Août',
  49199. * 'Septembre', 'Octobre', 'Novembre', 'Décembre'
  49200. * ],
  49201. * weekdays: [
  49202. * 'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
  49203. * 'Jeudi', 'Vendredi', 'Samedi'
  49204. * ]
  49205. * }
  49206. * });
  49207. * ```
  49208. *
  49209. * @optionparent lang
  49210. */
  49211. {
  49212. /**
  49213. * The text for the label for the range selector buttons.
  49214. *
  49215. * @product highstock gantt
  49216. */
  49217. rangeSelectorZoom: 'Zoom',
  49218. /**
  49219. * The text for the label for the "from" input box in the range
  49220. * selector.
  49221. *
  49222. * @product highstock gantt
  49223. */
  49224. rangeSelectorFrom: 'From',
  49225. /**
  49226. * The text for the label for the "to" input box in the range selector.
  49227. *
  49228. * @product highstock gantt
  49229. */
  49230. rangeSelectorTo: 'To'
  49231. });
  49232. /* eslint-disable no-invalid-this, valid-jsdoc */
  49233. /**
  49234. * The range selector.
  49235. *
  49236. * @private
  49237. * @class
  49238. * @name Highcharts.RangeSelector
  49239. * @param {Highcharts.Chart} chart
  49240. */
  49241. var RangeSelector = /** @class */ (function () {
  49242. function RangeSelector(chart) {
  49243. /* *
  49244. *
  49245. * Properties
  49246. *
  49247. * */
  49248. this.buttons = void 0;
  49249. this.buttonOptions = RangeSelector.prototype.defaultButtons;
  49250. this.options = void 0;
  49251. this.chart = chart;
  49252. // Run RangeSelector
  49253. this.init(chart);
  49254. }
  49255. /**
  49256. * The method to run when one of the buttons in the range selectors is
  49257. * clicked
  49258. *
  49259. * @private
  49260. * @function Highcharts.RangeSelector#clickButton
  49261. * @param {number} i
  49262. * The index of the button
  49263. * @param {boolean} [redraw]
  49264. * @return {void}
  49265. */
  49266. RangeSelector.prototype.clickButton = function (i, redraw) {
  49267. var rangeSelector = this, chart = rangeSelector.chart, rangeOptions = rangeSelector.buttonOptions[i], baseAxis = chart.xAxis[0], unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {}, dataMin = unionExtremes.dataMin, dataMax = unionExtremes.dataMax, newMin, newMax = baseAxis && Math.round(Math.min(baseAxis.max, pick(dataMax, baseAxis.max))), // #1568
  49268. type = rangeOptions.type, baseXAxisOptions, range = rangeOptions._range, rangeMin, minSetting, rangeSetting, ctx, ytdExtremes, dataGrouping = rangeOptions.dataGrouping;
  49269. // chart has no data, base series is removed
  49270. if (dataMin === null || dataMax === null) {
  49271. return;
  49272. }
  49273. // Set the fixed range before range is altered
  49274. chart.fixedRange = range;
  49275. // Apply dataGrouping associated to button
  49276. if (dataGrouping) {
  49277. this.forcedDataGrouping = true;
  49278. Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
  49279. this.frozenStates = rangeOptions.preserveDataGrouping;
  49280. }
  49281. // Apply range
  49282. if (type === 'month' || type === 'year') {
  49283. if (!baseAxis) {
  49284. // This is set to the user options and picked up later when the
  49285. // axis is instantiated so that we know the min and max.
  49286. range = rangeOptions;
  49287. }
  49288. else {
  49289. ctx = {
  49290. range: rangeOptions,
  49291. max: newMax,
  49292. chart: chart,
  49293. dataMin: dataMin,
  49294. dataMax: dataMax
  49295. };
  49296. newMin = baseAxis.minFromRange.call(ctx);
  49297. if (isNumber(ctx.newMax)) {
  49298. newMax = ctx.newMax;
  49299. }
  49300. }
  49301. // Fixed times like minutes, hours, days
  49302. }
  49303. else if (range) {
  49304. newMin = Math.max(newMax - range, dataMin);
  49305. newMax = Math.min(newMin + range, dataMax);
  49306. }
  49307. else if (type === 'ytd') {
  49308. // On user clicks on the buttons, or a delayed action running from
  49309. // the beforeRender event (below), the baseAxis is defined.
  49310. if (baseAxis) {
  49311. // When "ytd" is the pre-selected button for the initial view,
  49312. // its calculation is delayed and rerun in the beforeRender
  49313. // event (below). When the series are initialized, but before
  49314. // the chart is rendered, we have access to the xData array
  49315. // (#942).
  49316. if (typeof dataMax === 'undefined') {
  49317. dataMin = Number.MAX_VALUE;
  49318. dataMax = Number.MIN_VALUE;
  49319. chart.series.forEach(function (series) {
  49320. // reassign it to the last item
  49321. var xData = series.xData;
  49322. dataMin = Math.min(xData[0], dataMin);
  49323. dataMax = Math.max(xData[xData.length - 1], dataMax);
  49324. });
  49325. redraw = false;
  49326. }
  49327. ytdExtremes = rangeSelector.getYTDExtremes(dataMax, dataMin, chart.time.useUTC);
  49328. newMin = rangeMin = ytdExtremes.min;
  49329. newMax = ytdExtremes.max;
  49330. // "ytd" is pre-selected. We don't yet have access to processed
  49331. // point and extremes data (things like pointStart and pointInterval
  49332. // are missing), so we delay the process (#942)
  49333. }
  49334. else {
  49335. rangeSelector.deferredYTDClick = i;
  49336. return;
  49337. }
  49338. }
  49339. else if (type === 'all' && baseAxis) {
  49340. newMin = dataMin;
  49341. newMax = dataMax;
  49342. }
  49343. newMin += rangeOptions._offsetMin;
  49344. newMax += rangeOptions._offsetMax;
  49345. rangeSelector.setSelected(i);
  49346. // Update the chart
  49347. if (!baseAxis) {
  49348. // Axis not yet instanciated. Temporarily set min and range
  49349. // options and remove them on chart load (#4317).
  49350. baseXAxisOptions = splat(chart.options.xAxis)[0];
  49351. rangeSetting = baseXAxisOptions.range;
  49352. baseXAxisOptions.range = range;
  49353. minSetting = baseXAxisOptions.min;
  49354. baseXAxisOptions.min = rangeMin;
  49355. addEvent(chart, 'load', function resetMinAndRange() {
  49356. baseXAxisOptions.range = rangeSetting;
  49357. baseXAxisOptions.min = minSetting;
  49358. });
  49359. }
  49360. else {
  49361. // Existing axis object. Set extremes after render time.
  49362. baseAxis.setExtremes(newMin, newMax, pick(redraw, 1), null, // auto animation
  49363. {
  49364. trigger: 'rangeSelectorButton',
  49365. rangeSelectorButton: rangeOptions
  49366. });
  49367. }
  49368. };
  49369. /**
  49370. * Set the selected option. This method only sets the internal flag, it
  49371. * doesn't update the buttons or the actual zoomed range.
  49372. *
  49373. * @private
  49374. * @function Highcharts.RangeSelector#setSelected
  49375. * @param {number} [selected]
  49376. * @return {void}
  49377. */
  49378. RangeSelector.prototype.setSelected = function (selected) {
  49379. this.selected = this.options.selected = selected;
  49380. };
  49381. /**
  49382. * Initialize the range selector
  49383. *
  49384. * @private
  49385. * @function Highcharts.RangeSelector#init
  49386. * @param {Highcharts.Chart} chart
  49387. * @return {void}
  49388. */
  49389. RangeSelector.prototype.init = function (chart) {
  49390. var rangeSelector = this, options = chart.options.rangeSelector, buttonOptions = options.buttons || rangeSelector.defaultButtons.slice(), selectedOption = options.selected, blurInputs = function () {
  49391. var minInput = rangeSelector.minInput, maxInput = rangeSelector.maxInput;
  49392. // #3274 in some case blur is not defined
  49393. if (minInput && minInput.blur) {
  49394. fireEvent(minInput, 'blur');
  49395. }
  49396. if (maxInput && maxInput.blur) {
  49397. fireEvent(maxInput, 'blur');
  49398. }
  49399. };
  49400. rangeSelector.chart = chart;
  49401. rangeSelector.options = options;
  49402. rangeSelector.buttons = [];
  49403. rangeSelector.buttonOptions = buttonOptions;
  49404. this.unMouseDown = addEvent(chart.container, 'mousedown', blurInputs);
  49405. this.unResize = addEvent(chart, 'resize', blurInputs);
  49406. // Extend the buttonOptions with actual range
  49407. buttonOptions.forEach(rangeSelector.computeButtonRange);
  49408. // zoomed range based on a pre-selected button index
  49409. if (typeof selectedOption !== 'undefined' &&
  49410. buttonOptions[selectedOption]) {
  49411. this.clickButton(selectedOption, false);
  49412. }
  49413. addEvent(chart, 'load', function () {
  49414. // If a data grouping is applied to the current button, release it
  49415. // when extremes change
  49416. if (chart.xAxis && chart.xAxis[0]) {
  49417. addEvent(chart.xAxis[0], 'setExtremes', function (e) {
  49418. if (this.max - this.min !==
  49419. chart.fixedRange &&
  49420. e.trigger !== 'rangeSelectorButton' &&
  49421. e.trigger !== 'updatedData' &&
  49422. rangeSelector.forcedDataGrouping &&
  49423. !rangeSelector.frozenStates) {
  49424. this.setDataGrouping(false, false);
  49425. }
  49426. });
  49427. }
  49428. });
  49429. };
  49430. /**
  49431. * Dynamically update the range selector buttons after a new range has been
  49432. * set
  49433. *
  49434. * @private
  49435. * @function Highcharts.RangeSelector#updateButtonStates
  49436. * @return {void}
  49437. */
  49438. RangeSelector.prototype.updateButtonStates = function () {
  49439. var rangeSelector = this, chart = this.chart, baseAxis = chart.xAxis[0], actualRange = Math.round(baseAxis.max - baseAxis.min), hasNoData = !baseAxis.hasVisibleSeries, day = 24 * 36e5, // A single day in milliseconds
  49440. unionExtremes = (chart.scroller &&
  49441. 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, selectedExists = isNumber(selected), allButtonsEnabled = rangeSelector.options.allButtonsEnabled, buttons = rangeSelector.buttons;
  49442. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  49443. var range = rangeOptions._range, type = rangeOptions.type, count = rangeOptions.count || 1, button = buttons[i], state = 0, disable, select, offsetRange = rangeOptions._offsetMax -
  49444. rangeOptions._offsetMin, isSelected = i === selected,
  49445. // Disable buttons where the range exceeds what is allowed in
  49446. // the current view
  49447. isTooGreatRange = range >
  49448. dataMax - dataMin,
  49449. // Disable buttons where the range is smaller than the minimum
  49450. // range
  49451. isTooSmallRange = range < baseAxis.minRange,
  49452. // Do not select the YTD button if not explicitly told so
  49453. isYTDButNotSelected = false,
  49454. // Disable the All button if we're already showing all
  49455. isAllButAlreadyShowingAll = false, isSameRange = range === actualRange;
  49456. // Months and years have a variable range so we check the extremes
  49457. if ((type === 'month' || type === 'year') &&
  49458. (actualRange + 36e5 >=
  49459. { month: 28, year: 365 }[type] * day * count - offsetRange) &&
  49460. (actualRange - 36e5 <=
  49461. { month: 31, year: 366 }[type] * day * count + offsetRange)) {
  49462. isSameRange = true;
  49463. }
  49464. else if (type === 'ytd') {
  49465. isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
  49466. isYTDButNotSelected = !isSelected;
  49467. }
  49468. else if (type === 'all') {
  49469. isSameRange = (baseAxis.max - baseAxis.min >=
  49470. dataMax - dataMin);
  49471. isAllButAlreadyShowingAll = (!isSelected &&
  49472. selectedExists &&
  49473. isSameRange);
  49474. }
  49475. // The new zoom area happens to match the range for a button - mark
  49476. // it selected. This happens when scrolling across an ordinal gap.
  49477. // It can be seen in the intraday demos when selecting 1h and scroll
  49478. // across the night gap.
  49479. disable = (!allButtonsEnabled &&
  49480. (isTooGreatRange ||
  49481. isTooSmallRange ||
  49482. isAllButAlreadyShowingAll ||
  49483. hasNoData));
  49484. select = ((isSelected && isSameRange) ||
  49485. (isSameRange && !selectedExists && !isYTDButNotSelected) ||
  49486. (isSelected && rangeSelector.frozenStates));
  49487. if (disable) {
  49488. state = 3;
  49489. }
  49490. else if (select) {
  49491. selectedExists = true; // Only one button can be selected
  49492. state = 2;
  49493. }
  49494. // If state has changed, update the button
  49495. if (button.state !== state) {
  49496. button.setState(state);
  49497. // Reset (#9209)
  49498. if (state === 0 && selected === i) {
  49499. rangeSelector.setSelected(null);
  49500. }
  49501. }
  49502. });
  49503. };
  49504. /**
  49505. * Compute and cache the range for an individual button
  49506. *
  49507. * @private
  49508. * @function Highcharts.RangeSelector#computeButtonRange
  49509. * @param {Highcharts.RangeSelectorButtonsOptions} rangeOptions
  49510. * @return {void}
  49511. */
  49512. RangeSelector.prototype.computeButtonRange = function (rangeOptions) {
  49513. var type = rangeOptions.type, count = rangeOptions.count || 1,
  49514. // these time intervals have a fixed number of milliseconds, as
  49515. // opposed to month, ytd and year
  49516. fixedTimes = {
  49517. millisecond: 1,
  49518. second: 1000,
  49519. minute: 60 * 1000,
  49520. hour: 3600 * 1000,
  49521. day: 24 * 3600 * 1000,
  49522. week: 7 * 24 * 3600 * 1000
  49523. };
  49524. // Store the range on the button object
  49525. if (fixedTimes[type]) {
  49526. rangeOptions._range = fixedTimes[type] * count;
  49527. }
  49528. else if (type === 'month' || type === 'year') {
  49529. rangeOptions._range = {
  49530. month: 30,
  49531. year: 365
  49532. }[type] * 24 * 36e5 * count;
  49533. }
  49534. rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
  49535. rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
  49536. rangeOptions._range +=
  49537. rangeOptions._offsetMax - rangeOptions._offsetMin;
  49538. };
  49539. /**
  49540. * Set the internal and displayed value of a HTML input for the dates
  49541. *
  49542. * @private
  49543. * @function Highcharts.RangeSelector#setInputValue
  49544. * @param {string} name
  49545. * @param {number} [inputTime]
  49546. * @return {void}
  49547. */
  49548. RangeSelector.prototype.setInputValue = function (name, inputTime) {
  49549. var options = this.chart.options.rangeSelector, time = this.chart.time, input = this[name + 'Input'];
  49550. if (defined(inputTime)) {
  49551. input.previousValue = input.HCTime;
  49552. input.HCTime = inputTime;
  49553. }
  49554. input.value = time.dateFormat(options.inputEditDateFormat || '%Y-%m-%d', input.HCTime);
  49555. this[name + 'DateBox'].attr({
  49556. text: time.dateFormat(options.inputDateFormat || '%b %e, %Y', input.HCTime)
  49557. });
  49558. };
  49559. /**
  49560. * @private
  49561. * @function Highcharts.RangeSelector#showInput
  49562. * @param {string} name
  49563. * @return {void}
  49564. */
  49565. RangeSelector.prototype.showInput = function (name) {
  49566. var inputGroup = this.inputGroup, dateBox = this[name + 'DateBox'];
  49567. css(this[name + 'Input'], {
  49568. left: (inputGroup.translateX + dateBox.x) + 'px',
  49569. top: inputGroup.translateY + 'px',
  49570. width: (dateBox.width - 2) + 'px',
  49571. height: (dateBox.height - 2) + 'px',
  49572. border: '2px solid silver'
  49573. });
  49574. };
  49575. /**
  49576. * @private
  49577. * @function Highcharts.RangeSelector#hideInput
  49578. * @param {string} name
  49579. * @return {void}
  49580. */
  49581. RangeSelector.prototype.hideInput = function (name) {
  49582. css(this[name + 'Input'], {
  49583. border: 0,
  49584. width: '1px',
  49585. height: '1px'
  49586. });
  49587. this.setInputValue(name);
  49588. };
  49589. /**
  49590. * Draw either the 'from' or the 'to' HTML input box of the range selector
  49591. *
  49592. * @private
  49593. * @function Highcharts.RangeSelector#drawInput
  49594. * @param {string} name
  49595. * @return {void}
  49596. */
  49597. RangeSelector.prototype.drawInput = function (name) {
  49598. var rangeSelector = this, chart = rangeSelector.chart, chartStyle = chart.renderer.style || {}, renderer = chart.renderer, options = chart.options.rangeSelector, lang = defaultOptions.lang, div = rangeSelector.div, isMin = name === 'min', input, label, dateBox, inputGroup = this.inputGroup;
  49599. /**
  49600. * @private
  49601. */
  49602. function updateExtremes() {
  49603. var inputValue = input.value, value = (options.inputDateParser || Date.parse)(inputValue), chartAxis = chart.xAxis[0], dataAxis = chart.scroller && chart.scroller.xAxis ?
  49604. chart.scroller.xAxis :
  49605. chartAxis, dataMin = dataAxis.dataMin, dataMax = dataAxis.dataMax;
  49606. if (value !== input.previousValue) {
  49607. input.previousValue = value;
  49608. // If the value isn't parsed directly to a value by the
  49609. // browser's Date.parse method, like YYYY-MM-DD in IE, try
  49610. // parsing it a different way
  49611. if (!isNumber(value)) {
  49612. value = inputValue.split('-');
  49613. value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
  49614. }
  49615. if (isNumber(value)) {
  49616. // Correct for timezone offset (#433)
  49617. if (!chart.time.useUTC) {
  49618. value =
  49619. value + new Date().getTimezoneOffset() * 60 * 1000;
  49620. }
  49621. // Validate the extremes. If it goes beyound the data min or
  49622. // max, use the actual data extreme (#2438).
  49623. if (isMin) {
  49624. if (value > rangeSelector.maxInput.HCTime) {
  49625. value = void 0;
  49626. }
  49627. else if (value < dataMin) {
  49628. value = dataMin;
  49629. }
  49630. }
  49631. else {
  49632. if (value < rangeSelector.minInput.HCTime) {
  49633. value = void 0;
  49634. }
  49635. else if (value > dataMax) {
  49636. value = dataMax;
  49637. }
  49638. }
  49639. // Set the extremes
  49640. if (typeof value !== 'undefined') { // @todo typof undefined
  49641. chartAxis.setExtremes(isMin ? value : chartAxis.min, isMin ? chartAxis.max : value, void 0, void 0, { trigger: 'rangeSelectorInput' });
  49642. }
  49643. }
  49644. }
  49645. }
  49646. // Create the text label
  49647. this[name + 'Label'] = label = renderer
  49648. .label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset)
  49649. .addClass('highcharts-range-label')
  49650. .attr({
  49651. padding: 2
  49652. })
  49653. .add(inputGroup);
  49654. inputGroup.offset += label.width + 5;
  49655. // Create an SVG label that shows updated date ranges and and records
  49656. // click events that bring in the HTML input.
  49657. this[name + 'DateBox'] = dateBox = renderer
  49658. .label('', inputGroup.offset)
  49659. .addClass('highcharts-range-input')
  49660. .attr({
  49661. padding: 2,
  49662. width: options.inputBoxWidth || 90,
  49663. height: options.inputBoxHeight || 17,
  49664. 'text-align': 'center'
  49665. })
  49666. .on('click', function () {
  49667. // If it is already focused, the onfocus event doesn't fire
  49668. // (#3713)
  49669. rangeSelector.showInput(name);
  49670. rangeSelector[name + 'Input'].focus();
  49671. });
  49672. if (!chart.styledMode) {
  49673. dateBox.attr({
  49674. stroke: options.inputBoxBorderColor || '#cccccc',
  49675. 'stroke-width': 1
  49676. });
  49677. }
  49678. dateBox.add(inputGroup);
  49679. inputGroup.offset += dateBox.width + (isMin ? 10 : 0);
  49680. // Create the HTML input element. This is rendered as 1x1 pixel then set
  49681. // to the right size when focused.
  49682. this[name + 'Input'] = input = createElement('input', {
  49683. name: name,
  49684. className: 'highcharts-range-selector',
  49685. type: 'text'
  49686. }, {
  49687. top: chart.plotTop + 'px' // prevent jump on focus in Firefox
  49688. }, div);
  49689. if (!chart.styledMode) {
  49690. // Styles
  49691. label.css(merge(chartStyle, options.labelStyle));
  49692. dateBox.css(merge({
  49693. color: '#333333'
  49694. }, chartStyle, options.inputStyle));
  49695. css(input, extend({
  49696. position: 'absolute',
  49697. border: 0,
  49698. width: '1px',
  49699. height: '1px',
  49700. padding: 0,
  49701. textAlign: 'center',
  49702. fontSize: chartStyle.fontSize,
  49703. fontFamily: chartStyle.fontFamily,
  49704. top: '-9999em' // #4798
  49705. }, options.inputStyle));
  49706. }
  49707. // Blow up the input box
  49708. input.onfocus = function () {
  49709. rangeSelector.showInput(name);
  49710. };
  49711. // Hide away the input box
  49712. input.onblur = function () {
  49713. // update extermes only when inputs are active
  49714. if (input === H.doc.activeElement) { // Only when focused
  49715. // Update also when no `change` event is triggered, like when
  49716. // clicking inside the SVG (#4710)
  49717. updateExtremes();
  49718. }
  49719. // #10404 - move hide and blur outside focus
  49720. rangeSelector.hideInput(name);
  49721. input.blur(); // #4606
  49722. };
  49723. // handle changes in the input boxes
  49724. input.onchange = updateExtremes;
  49725. input.onkeypress = function (event) {
  49726. // IE does not fire onchange on enter
  49727. if (event.keyCode === 13) {
  49728. updateExtremes();
  49729. }
  49730. };
  49731. };
  49732. /**
  49733. * Get the position of the range selector buttons and inputs. This can be
  49734. * overridden from outside for custom positioning.
  49735. *
  49736. * @private
  49737. * @function Highcharts.RangeSelector#getPosition
  49738. *
  49739. * @return {Highcharts.Dictionary<number>}
  49740. */
  49741. RangeSelector.prototype.getPosition = function () {
  49742. var chart = this.chart, options = chart.options.rangeSelector, top = options.verticalAlign === 'top' ?
  49743. chart.plotTop - chart.axisOffset[0] :
  49744. 0; // set offset only for varticalAlign top
  49745. return {
  49746. buttonTop: top + options.buttonPosition.y,
  49747. inputTop: top + options.inputPosition.y - 10
  49748. };
  49749. };
  49750. /**
  49751. * Get the extremes of YTD. Will choose dataMax if its value is lower than
  49752. * the current timestamp. Will choose dataMin if its value is higher than
  49753. * the timestamp for the start of current year.
  49754. *
  49755. * @private
  49756. * @function Highcharts.RangeSelector#getYTDExtremes
  49757. *
  49758. * @param {number} dataMax
  49759. *
  49760. * @param {number} dataMin
  49761. *
  49762. * @return {*}
  49763. * Returns min and max for the YTD
  49764. */
  49765. RangeSelector.prototype.getYTDExtremes = function (dataMax, dataMin, useUTC) {
  49766. var time = this.chart.time, min, now = new time.Date(dataMax), year = time.get('FullYear', now), startOfYear = useUTC ?
  49767. time.Date.UTC(year, 0, 1) : // eslint-disable-line new-cap
  49768. +new time.Date(year, 0, 1);
  49769. min = Math.max(dataMin || 0, startOfYear);
  49770. now = now.getTime();
  49771. return {
  49772. max: Math.min(dataMax || now, now),
  49773. min: min
  49774. };
  49775. };
  49776. /**
  49777. * Render the range selector including the buttons and the inputs. The first
  49778. * time render is called, the elements are created and positioned. On
  49779. * subsequent calls, they are moved and updated.
  49780. *
  49781. * @private
  49782. * @function Highcharts.RangeSelector#render
  49783. * @param {number} [min]
  49784. * X axis minimum
  49785. * @param {number} [max]
  49786. * X axis maximum
  49787. * @return {void}
  49788. */
  49789. RangeSelector.prototype.render = function (min, max) {
  49790. var rangeSelector = this, chart = rangeSelector.chart, renderer = chart.renderer, container = chart.container, chartOptions = chart.options, navButtonOptions = (chartOptions.exporting &&
  49791. chartOptions.exporting.enabled !== false &&
  49792. chartOptions.navigation &&
  49793. chartOptions.navigation.buttonOptions), lang = defaultOptions.lang, div = rangeSelector.div, options = chartOptions.rangeSelector,
  49794. // Place inputs above the container
  49795. inputsZIndex = pick(chartOptions.chart.style &&
  49796. chartOptions.chart.style.zIndex, 0) + 1, floating = options.floating, buttons = rangeSelector.buttons, inputGroup = rangeSelector.inputGroup, buttonTheme = options.buttonTheme, buttonPosition = options.buttonPosition, inputPosition = options.inputPosition, inputEnabled = options.inputEnabled, states = buttonTheme && buttonTheme.states, plotLeft = chart.plotLeft, buttonLeft, buttonGroup = rangeSelector.buttonGroup, group, groupHeight, rendered = rangeSelector.rendered, verticalAlign = rangeSelector.options.verticalAlign, legend = chart.legend, legendOptions = legend && legend.options, buttonPositionY = buttonPosition.y, inputPositionY = inputPosition.y, animate = chart.hasLoaded, verb = animate ? 'animate' : 'attr', exportingX = 0, alignTranslateY, legendHeight, minPosition, translateY = 0, translateX;
  49797. if (options.enabled === false) {
  49798. return;
  49799. }
  49800. // create the elements
  49801. if (!rendered) {
  49802. rangeSelector.group = group = renderer.g('range-selector-group')
  49803. .attr({
  49804. zIndex: 7
  49805. })
  49806. .add();
  49807. rangeSelector.buttonGroup = buttonGroup =
  49808. renderer.g('range-selector-buttons').add(group);
  49809. rangeSelector.zoomText = renderer
  49810. .text(lang.rangeSelectorZoom, 0, 15)
  49811. .add(buttonGroup);
  49812. if (!chart.styledMode) {
  49813. rangeSelector.zoomText.css(options.labelStyle);
  49814. buttonTheme['stroke-width'] =
  49815. pick(buttonTheme['stroke-width'], 0);
  49816. }
  49817. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  49818. buttons[i] = renderer
  49819. .button(rangeOptions.text, 0, 0, function (e) {
  49820. // extract events from button object and call
  49821. var buttonEvents = (rangeOptions.events &&
  49822. rangeOptions.events.click), callDefaultEvent;
  49823. if (buttonEvents) {
  49824. callDefaultEvent =
  49825. buttonEvents.call(rangeOptions, e);
  49826. }
  49827. if (callDefaultEvent !== false) {
  49828. rangeSelector.clickButton(i);
  49829. }
  49830. rangeSelector.isActive = true;
  49831. }, buttonTheme, states && states.hover, states && states.select, states && states.disabled)
  49832. .attr({
  49833. 'text-align': 'center'
  49834. })
  49835. .add(buttonGroup);
  49836. });
  49837. // first create a wrapper outside the container in order to make
  49838. // the inputs work and make export correct
  49839. if (inputEnabled !== false) {
  49840. rangeSelector.div = div = createElement('div', null, {
  49841. position: 'relative',
  49842. height: 0,
  49843. zIndex: inputsZIndex
  49844. });
  49845. container.parentNode.insertBefore(div, container);
  49846. // Create the group to keep the inputs
  49847. rangeSelector.inputGroup = inputGroup =
  49848. renderer.g('input-group').add(group);
  49849. inputGroup.offset = 0;
  49850. rangeSelector.drawInput('min');
  49851. rangeSelector.drawInput('max');
  49852. }
  49853. }
  49854. // #8769, allow dynamically updating margins
  49855. rangeSelector.zoomText[verb]({
  49856. x: pick(plotLeft + buttonPosition.x, plotLeft)
  49857. });
  49858. // button start position
  49859. buttonLeft = pick(plotLeft + buttonPosition.x, plotLeft) +
  49860. rangeSelector.zoomText.getBBox().width + 5;
  49861. rangeSelector.buttonOptions.forEach(function (rangeOptions, i) {
  49862. buttons[i][verb]({ x: buttonLeft });
  49863. // increase button position for the next button
  49864. buttonLeft += buttons[i].width + pick(options.buttonSpacing, 5);
  49865. });
  49866. plotLeft = chart.plotLeft - chart.spacing[3];
  49867. rangeSelector.updateButtonStates();
  49868. // detect collisiton with exporting
  49869. if (navButtonOptions &&
  49870. this.titleCollision(chart) &&
  49871. verticalAlign === 'top' &&
  49872. buttonPosition.align === 'right' && ((buttonPosition.y +
  49873. buttonGroup.getBBox().height - 12) <
  49874. ((navButtonOptions.y || 0) +
  49875. navButtonOptions.height))) {
  49876. exportingX = -40;
  49877. }
  49878. translateX = buttonPosition.x - chart.spacing[3];
  49879. if (buttonPosition.align === 'right') {
  49880. translateX += exportingX - plotLeft; // (#13014)
  49881. }
  49882. else if (buttonPosition.align === 'center') {
  49883. translateX -= plotLeft / 2;
  49884. }
  49885. // align button group
  49886. buttonGroup.align({
  49887. y: buttonPosition.y,
  49888. width: buttonGroup.getBBox().width,
  49889. align: buttonPosition.align,
  49890. x: translateX
  49891. }, true, chart.spacingBox);
  49892. // skip animation
  49893. rangeSelector.group.placed = animate;
  49894. rangeSelector.buttonGroup.placed = animate;
  49895. if (inputEnabled !== false) {
  49896. var inputGroupX, inputGroupWidth, buttonGroupX, buttonGroupWidth;
  49897. // detect collision with exporting
  49898. if (navButtonOptions &&
  49899. this.titleCollision(chart) &&
  49900. verticalAlign === 'top' &&
  49901. inputPosition.align === 'right' && ((inputPosition.y -
  49902. inputGroup.getBBox().height - 12) <
  49903. ((navButtonOptions.y || 0) +
  49904. navButtonOptions.height +
  49905. chart.spacing[0]))) {
  49906. exportingX = -40;
  49907. }
  49908. else {
  49909. exportingX = 0;
  49910. }
  49911. if (inputPosition.align === 'left') {
  49912. translateX = plotLeft;
  49913. }
  49914. else if (inputPosition.align === 'right') {
  49915. translateX = -Math.max(chart.axisOffset[1], -exportingX);
  49916. }
  49917. // Update the alignment to the updated spacing box
  49918. inputGroup.align({
  49919. y: inputPosition.y,
  49920. width: inputGroup.getBBox().width,
  49921. align: inputPosition.align,
  49922. // fix wrong getBBox() value on right align
  49923. x: inputPosition.x + translateX - 2
  49924. }, true, chart.spacingBox);
  49925. // detect collision
  49926. inputGroupX = (inputGroup.alignAttr.translateX +
  49927. inputGroup.alignOptions.x -
  49928. exportingX +
  49929. // getBBox for detecing left margin
  49930. inputGroup.getBBox().x +
  49931. // 2px padding to not overlap input and label
  49932. 2);
  49933. inputGroupWidth = inputGroup.alignOptions.width;
  49934. buttonGroupX = buttonGroup.alignAttr.translateX +
  49935. buttonGroup.getBBox().x;
  49936. // 20 is minimal spacing between elements
  49937. buttonGroupWidth = buttonGroup.getBBox().width + 20;
  49938. if ((inputPosition.align ===
  49939. buttonPosition.align) || ((buttonGroupX + buttonGroupWidth > inputGroupX) &&
  49940. (inputGroupX + inputGroupWidth > buttonGroupX) &&
  49941. (buttonPositionY <
  49942. (inputPositionY +
  49943. inputGroup.getBBox().height)))) {
  49944. inputGroup.attr({
  49945. translateX: inputGroup.alignAttr.translateX +
  49946. (chart.axisOffset[1] >= -exportingX ? 0 : -exportingX),
  49947. translateY: inputGroup.alignAttr.translateY +
  49948. buttonGroup.getBBox().height + 10
  49949. });
  49950. }
  49951. // Set or reset the input values
  49952. rangeSelector.setInputValue('min', min);
  49953. rangeSelector.setInputValue('max', max);
  49954. // skip animation
  49955. rangeSelector.inputGroup.placed = animate;
  49956. }
  49957. // vertical align
  49958. rangeSelector.group.align({
  49959. verticalAlign: verticalAlign
  49960. }, true, chart.spacingBox);
  49961. // set position
  49962. groupHeight =
  49963. rangeSelector.group.getBBox().height + 20; // # 20 padding
  49964. alignTranslateY =
  49965. rangeSelector.group.alignAttr.translateY;
  49966. // calculate bottom position
  49967. if (verticalAlign === 'bottom') {
  49968. legendHeight = (legendOptions &&
  49969. legendOptions.verticalAlign === 'bottom' &&
  49970. legendOptions.enabled &&
  49971. !legendOptions.floating ?
  49972. legend.legendHeight + pick(legendOptions.margin, 10) :
  49973. 0);
  49974. groupHeight = groupHeight + legendHeight - 20;
  49975. translateY = (alignTranslateY -
  49976. groupHeight -
  49977. (floating ? 0 : options.y) -
  49978. (chart.titleOffset ? chart.titleOffset[2] : 0) -
  49979. 10 // 10 spacing
  49980. );
  49981. }
  49982. if (verticalAlign === 'top') {
  49983. if (floating) {
  49984. translateY = 0;
  49985. }
  49986. if (chart.titleOffset && chart.titleOffset[0]) {
  49987. translateY = chart.titleOffset[0];
  49988. }
  49989. translateY += ((chart.margin[0] - chart.spacing[0]) || 0);
  49990. }
  49991. else if (verticalAlign === 'middle') {
  49992. if (inputPositionY === buttonPositionY) {
  49993. if (inputPositionY < 0) {
  49994. translateY = alignTranslateY + minPosition;
  49995. }
  49996. else {
  49997. translateY = alignTranslateY;
  49998. }
  49999. }
  50000. else if (inputPositionY || buttonPositionY) {
  50001. if (inputPositionY < 0 ||
  50002. buttonPositionY < 0) {
  50003. translateY -= Math.min(inputPositionY, buttonPositionY);
  50004. }
  50005. else {
  50006. translateY =
  50007. alignTranslateY - groupHeight + minPosition;
  50008. }
  50009. }
  50010. }
  50011. rangeSelector.group.translate(options.x, options.y + Math.floor(translateY));
  50012. // translate HTML inputs
  50013. if (inputEnabled !== false) {
  50014. rangeSelector.minInput.style.marginTop =
  50015. rangeSelector.group.translateY + 'px';
  50016. rangeSelector.maxInput.style.marginTop =
  50017. rangeSelector.group.translateY + 'px';
  50018. }
  50019. rangeSelector.rendered = true;
  50020. };
  50021. /**
  50022. * Extracts height of range selector
  50023. *
  50024. * @private
  50025. * @function Highcharts.RangeSelector#getHeight
  50026. * @return {number}
  50027. * Returns rangeSelector height
  50028. */
  50029. RangeSelector.prototype.getHeight = function () {
  50030. var rangeSelector = this, options = rangeSelector.options, rangeSelectorGroup = rangeSelector.group, inputPosition = options.inputPosition, buttonPosition = options.buttonPosition, yPosition = options.y, buttonPositionY = buttonPosition.y, inputPositionY = inputPosition.y, rangeSelectorHeight = 0, minPosition;
  50031. if (options.height) {
  50032. return options.height;
  50033. }
  50034. rangeSelectorHeight = rangeSelectorGroup ?
  50035. // 13px to keep back compatibility
  50036. (rangeSelectorGroup.getBBox(true).height) + 13 +
  50037. yPosition :
  50038. 0;
  50039. minPosition = Math.min(inputPositionY, buttonPositionY);
  50040. if ((inputPositionY < 0 && buttonPositionY < 0) ||
  50041. (inputPositionY > 0 && buttonPositionY > 0)) {
  50042. rangeSelectorHeight += Math.abs(minPosition);
  50043. }
  50044. return rangeSelectorHeight;
  50045. };
  50046. /**
  50047. * Detect collision with title or subtitle
  50048. *
  50049. * @private
  50050. * @function Highcharts.RangeSelector#titleCollision
  50051. *
  50052. * @param {Highcharts.Chart} chart
  50053. *
  50054. * @return {boolean}
  50055. * Returns collision status
  50056. */
  50057. RangeSelector.prototype.titleCollision = function (chart) {
  50058. return !(chart.options.title.text ||
  50059. chart.options.subtitle.text);
  50060. };
  50061. /**
  50062. * Update the range selector with new options
  50063. *
  50064. * @private
  50065. * @function Highcharts.RangeSelector#update
  50066. * @param {Highcharts.RangeSelectorOptions} options
  50067. * @return {void}
  50068. */
  50069. RangeSelector.prototype.update = function (options) {
  50070. var chart = this.chart;
  50071. merge(true, chart.options.rangeSelector, options);
  50072. this.destroy();
  50073. this.init(chart);
  50074. chart.rangeSelector.render();
  50075. };
  50076. /**
  50077. * Destroys allocated elements.
  50078. *
  50079. * @private
  50080. * @function Highcharts.RangeSelector#destroy
  50081. */
  50082. RangeSelector.prototype.destroy = function () {
  50083. var rSelector = this, minInput = rSelector.minInput, maxInput = rSelector.maxInput;
  50084. rSelector.unMouseDown();
  50085. rSelector.unResize();
  50086. // Destroy elements in collections
  50087. destroyObjectProperties(rSelector.buttons);
  50088. // Clear input element events
  50089. if (minInput) {
  50090. minInput.onfocus = minInput.onblur = minInput.onchange = null;
  50091. }
  50092. if (maxInput) {
  50093. maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
  50094. }
  50095. // Destroy HTML and SVG elements
  50096. objectEach(rSelector, function (val, key) {
  50097. if (val && key !== 'chart') {
  50098. if (val instanceof SVGElement) {
  50099. // SVGElement
  50100. val.destroy();
  50101. }
  50102. else if (val instanceof window.HTMLElement) {
  50103. // HTML element
  50104. discardElement(val);
  50105. }
  50106. }
  50107. if (val !== RangeSelector.prototype[key]) {
  50108. rSelector[key] = null;
  50109. }
  50110. }, this);
  50111. };
  50112. return RangeSelector;
  50113. }());
  50114. /**
  50115. * The default buttons for pre-selecting time frames
  50116. */
  50117. RangeSelector.prototype.defaultButtons = [{
  50118. type: 'month',
  50119. count: 1,
  50120. text: '1m'
  50121. }, {
  50122. type: 'month',
  50123. count: 3,
  50124. text: '3m'
  50125. }, {
  50126. type: 'month',
  50127. count: 6,
  50128. text: '6m'
  50129. }, {
  50130. type: 'ytd',
  50131. text: 'YTD'
  50132. }, {
  50133. type: 'year',
  50134. count: 1,
  50135. text: '1y'
  50136. }, {
  50137. type: 'all',
  50138. text: 'All'
  50139. }];
  50140. /**
  50141. * Get the axis min value based on the range option and the current max. For
  50142. * stock charts this is extended via the {@link RangeSelector} so that if the
  50143. * selected range is a multiple of months or years, it is compensated for
  50144. * various month lengths.
  50145. *
  50146. * @private
  50147. * @function Highcharts.Axis#minFromRange
  50148. * @return {number|undefined}
  50149. * The new minimum value.
  50150. */
  50151. Axis.prototype.minFromRange = function () {
  50152. var rangeOptions = this.range, type = rangeOptions.type, min, max = this.max, dataMin, range, time = this.chart.time,
  50153. // Get the true range from a start date
  50154. getTrueRange = function (base, count) {
  50155. var timeName = type === 'year' ? 'FullYear' : 'Month';
  50156. var date = new time.Date(base);
  50157. var basePeriod = time.get(timeName, date);
  50158. time.set(timeName, date, basePeriod + count);
  50159. if (basePeriod === time.get(timeName, date)) {
  50160. time.set('Date', date, 0); // #6537
  50161. }
  50162. return date.getTime() - base;
  50163. };
  50164. if (isNumber(rangeOptions)) {
  50165. min = max - rangeOptions;
  50166. range = rangeOptions;
  50167. }
  50168. else {
  50169. min = max + getTrueRange(max, -rangeOptions.count);
  50170. // Let the fixedRange reflect initial settings (#5930)
  50171. if (this.chart) {
  50172. this.chart.fixedRange = max - min;
  50173. }
  50174. }
  50175. dataMin = pick(this.dataMin, Number.MIN_VALUE);
  50176. if (!isNumber(min)) {
  50177. min = dataMin;
  50178. }
  50179. if (min <= dataMin) {
  50180. min = dataMin;
  50181. if (typeof range === 'undefined') { // #4501
  50182. range = getTrueRange(min, rangeOptions.count);
  50183. }
  50184. this.newMax = Math.min(min + range, this.dataMax);
  50185. }
  50186. if (!isNumber(max)) {
  50187. min = void 0;
  50188. }
  50189. return min;
  50190. };
  50191. if (!H.RangeSelector) {
  50192. // Initialize rangeselector for stock charts
  50193. addEvent(Chart, 'afterGetContainer', function () {
  50194. if (this.options.rangeSelector.enabled) {
  50195. this.rangeSelector = new RangeSelector(this);
  50196. }
  50197. });
  50198. addEvent(Chart, 'beforeRender', function () {
  50199. var chart = this, axes = chart.axes, rangeSelector = chart.rangeSelector, verticalAlign;
  50200. if (rangeSelector) {
  50201. if (isNumber(rangeSelector.deferredYTDClick)) {
  50202. rangeSelector.clickButton(rangeSelector.deferredYTDClick);
  50203. delete rangeSelector.deferredYTDClick;
  50204. }
  50205. axes.forEach(function (axis) {
  50206. axis.updateNames();
  50207. axis.setScale();
  50208. });
  50209. chart.getAxisMargins();
  50210. rangeSelector.render();
  50211. verticalAlign = rangeSelector.options.verticalAlign;
  50212. if (!rangeSelector.options.floating) {
  50213. if (verticalAlign === 'bottom') {
  50214. this.extraBottomMargin = true;
  50215. }
  50216. else if (verticalAlign !== 'middle') {
  50217. this.extraTopMargin = true;
  50218. }
  50219. }
  50220. }
  50221. });
  50222. addEvent(Chart, 'update', function (e) {
  50223. var chart = this, options = e.options, optionsRangeSelector = options.rangeSelector, rangeSelector = chart.rangeSelector, verticalAlign, extraBottomMarginWas = this.extraBottomMargin, extraTopMarginWas = this.extraTopMargin;
  50224. if (optionsRangeSelector &&
  50225. optionsRangeSelector.enabled &&
  50226. !defined(rangeSelector)) {
  50227. this.options.rangeSelector.enabled = true;
  50228. this.rangeSelector = new RangeSelector(this);
  50229. }
  50230. this.extraBottomMargin = false;
  50231. this.extraTopMargin = false;
  50232. if (rangeSelector) {
  50233. rangeSelector.render();
  50234. verticalAlign = (optionsRangeSelector &&
  50235. optionsRangeSelector.verticalAlign) || (rangeSelector.options && rangeSelector.options.verticalAlign);
  50236. if (!rangeSelector.options.floating) {
  50237. if (verticalAlign === 'bottom') {
  50238. this.extraBottomMargin = true;
  50239. }
  50240. else if (verticalAlign !== 'middle') {
  50241. this.extraTopMargin = true;
  50242. }
  50243. }
  50244. if (this.extraBottomMargin !== extraBottomMarginWas ||
  50245. this.extraTopMargin !== extraTopMarginWas) {
  50246. this.isDirtyBox = true;
  50247. }
  50248. }
  50249. });
  50250. addEvent(Chart, 'render', function () {
  50251. var chart = this, rangeSelector = chart.rangeSelector, verticalAlign;
  50252. if (rangeSelector && !rangeSelector.options.floating) {
  50253. rangeSelector.render();
  50254. verticalAlign = rangeSelector.options.verticalAlign;
  50255. if (verticalAlign === 'bottom') {
  50256. this.extraBottomMargin = true;
  50257. }
  50258. else if (verticalAlign !== 'middle') {
  50259. this.extraTopMargin = true;
  50260. }
  50261. }
  50262. });
  50263. addEvent(Chart, 'getMargins', function () {
  50264. var rangeSelector = this.rangeSelector, rangeSelectorHeight;
  50265. if (rangeSelector) {
  50266. rangeSelectorHeight = rangeSelector.getHeight();
  50267. if (this.extraTopMargin) {
  50268. this.plotTop += rangeSelectorHeight;
  50269. }
  50270. if (this.extraBottomMargin) {
  50271. this.marginBottom += rangeSelectorHeight;
  50272. }
  50273. }
  50274. });
  50275. Chart.prototype.callbacks.push(function (chart) {
  50276. var extremes, rangeSelector = chart.rangeSelector, unbindRender, unbindSetExtremes, legend, alignTo, verticalAlign;
  50277. /**
  50278. * @private
  50279. */
  50280. function renderRangeSelector() {
  50281. extremes = chart.xAxis[0].getExtremes();
  50282. legend = chart.legend;
  50283. verticalAlign = rangeSelector === null || rangeSelector === void 0 ? void 0 : rangeSelector.options.verticalAlign;
  50284. if (isNumber(extremes.min)) {
  50285. rangeSelector.render(extremes.min, extremes.max);
  50286. }
  50287. // Re-align the legend so that it's below the rangeselector
  50288. if (rangeSelector && legend.display &&
  50289. verticalAlign === 'top' &&
  50290. verticalAlign === legend.options.verticalAlign) {
  50291. // Create a new alignment box for the legend.
  50292. alignTo = merge(chart.spacingBox);
  50293. if (legend.options.layout === 'vertical') {
  50294. alignTo.y = chart.plotTop;
  50295. }
  50296. else {
  50297. alignTo.y += rangeSelector.getHeight();
  50298. }
  50299. legend.group.placed = false; // Don't animate the alignment.
  50300. legend.align(alignTo);
  50301. }
  50302. }
  50303. if (rangeSelector) {
  50304. // redraw the scroller on setExtremes
  50305. unbindSetExtremes = addEvent(chart.xAxis[0], 'afterSetExtremes', function (e) {
  50306. rangeSelector.render(e.min, e.max);
  50307. });
  50308. // redraw the scroller chart resize
  50309. unbindRender = addEvent(chart, 'redraw', renderRangeSelector);
  50310. // do it now
  50311. renderRangeSelector();
  50312. }
  50313. // Remove resize/afterSetExtremes at chart destroy
  50314. addEvent(chart, 'destroy', function destroyEvents() {
  50315. if (rangeSelector) {
  50316. unbindRender();
  50317. unbindSetExtremes();
  50318. }
  50319. });
  50320. });
  50321. H.RangeSelector = RangeSelector;
  50322. }
  50323. return H.RangeSelector;
  50324. });
  50325. _registerModule(_modules, 'parts/StockChart.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Point.js'], _modules['parts/SVGRenderer.js'], _modules['parts/Utilities.js']], function (Axis, Chart, H, Point, SVGRenderer, U) {
  50326. /* *
  50327. *
  50328. * (c) 2010-2020 Torstein Honsi
  50329. *
  50330. * License: www.highcharts.com/license
  50331. *
  50332. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  50333. *
  50334. * */
  50335. var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, defined = U.defined, extend = U.extend, find = U.find, format = U.format, getOptions = U.getOptions, isNumber = U.isNumber, isString = U.isString, merge = U.merge, pick = U.pick, splat = U.splat;
  50336. // Has a dependency on Navigator due to the use of
  50337. // defaultOptions.navigator
  50338. // Has a dependency on Scrollbar due to the use of
  50339. // defaultOptions.scrollbar
  50340. // Has a dependency on RangeSelector due to the use of
  50341. // defaultOptions.rangeSelector
  50342. var Series = H.Series, seriesProto = Series.prototype, seriesInit = seriesProto.init, seriesProcessData = seriesProto.processData, pointTooltipFormatter = Point.prototype.tooltipFormatter;
  50343. /**
  50344. * Compare the values of the series against the first non-null, non-
  50345. * zero value in the visible range. The y axis will show percentage
  50346. * or absolute change depending on whether `compare` is set to `"percent"`
  50347. * or `"value"`. When this is applied to multiple series, it allows
  50348. * comparing the development of the series against each other. Adds
  50349. * a `change` field to every point object.
  50350. *
  50351. * @see [compareBase](#plotOptions.series.compareBase)
  50352. * @see [Axis.setCompare()](/class-reference/Highcharts.Axis#setCompare)
  50353. *
  50354. * @sample {highstock} stock/plotoptions/series-compare-percent/
  50355. * Percent
  50356. * @sample {highstock} stock/plotoptions/series-compare-value/
  50357. * Value
  50358. *
  50359. * @type {string}
  50360. * @since 1.0.1
  50361. * @product highstock
  50362. * @apioption plotOptions.series.compare
  50363. */
  50364. /**
  50365. * Defines if comparison should start from the first point within the visible
  50366. * range or should start from the first point **before** the range.
  50367. *
  50368. * In other words, this flag determines if first point within the visible range
  50369. * will have 0% (`compareStart=true`) or should have been already calculated
  50370. * according to the previous point (`compareStart=false`).
  50371. *
  50372. * @sample {highstock} stock/plotoptions/series-comparestart/
  50373. * Calculate compare within visible range
  50374. *
  50375. * @type {boolean}
  50376. * @default false
  50377. * @since 6.0.0
  50378. * @product highstock
  50379. * @apioption plotOptions.series.compareStart
  50380. */
  50381. /**
  50382. * When [compare](#plotOptions.series.compare) is `percent`, this option
  50383. * dictates whether to use 0 or 100 as the base of comparison.
  50384. *
  50385. * @sample {highstock} stock/plotoptions/series-comparebase/
  50386. * Compare base is 100
  50387. *
  50388. * @type {number}
  50389. * @default 0
  50390. * @since 5.0.6
  50391. * @product highstock
  50392. * @validvalue [0, 100]
  50393. * @apioption plotOptions.series.compareBase
  50394. */
  50395. /* eslint-disable no-invalid-this, valid-jsdoc */
  50396. /**
  50397. * Factory function for creating new stock charts. Creates a new
  50398. * {@link Highcharts.Chart|Chart} object with different default options than the
  50399. * basic Chart.
  50400. *
  50401. * @example
  50402. * var chart = Highcharts.stockChart('container', {
  50403. * series: [{
  50404. * data: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  50405. * pointInterval: 24 * 60 * 60 * 1000
  50406. * }]
  50407. * });
  50408. *
  50409. * @function Highcharts.stockChart
  50410. *
  50411. * @param {string|Highcharts.HTMLDOMElement} [renderTo]
  50412. * The DOM element to render to, or its id.
  50413. *
  50414. * @param {Highcharts.Options} options
  50415. * The chart options structure as described in the
  50416. * [options reference](https://api.highcharts.com/highstock).
  50417. *
  50418. * @param {Highcharts.ChartCallbackFunction} [callback]
  50419. * A function to execute when the chart object is finished loading and
  50420. * rendering. In most cases the chart is built in one thread, but in
  50421. * Internet Explorer version 8 or less the chart is sometimes
  50422. * initialized before the document is ready, and in these cases the
  50423. * chart object will not be finished synchronously. As a consequence,
  50424. * code that relies on the newly built Chart object should always run in
  50425. * the callback. Defining a
  50426. * [chart.events.load](https://api.highcharts.com/highstock/chart.events.load)
  50427. * handler is equivalent.
  50428. *
  50429. * @return {Highcharts.Chart}
  50430. * The chart object.
  50431. */
  50432. H.StockChart = H.stockChart = function (a, b, c) {
  50433. var hasRenderToArg = isString(a) || a.nodeName, options = arguments[hasRenderToArg ? 1 : 0], userOptions = options,
  50434. // to increase performance, don't merge the data
  50435. seriesOptions = options.series, defaultOptions = getOptions(), opposite,
  50436. // Always disable startOnTick:true on the main axis when the navigator
  50437. // is enabled (#1090)
  50438. navigatorEnabled = pick(options.navigator && options.navigator.enabled, defaultOptions.navigator.enabled, true);
  50439. // apply X axis options to both single and multi y axes
  50440. options.xAxis = splat(options.xAxis || {}).map(function (xAxisOptions, i) {
  50441. return merge({
  50442. minPadding: 0,
  50443. maxPadding: 0,
  50444. overscroll: 0,
  50445. ordinal: true,
  50446. title: {
  50447. text: null
  50448. },
  50449. labels: {
  50450. overflow: 'justify'
  50451. },
  50452. showLastLabel: true
  50453. }, defaultOptions.xAxis, // #3802
  50454. defaultOptions.xAxis && defaultOptions.xAxis[i], // #7690
  50455. xAxisOptions, // user options
  50456. {
  50457. type: 'datetime',
  50458. categories: null
  50459. }, (navigatorEnabled ? {
  50460. startOnTick: false,
  50461. endOnTick: false
  50462. } : null));
  50463. });
  50464. // apply Y axis options to both single and multi y axes
  50465. options.yAxis = splat(options.yAxis || {}).map(function (yAxisOptions, i) {
  50466. opposite = pick(yAxisOptions.opposite, true);
  50467. return merge({
  50468. labels: {
  50469. y: -2
  50470. },
  50471. opposite: opposite,
  50472. /**
  50473. * @default {highcharts} true
  50474. * @default {highstock} false
  50475. * @apioption yAxis.showLastLabel
  50476. *
  50477. * @private
  50478. */
  50479. showLastLabel: !!(
  50480. // #6104, show last label by default for category axes
  50481. yAxisOptions.categories ||
  50482. yAxisOptions.type === 'category'),
  50483. title: {
  50484. text: null
  50485. }
  50486. }, defaultOptions.yAxis, // #3802
  50487. defaultOptions.yAxis && defaultOptions.yAxis[i], // #7690
  50488. yAxisOptions // user options
  50489. );
  50490. });
  50491. options.series = null;
  50492. options = merge({
  50493. chart: {
  50494. panning: {
  50495. enabled: true,
  50496. type: 'x'
  50497. },
  50498. pinchType: 'x'
  50499. },
  50500. navigator: {
  50501. enabled: navigatorEnabled
  50502. },
  50503. scrollbar: {
  50504. // #4988 - check if setOptions was called
  50505. enabled: pick(defaultOptions.scrollbar.enabled, true)
  50506. },
  50507. rangeSelector: {
  50508. // #4988 - check if setOptions was called
  50509. enabled: pick(defaultOptions.rangeSelector.enabled, true)
  50510. },
  50511. title: {
  50512. text: null
  50513. },
  50514. tooltip: {
  50515. split: pick(defaultOptions.tooltip.split, true),
  50516. crosshairs: true
  50517. },
  50518. legend: {
  50519. enabled: false
  50520. }
  50521. }, options, // user's options
  50522. {
  50523. isStock: true // internal flag
  50524. });
  50525. options.series = userOptions.series = seriesOptions;
  50526. return hasRenderToArg ?
  50527. new Chart(a, options, c) :
  50528. new Chart(options, b);
  50529. };
  50530. // Handle som Stock-specific series defaults, override the plotOptions before
  50531. // series options are handled.
  50532. addEvent(Series, 'setOptions', function (e) {
  50533. var overrides;
  50534. if (this.chart.options.isStock) {
  50535. if (this.is('column') || this.is('columnrange')) {
  50536. overrides = {
  50537. borderWidth: 0,
  50538. shadow: false
  50539. };
  50540. }
  50541. else if (!this.is('scatter') && !this.is('sma')) {
  50542. overrides = {
  50543. marker: {
  50544. enabled: false,
  50545. radius: 2
  50546. }
  50547. };
  50548. }
  50549. if (overrides) {
  50550. e.plotOptions[this.type] = merge(e.plotOptions[this.type], overrides);
  50551. }
  50552. }
  50553. });
  50554. // Override the automatic label alignment so that the first Y axis' labels
  50555. // are drawn on top of the grid line, and subsequent axes are drawn outside
  50556. addEvent(Axis, 'autoLabelAlign', function (e) {
  50557. var chart = this.chart, options = this.options, panes = chart._labelPanes = chart._labelPanes || {}, key, labelOptions = this.options.labels;
  50558. if (this.chart.options.isStock && this.coll === 'yAxis') {
  50559. key = options.top + ',' + options.height;
  50560. // do it only for the first Y axis of each pane
  50561. if (!panes[key] && labelOptions.enabled) {
  50562. if (labelOptions.x === 15) { // default
  50563. labelOptions.x = 0;
  50564. }
  50565. if (typeof labelOptions.align === 'undefined') {
  50566. labelOptions.align = 'right';
  50567. }
  50568. panes[key] = this;
  50569. e.align = 'right';
  50570. e.preventDefault();
  50571. }
  50572. }
  50573. });
  50574. // Clear axis from label panes (#6071)
  50575. addEvent(Axis, 'destroy', function () {
  50576. var chart = this.chart, key = this.options && (this.options.top + ',' + this.options.height);
  50577. if (key && chart._labelPanes && chart._labelPanes[key] === this) {
  50578. delete chart._labelPanes[key];
  50579. }
  50580. });
  50581. // Override getPlotLinePath to allow for multipane charts
  50582. addEvent(Axis, 'getPlotLinePath', function (e) {
  50583. var axis = this, series = (this.isLinked && !this.series ?
  50584. this.linkedParent.series :
  50585. 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
  50586. axes2, uniqueAxes, translatedValue = e.translatedValue, value = e.value, force = e.force, transVal;
  50587. /**
  50588. * Return the other axis based on either the axis option or on related
  50589. * series.
  50590. * @private
  50591. */
  50592. function getAxis(coll) {
  50593. var otherColl = coll === 'xAxis' ? 'yAxis' : 'xAxis', opt = axis.options[otherColl];
  50594. // Other axis indexed by number
  50595. if (isNumber(opt)) {
  50596. return [chart[otherColl][opt]];
  50597. }
  50598. // Other axis indexed by id (like navigator)
  50599. if (isString(opt)) {
  50600. return [chart.get(opt)];
  50601. }
  50602. // Auto detect based on existing series
  50603. return series.map(function (s) {
  50604. return s[otherColl];
  50605. });
  50606. }
  50607. if ( // For stock chart, by default render paths across the panes
  50608. // except the case when `acrossPanes` is disabled by user (#6644)
  50609. (chart.options.isStock && e.acrossPanes !== false) &&
  50610. // Ignore in case of colorAxis or zAxis. #3360, #3524, #6720
  50611. axis.coll === 'xAxis' || axis.coll === 'yAxis') {
  50612. e.preventDefault();
  50613. // Get the related axes based on series
  50614. axes = getAxis(axis.coll);
  50615. // Get the related axes based options.*Axis setting #2810
  50616. axes2 = (axis.isXAxis ? chart.yAxis : chart.xAxis);
  50617. axes2.forEach(function (A) {
  50618. if (defined(A.options.id) ?
  50619. A.options.id.indexOf('navigator') === -1 :
  50620. true) {
  50621. var a = (A.isXAxis ? 'yAxis' : 'xAxis'), rax = (defined(A.options[a]) ?
  50622. chart[a][A.options[a]] :
  50623. chart[a][0]);
  50624. if (axis === rax) {
  50625. axes.push(A);
  50626. }
  50627. }
  50628. });
  50629. // Remove duplicates in the axes array. If there are no axes in the axes
  50630. // array, we are adding an axis without data, so we need to populate
  50631. // this with grid lines (#2796).
  50632. uniqueAxes = axes.length ?
  50633. [] :
  50634. [axis.isXAxis ? chart.yAxis[0] : chart.xAxis[0]]; // #3742
  50635. axes.forEach(function (axis2) {
  50636. if (uniqueAxes.indexOf(axis2) === -1 &&
  50637. // Do not draw on axis which overlap completely. #5424
  50638. !find(uniqueAxes, function (unique) {
  50639. return unique.pos === axis2.pos && unique.len === axis2.len;
  50640. })) {
  50641. uniqueAxes.push(axis2);
  50642. }
  50643. });
  50644. transVal = pick(translatedValue, axis.translate(value, null, null, e.old));
  50645. if (isNumber(transVal)) {
  50646. if (axis.horiz) {
  50647. uniqueAxes.forEach(function (axis2) {
  50648. var skip;
  50649. y1 = axis2.pos;
  50650. y2 = y1 + axis2.len;
  50651. x1 = x2 = Math.round(transVal + axis.transB);
  50652. // outside plot area
  50653. if (force !== 'pass' &&
  50654. (x1 < axisLeft || x1 > axisLeft + axis.width)) {
  50655. if (force) {
  50656. x1 = x2 = clamp(x1, axisLeft, axisLeft + axis.width);
  50657. }
  50658. else {
  50659. skip = true;
  50660. }
  50661. }
  50662. if (!skip) {
  50663. result.push(['M', x1, y1], ['L', x2, y2]);
  50664. }
  50665. });
  50666. }
  50667. else {
  50668. uniqueAxes.forEach(function (axis2) {
  50669. var skip;
  50670. x1 = axis2.pos;
  50671. x2 = x1 + axis2.len;
  50672. y1 = y2 = Math.round(axisTop + axis.height - transVal);
  50673. // outside plot area
  50674. if (force !== 'pass' &&
  50675. (y1 < axisTop || y1 > axisTop + axis.height)) {
  50676. if (force) {
  50677. y1 = y2 = clamp(y1, axisTop, axisTop + axis.height);
  50678. }
  50679. else {
  50680. skip = true;
  50681. }
  50682. }
  50683. if (!skip) {
  50684. result.push(['M', x1, y1], ['L', x2, y2]);
  50685. }
  50686. });
  50687. }
  50688. }
  50689. e.path = result.length > 0 ?
  50690. renderer.crispPolyLine(result, e.lineWidth || 1) :
  50691. // #3557 getPlotLinePath in regular Highcharts also returns null
  50692. null;
  50693. }
  50694. });
  50695. /**
  50696. * Function to crisp a line with multiple segments
  50697. *
  50698. * @private
  50699. * @function Highcharts.SVGRenderer#crispPolyLine
  50700. * @param {Highcharts.SVGPathArray} points
  50701. * @param {number} width
  50702. * @return {Highcharts.SVGPathArray}
  50703. */
  50704. SVGRenderer.prototype.crispPolyLine = function (points, width) {
  50705. // points format: [['M', 0, 0], ['L', 100, 0]]
  50706. // normalize to a crisp line
  50707. for (var i = 0; i < points.length; i = i + 2) {
  50708. var start = points[i], end = points[i + 1];
  50709. if (start[1] === end[1]) {
  50710. // Substract due to #1129. Now bottom and left axis gridlines behave
  50711. // the same.
  50712. start[1] = end[1] =
  50713. Math.round(start[1]) - (width % 2 / 2);
  50714. }
  50715. if (start[2] === end[2]) {
  50716. start[2] = end[2] =
  50717. Math.round(start[2]) + (width % 2 / 2);
  50718. }
  50719. }
  50720. return points;
  50721. };
  50722. // Wrapper to hide the label
  50723. addEvent(Axis, 'afterHideCrosshair', function () {
  50724. if (this.crossLabel) {
  50725. this.crossLabel = this.crossLabel.hide();
  50726. }
  50727. });
  50728. // Extend crosshairs to also draw the label
  50729. addEvent(Axis, 'afterDrawCrosshair', function (event) {
  50730. // Check if the label has to be drawn
  50731. if (!defined(this.crosshair.label) ||
  50732. !this.crosshair.label.enabled ||
  50733. !this.cross) {
  50734. return;
  50735. }
  50736. var chart = this.chart, log = this.logarithmic, options = this.options.crosshair.label, // the label's options
  50737. horiz = this.horiz, // axis orientation
  50738. opposite = this.opposite, // axis position
  50739. left = this.left, // left position
  50740. top = this.top, // top position
  50741. crossLabel = this.crossLabel, // the svgElement
  50742. posx, posy, crossBox, formatOption = options.format, formatFormat = '', limit, align, tickInside = this.options.tickPosition === 'inside', snap = this.crosshair.snap !== false, value, offset = 0,
  50743. // Use last available event (#5287)
  50744. e = event.e || (this.cross && this.cross.e), point = event.point, min = this.min, max = this.max;
  50745. if (log) {
  50746. min = log.lin2log(min);
  50747. max = log.lin2log(max);
  50748. }
  50749. align = (horiz ? 'center' : opposite ?
  50750. (this.labelAlign === 'right' ? 'right' : 'left') :
  50751. (this.labelAlign === 'left' ? 'left' : 'center'));
  50752. // If the label does not exist yet, create it.
  50753. if (!crossLabel) {
  50754. crossLabel = this.crossLabel = chart.renderer
  50755. .label(null, null, null, options.shape || 'callout')
  50756. .addClass('highcharts-crosshair-label' + (this.series[0] &&
  50757. ' highcharts-color-' + this.series[0].colorIndex))
  50758. .attr({
  50759. align: options.align || align,
  50760. padding: pick(options.padding, 8),
  50761. r: pick(options.borderRadius, 3),
  50762. zIndex: 2
  50763. })
  50764. .add(this.labelGroup);
  50765. // Presentational
  50766. if (!chart.styledMode) {
  50767. crossLabel
  50768. .attr({
  50769. fill: options.backgroundColor ||
  50770. (this.series[0] && this.series[0].color) ||
  50771. '#666666',
  50772. stroke: options.borderColor || '',
  50773. 'stroke-width': options.borderWidth || 0
  50774. })
  50775. .css(extend({
  50776. color: '#ffffff',
  50777. fontWeight: 'normal',
  50778. fontSize: '11px',
  50779. textAlign: 'center'
  50780. }, options.style));
  50781. }
  50782. }
  50783. if (horiz) {
  50784. posx = snap ? point.plotX + left : e.chartX;
  50785. posy = top + (opposite ? 0 : this.height);
  50786. }
  50787. else {
  50788. posx = opposite ? this.width + left : 0;
  50789. posy = snap ? point.plotY + top : e.chartY;
  50790. }
  50791. if (!formatOption && !options.formatter) {
  50792. if (this.dateTime) {
  50793. formatFormat = '%b %d, %Y';
  50794. }
  50795. formatOption =
  50796. '{value' + (formatFormat ? ':' + formatFormat : '') + '}';
  50797. }
  50798. // Show the label
  50799. value = snap ?
  50800. point[this.isXAxis ? 'x' : 'y'] :
  50801. this.toValue(horiz ? e.chartX : e.chartY);
  50802. crossLabel.attr({
  50803. text: formatOption ?
  50804. format(formatOption, { value: value }, chart) :
  50805. options.formatter.call(this, value),
  50806. x: posx,
  50807. y: posy,
  50808. // Crosshair should be rendered within Axis range (#7219)
  50809. visibility: value < min || value > max ?
  50810. 'hidden' :
  50811. 'visible'
  50812. });
  50813. crossBox = crossLabel.getBBox();
  50814. // now it is placed we can correct its position
  50815. if (isNumber(crossLabel.y)) {
  50816. if (horiz) {
  50817. if ((tickInside && !opposite) || (!tickInside && opposite)) {
  50818. posy = crossLabel.y - crossBox.height;
  50819. }
  50820. }
  50821. else {
  50822. posy = crossLabel.y - (crossBox.height / 2);
  50823. }
  50824. }
  50825. // check the edges
  50826. if (horiz) {
  50827. limit = {
  50828. left: left - crossBox.x,
  50829. right: left + this.width - crossBox.x
  50830. };
  50831. }
  50832. else {
  50833. limit = {
  50834. left: this.labelAlign === 'left' ? left : 0,
  50835. right: this.labelAlign === 'right' ?
  50836. left + this.width :
  50837. chart.chartWidth
  50838. };
  50839. }
  50840. // left edge
  50841. if (crossLabel.translateX < limit.left) {
  50842. offset = limit.left - crossLabel.translateX;
  50843. }
  50844. // right edge
  50845. if (crossLabel.translateX + crossBox.width >= limit.right) {
  50846. offset = -(crossLabel.translateX + crossBox.width - limit.right);
  50847. }
  50848. // show the crosslabel
  50849. crossLabel.attr({
  50850. x: posx + offset,
  50851. y: posy,
  50852. // First set x and y, then anchorX and anchorY, when box is actually
  50853. // calculated, #5702
  50854. anchorX: horiz ?
  50855. posx :
  50856. (this.opposite ? 0 : chart.chartWidth),
  50857. anchorY: horiz ?
  50858. (this.opposite ? chart.chartHeight : 0) :
  50859. posy + crossBox.height / 2
  50860. });
  50861. });
  50862. /* ************************************************************************** *
  50863. * Start value compare logic *
  50864. * ************************************************************************** */
  50865. /**
  50866. * Extend series.init by adding a method to modify the y value used for plotting
  50867. * on the y axis. This method is called both from the axis when finding dataMin
  50868. * and dataMax, and from the series.translate method.
  50869. *
  50870. * @ignore
  50871. * @function Highcharts.Series#init
  50872. */
  50873. seriesProto.init = function () {
  50874. // Call base method
  50875. seriesInit.apply(this, arguments);
  50876. // Set comparison mode
  50877. this.setCompare(this.options.compare);
  50878. };
  50879. /**
  50880. * Highstock only. Set the
  50881. * [compare](https://api.highcharts.com/highstock/plotOptions.series.compare)
  50882. * mode of the series after render time. In most cases it is more useful running
  50883. * {@link Axis#setCompare} on the X axis to update all its series.
  50884. *
  50885. * @function Highcharts.Series#setCompare
  50886. *
  50887. * @param {string} [compare]
  50888. * Can be one of `null` (default), `"percent"` or `"value"`.
  50889. */
  50890. seriesProto.setCompare = function (compare) {
  50891. // Set or unset the modifyValue method
  50892. this.modifyValue = (compare === 'value' || compare === 'percent') ?
  50893. function (value, point) {
  50894. var compareValue = this.compareValue;
  50895. if (typeof value !== 'undefined' &&
  50896. typeof compareValue !== 'undefined') { // #2601, #5814
  50897. // Get the modified value
  50898. if (compare === 'value') {
  50899. value -= compareValue;
  50900. // Compare percent
  50901. }
  50902. else {
  50903. value = 100 * (value / compareValue) -
  50904. (this.options.compareBase === 100 ? 0 : 100);
  50905. }
  50906. // record for tooltip etc.
  50907. if (point) {
  50908. point.change = value;
  50909. }
  50910. return value;
  50911. }
  50912. return 0;
  50913. } :
  50914. null;
  50915. // Survive to export, #5485
  50916. this.userOptions.compare = compare;
  50917. // Mark dirty
  50918. if (this.chart.hasRendered) {
  50919. this.isDirty = true;
  50920. }
  50921. };
  50922. /**
  50923. * Extend series.processData by finding the first y value in the plot area,
  50924. * used for comparing the following values
  50925. *
  50926. * @ignore
  50927. * @function Highcharts.Series#processData
  50928. */
  50929. seriesProto.processData = function (force) {
  50930. var series = this, i, keyIndex = -1, processedXData, processedYData, compareStart = series.options.compareStart === true ? 0 : 1, length, compareValue;
  50931. // call base method
  50932. seriesProcessData.apply(this, arguments);
  50933. if (series.xAxis && series.processedYData) { // not pies
  50934. // local variables
  50935. processedXData = series.processedXData;
  50936. processedYData = series.processedYData;
  50937. length = processedYData.length;
  50938. // For series with more than one value (range, OHLC etc), compare
  50939. // against close or the pointValKey (#4922, #3112, #9854)
  50940. if (series.pointArrayMap) {
  50941. keyIndex = series.pointArrayMap.indexOf(series.options.pointValKey || series.pointValKey || 'y');
  50942. }
  50943. // find the first value for comparison
  50944. for (i = 0; i < length - compareStart; i++) {
  50945. compareValue = processedYData[i] && keyIndex > -1 ?
  50946. processedYData[i][keyIndex] :
  50947. processedYData[i];
  50948. if (isNumber(compareValue) &&
  50949. processedXData[i + compareStart] >=
  50950. series.xAxis.min &&
  50951. compareValue !== 0) {
  50952. series.compareValue = compareValue;
  50953. break;
  50954. }
  50955. }
  50956. }
  50957. return;
  50958. };
  50959. // Modify series extremes
  50960. addEvent(Series, 'afterGetExtremes', function (e) {
  50961. var dataExtremes = e.dataExtremes;
  50962. if (this.modifyValue && dataExtremes) {
  50963. var extremes = [
  50964. this.modifyValue(dataExtremes.dataMin),
  50965. this.modifyValue(dataExtremes.dataMax)
  50966. ];
  50967. dataExtremes.dataMin = arrayMin(extremes);
  50968. dataExtremes.dataMax = arrayMax(extremes);
  50969. }
  50970. });
  50971. /**
  50972. * Highstock only. Set the compare mode on all series belonging to an Y axis
  50973. * after render time.
  50974. *
  50975. * @see [series.plotOptions.compare](https://api.highcharts.com/highstock/series.plotOptions.compare)
  50976. *
  50977. * @sample stock/members/axis-setcompare/
  50978. * Set compoare
  50979. *
  50980. * @function Highcharts.Axis#setCompare
  50981. *
  50982. * @param {string} [compare]
  50983. * The compare mode. Can be one of `null` (default), `"value"` or
  50984. * `"percent"`.
  50985. *
  50986. * @param {boolean} [redraw=true]
  50987. * Whether to redraw the chart or to wait for a later call to
  50988. * {@link Chart#redraw}.
  50989. */
  50990. Axis.prototype.setCompare = function (compare, redraw) {
  50991. if (!this.isXAxis) {
  50992. this.series.forEach(function (series) {
  50993. series.setCompare(compare);
  50994. });
  50995. if (pick(redraw, true)) {
  50996. this.chart.redraw();
  50997. }
  50998. }
  50999. };
  51000. /**
  51001. * Extend the tooltip formatter by adding support for the point.change variable
  51002. * as well as the changeDecimals option.
  51003. *
  51004. * @ignore
  51005. * @function Highcharts.Point#tooltipFormatter
  51006. *
  51007. * @param {string} pointFormat
  51008. */
  51009. Point.prototype.tooltipFormatter = function (pointFormat) {
  51010. var point = this;
  51011. var numberFormatter = point.series.chart.numberFormatter;
  51012. pointFormat = pointFormat.replace('{point.change}', (point.change > 0 ? '+' : '') + numberFormatter(point.change, pick(point.series.tooltipOptions.changeDecimals, 2)));
  51013. return pointTooltipFormatter.apply(this, [pointFormat]);
  51014. };
  51015. /* ************************************************************************** *
  51016. * End value compare logic *
  51017. * ************************************************************************** */
  51018. // Extend the Series prototype to create a separate series clip box. This is
  51019. // related to using multiple panes, and a future pane logic should incorporate
  51020. // this feature (#2754).
  51021. addEvent(Series, 'render', function () {
  51022. var chart = this.chart, clipHeight;
  51023. // Only do this on not 3d (#2939, #5904) nor polar (#6057) charts, and only
  51024. // if the series type handles clipping in the animate method (#2975).
  51025. if (!(chart.is3d && chart.is3d()) &&
  51026. !chart.polar &&
  51027. this.xAxis &&
  51028. !this.xAxis.isRadial // Gauge, #6192
  51029. ) {
  51030. clipHeight = this.yAxis.len;
  51031. // Include xAxis line width (#8031) but only if the Y axis ends on the
  51032. // edge of the X axis (#11005).
  51033. if (this.xAxis.axisLine) {
  51034. var dist = chart.plotTop + chart.plotHeight -
  51035. this.yAxis.pos - this.yAxis.len, lineHeightCorrection = Math.floor(this.xAxis.axisLine.strokeWidth() / 2);
  51036. if (dist >= 0) {
  51037. clipHeight -= Math.max(lineHeightCorrection - dist, 0);
  51038. }
  51039. }
  51040. // First render, initial clip box
  51041. if (!this.clipBox && this.animate) {
  51042. this.clipBox = merge(chart.clipBox);
  51043. this.clipBox.width = this.xAxis.len;
  51044. this.clipBox.height = clipHeight;
  51045. // On redrawing, resizing etc, update the clip rectangle
  51046. }
  51047. else if (chart[this.sharedClipKey]) {
  51048. // animate in case resize is done during initial animation
  51049. chart[this.sharedClipKey].animate({
  51050. width: this.xAxis.len,
  51051. height: clipHeight
  51052. });
  51053. // also change markers clip animation for consistency
  51054. // (marker clip rects should exist only on chart init)
  51055. if (chart[this.sharedClipKey + 'm']) {
  51056. chart[this.sharedClipKey + 'm'].animate({
  51057. width: this.xAxis.len
  51058. });
  51059. }
  51060. }
  51061. }
  51062. });
  51063. addEvent(Chart, 'update', function (e) {
  51064. var options = e.options;
  51065. // Use case: enabling scrollbar from a disabled state.
  51066. // Scrollbar needs to be initialized from a controller, Navigator in this
  51067. // case (#6615)
  51068. if ('scrollbar' in options && this.navigator) {
  51069. merge(true, this.options.scrollbar, options.scrollbar);
  51070. this.navigator.update({}, false);
  51071. delete options.scrollbar;
  51072. }
  51073. });
  51074. });
  51075. _registerModule(_modules, 'masters/modules/stock.src.js', [], function () {
  51076. });
  51077. _registerModule(_modules, 'masters/highstock.src.js', [_modules['masters/highcharts.src.js']], function (Highcharts) {
  51078. Highcharts.product = 'Highstock';
  51079. return Highcharts;
  51080. });
  51081. _modules['masters/highstock.src.js']._modules = _modules;
  51082. return _modules['masters/highstock.src.js'];
  51083. }));